|
3 | 3 | //! PCI Root Bridge protocol. |
4 | 4 |
|
5 | 5 | use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit}; |
6 | | -#[cfg(doc)] |
7 | | -use crate::Status; |
8 | | -use crate::StatusExt; |
9 | 6 | #[cfg(feature = "alloc")] |
10 | 7 | use crate::proto::pci::configuration::QwordAddressSpaceDescriptor; |
| 8 | +use crate::proto::pci::root_bridge::buffer::PciBuffer; |
| 9 | +use crate::proto::pci::root_bridge::region::PciMappedRegion; |
| 10 | +use crate::{Status, StatusExt}; |
11 | 11 | #[cfg(feature = "alloc")] |
12 | 12 | use alloc::vec::Vec; |
13 | 13 | #[cfg(feature = "alloc")] |
14 | 14 | use core::ffi::c_void; |
| 15 | +use core::mem::MaybeUninit; |
| 16 | +use core::num::NonZeroUsize; |
15 | 17 | use core::ptr; |
| 18 | +use core::ptr::{NonNull, null_mut}; |
| 19 | +use log::debug; |
16 | 20 | use uefi_macros::unsafe_protocol; |
17 | | -use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol}; |
| 21 | +use uefi_raw::protocol::pci::root_bridge::{ |
| 22 | + PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, |
| 23 | + PciRootBridgeIoProtocolOperation, |
| 24 | +}; |
| 25 | +use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; |
18 | 26 |
|
19 | | -pub mod page; |
| 27 | +pub mod buffer; |
20 | 28 | pub mod region; |
21 | 29 |
|
22 | 30 | /// Protocol that provides access to the PCI Root Bridge I/O protocol. |
@@ -53,11 +61,143 @@ impl PciRootBridgeIo { |
53 | 61 | unsafe { (self.0.flush)(&mut self.0).to_result() } |
54 | 62 | } |
55 | 63 |
|
| 64 | + /// Allocates pages suitable for communicating with PCI devices. |
| 65 | + /// |
| 66 | + /// # Errors |
| 67 | + /// - [`Status::INVALID_PARAMETER`] MemoryType is invalid. |
| 68 | + /// - [`Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are: |
| 69 | + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`] |
| 70 | + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`] |
| 71 | + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`] |
| 72 | + /// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated. |
| 73 | + pub fn allocate_buffer<T>( |
| 74 | + &self, |
| 75 | + memory_type: MemoryType, |
| 76 | + pages: Option<NonZeroUsize>, |
| 77 | + attributes: PciRootBridgeIoProtocolAttribute, |
| 78 | + ) -> crate::Result<PciBuffer<'_, MaybeUninit<T>>> { |
| 79 | + let original_alignment = align_of::<T>(); |
| 80 | + // TODO switch to const block once it lands on stable. These checks should be done in compile time. |
| 81 | + assert_ne!(original_alignment, 0); |
| 82 | + assert!(PAGE_SIZE >= original_alignment); |
| 83 | + assert_eq!(PAGE_SIZE % original_alignment, 0); |
| 84 | + |
| 85 | + let alignment = PAGE_SIZE; |
| 86 | + |
| 87 | + let pages = if let Some(pages) = pages { |
| 88 | + pages |
| 89 | + } else { |
| 90 | + let size = size_of::<T>(); |
| 91 | + assert_ne!(size, 0); |
| 92 | + |
| 93 | + NonZeroUsize::new(size.div_ceil(alignment)).unwrap() |
| 94 | + }; |
| 95 | + let size = size_of::<T>(); |
| 96 | + // TODO switch to const block once it lands on stable. |
| 97 | + assert!(pages.get() * PAGE_SIZE >= size); |
| 98 | + |
| 99 | + let mut address: *mut T = null_mut(); |
| 100 | + let status = unsafe { |
| 101 | + (self.0.allocate_buffer)( |
| 102 | + &self.0, |
| 103 | + AllocateType(0), |
| 104 | + memory_type, |
| 105 | + pages.get(), |
| 106 | + ptr::from_mut(&mut address).cast(), |
| 107 | + attributes.bits(), |
| 108 | + ) |
| 109 | + }; |
| 110 | + |
| 111 | + match status { |
| 112 | + Status::SUCCESS => { |
| 113 | + let base = NonNull::new(address.cast()).unwrap(); |
| 114 | + debug!("Allocated {} pages at 0x{:X}", pages.get(), address.addr()); |
| 115 | + Ok(PciBuffer { |
| 116 | + base, |
| 117 | + pages, |
| 118 | + proto: &self.0, |
| 119 | + }) |
| 120 | + } |
| 121 | + error |
| 122 | + @ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => { |
| 123 | + Err(error.into()) |
| 124 | + } |
| 125 | + _ => unreachable!(), |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + /// Allocates pages suitable for communicating with PCI devices and initialize it right away. |
| 130 | + /// |
| 131 | + /// # Errors |
| 132 | + /// Same as [`Self::allocate_buffer`] |
| 133 | + /// - [`Status::INVALID_PARAMETER`] MemoryType is invalid. |
| 134 | + /// - [`Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are: |
| 135 | + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`] |
| 136 | + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`] |
| 137 | + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`] |
| 138 | + /// - [`Status::OUT_OF_RESOURCES`] The memory pages could not be allocated. |
| 139 | + pub fn allocate_buffer_init<T>( |
| 140 | + &self, |
| 141 | + memory_type: MemoryType, |
| 142 | + value: T, |
| 143 | + attributes: PciRootBridgeIoProtocolAttribute, |
| 144 | + ) -> crate::Result<PciBuffer<'_, T>> { |
| 145 | + let buffer = self.allocate_buffer(memory_type, None, attributes)?; |
| 146 | + unsafe { |
| 147 | + buffer.base_ptr().write(MaybeUninit::new(value)); |
| 148 | + Ok(buffer.assume_init()) |
| 149 | + } |
| 150 | + } |
| 151 | + |
| 152 | + /// Map the given buffer into a PCI Controller-specific address |
| 153 | + /// so that devices can read system memory through it. |
| 154 | + /// |
| 155 | + /// # Arguments |
| 156 | + /// - `operation` - Indicates if bus master is going to read, write, or do both to the buffer. |
| 157 | + /// - `to_map` - Buffer to map. |
| 158 | + /// |
| 159 | + /// # Returns |
| 160 | + /// An mapped region and unmapped bytes. It can map up to one DMA operation. Meaning large values |
| 161 | + /// will require multiple calls to this function. |
| 162 | + pub fn map<'p: 'r, 'r, T>( |
| 163 | + &'p self, |
| 164 | + operation: PciRootBridgeIoProtocolOperation, |
| 165 | + to_map: &'r PciBuffer<'p, T>, |
| 166 | + ) -> crate::Result<(PciMappedRegion<'p, 'r, T>, usize)> { |
| 167 | + let host_address = to_map.base_ptr(); |
| 168 | + let mut bytes = size_of::<T>(); |
| 169 | + let requested_bytes = bytes; |
| 170 | + let mut device_address = 0usize; |
| 171 | + let mut mapping: *mut c_void = null_mut(); |
| 172 | + |
| 173 | + let status = unsafe { |
| 174 | + (self.0.map)( |
| 175 | + &self.0, |
| 176 | + operation, |
| 177 | + host_address.cast(), |
| 178 | + ptr::from_mut(&mut bytes), |
| 179 | + ptr::from_mut(&mut device_address).cast(), |
| 180 | + ptr::from_mut(&mut mapping), |
| 181 | + ) |
| 182 | + }; |
| 183 | + |
| 184 | + status.to_result_with_val(|| { |
| 185 | + let left_over = requested_bytes - bytes; |
| 186 | + let region = PciMappedRegion { |
| 187 | + host: to_map, |
| 188 | + length: 0, |
| 189 | + device_address, |
| 190 | + key: mapping, |
| 191 | + proto: &self.0, |
| 192 | + }; |
| 193 | + (region, left_over) |
| 194 | + }) |
| 195 | + } |
| 196 | + |
56 | 197 | // TODO: poll I/O |
57 | 198 | // TODO: mem I/O access |
58 | 199 | // TODO: io I/O access |
59 | | - // TODO: map & unmap & copy memory |
60 | | - // TODO: buffer management |
| 200 | + // TODO: copy memory |
61 | 201 | // TODO: get/set attributes |
62 | 202 |
|
63 | 203 | /// Retrieves the current resource settings of this PCI root bridge in the form of a set of ACPI resource descriptors. |
|
0 commit comments