|
2 | 2 |
|
3 | 3 | //! PCI Root Bridge protocol. |
4 | 4 |
|
5 | | -use core::ptr; |
6 | | - |
7 | 5 | use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit}; |
8 | 6 | use crate::StatusExt; |
| 7 | +use crate::proto::pci::buffer::PciBuffer; |
| 8 | +use crate::proto::pci::mapped_region::PciMappedRegion; |
| 9 | +use core::ffi::c_void; |
| 10 | +use core::mem::MaybeUninit; |
| 11 | +use core::num::NonZeroUsize; |
| 12 | +use core::ptr; |
| 13 | +use core::ptr::NonNull; |
| 14 | +use log::debug; |
9 | 15 | use uefi_macros::unsafe_protocol; |
10 | | -use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol}; |
| 16 | +use uefi_raw::Status; |
| 17 | +use uefi_raw::protocol::pci::root_bridge::{ |
| 18 | + PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, |
| 19 | + PciRootBridgeIoProtocolOperation, |
| 20 | +}; |
| 21 | +use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE}; |
11 | 22 |
|
12 | 23 | /// Protocol that provides access to the PCI Root Bridge I/O protocol. |
13 | 24 | /// |
@@ -43,11 +54,111 @@ impl PciRootBridgeIo { |
43 | 54 | unsafe { (self.0.flush)(&mut self.0).to_result() } |
44 | 55 | } |
45 | 56 |
|
| 57 | + /// Allocates pages suitable for communicating with PCI devices. |
| 58 | + /// |
| 59 | + /// # Errors |
| 60 | + /// - [`crate::Status::INVALID_PARAMETER`] MemoryType is invalid. |
| 61 | + /// - [`crate::Status::UNSUPPORTED`] Attributes is unsupported. The only legal attribute bits are: |
| 62 | + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE`] |
| 63 | + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_MEMORY_CACHED`] |
| 64 | + /// - [`PciRootBridgeIoProtocolAttribute::PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE`] |
| 65 | + /// - [`crate::Status::OUT_OF_RESOURCES`] The memory pages could not be allocated. |
| 66 | + pub fn allocate_buffer<T>( |
| 67 | + &self, |
| 68 | + memory_type: MemoryType, |
| 69 | + pages: Option<NonZeroUsize>, |
| 70 | + attributes: PciRootBridgeIoProtocolAttribute, |
| 71 | + ) -> crate::Result<PciBuffer<MaybeUninit<T>>> { |
| 72 | + let mut address = 0usize; |
| 73 | + let original_alignment = align_of::<T>(); |
| 74 | + assert_ne!(original_alignment, 0); |
| 75 | + assert!(PAGE_SIZE >= original_alignment); |
| 76 | + assert_eq!(PAGE_SIZE % original_alignment, 0); |
| 77 | + |
| 78 | + let alignment = PAGE_SIZE; |
| 79 | + |
| 80 | + let pages = if let Some(pages) = pages { |
| 81 | + pages |
| 82 | + } else { |
| 83 | + let size = size_of::<T>(); |
| 84 | + assert_ne!(size, 0); |
| 85 | + |
| 86 | + NonZeroUsize::new(size.div_ceil(alignment)).unwrap() |
| 87 | + }; |
| 88 | + |
| 89 | + let status = unsafe { |
| 90 | + (self.0.allocate_buffer)( |
| 91 | + &self.0, |
| 92 | + AllocateType(0), |
| 93 | + memory_type, |
| 94 | + pages.get(), |
| 95 | + ptr::from_mut(&mut address).cast(), |
| 96 | + attributes.bits(), |
| 97 | + ) |
| 98 | + }; |
| 99 | + |
| 100 | + match status { |
| 101 | + Status::SUCCESS => { |
| 102 | + let base = NonNull::new(address as *mut MaybeUninit<T>).unwrap(); |
| 103 | + debug!("Allocated {} pages at 0x{:X}", pages.get(), address); |
| 104 | + Ok(PciBuffer { |
| 105 | + base, |
| 106 | + pages, |
| 107 | + proto: &self.0, |
| 108 | + }) |
| 109 | + } |
| 110 | + error |
| 111 | + @ (Status::INVALID_PARAMETER | Status::UNSUPPORTED | Status::OUT_OF_RESOURCES) => { |
| 112 | + Err(error.into()) |
| 113 | + } |
| 114 | + _ => unreachable!(), |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + /// Map given variable's address into PCI Controller-specific address |
| 119 | + /// required to access it from a DMA bus master. |
| 120 | + /// # Arguments |
| 121 | + /// - `operation` - Indicates if bus master is going to read, write or do both to given variable. |
| 122 | + /// - `to_map` - Variable to map. |
| 123 | + /// |
| 124 | + /// # Returns |
| 125 | + /// - PciMappedRegion capturing lifetime of passed variable |
| 126 | + pub fn map<'p, 'r, T>( |
| 127 | + &'p self, |
| 128 | + operation: PciRootBridgeIoProtocolOperation, |
| 129 | + to_map: &'r T, |
| 130 | + ) -> PciMappedRegion<'p, 'r> |
| 131 | + where |
| 132 | + 'p: 'r, |
| 133 | + { |
| 134 | + let host_address = ptr::from_ref(to_map); |
| 135 | + let mut bytes = size_of_val(to_map); |
| 136 | + let mut mapped_address = 0u64; |
| 137 | + let mut mapping: *mut c_void = ptr::null_mut(); |
| 138 | + |
| 139 | + let status = unsafe { |
| 140 | + (self.0.map)( |
| 141 | + &self.0, |
| 142 | + operation, |
| 143 | + host_address.cast(), |
| 144 | + ptr::from_mut(&mut bytes), |
| 145 | + ptr::from_mut(&mut mapped_address).cast(), |
| 146 | + ptr::from_mut(&mut mapping), |
| 147 | + ) |
| 148 | + }; |
| 149 | + |
| 150 | + match status { |
| 151 | + Status::SUCCESS => { |
| 152 | + PciMappedRegion::new(mapped_address, bytes, mapping, to_map, &self.0) |
| 153 | + } |
| 154 | + _ => unreachable!(), |
| 155 | + } |
| 156 | + } |
| 157 | + |
46 | 158 | // TODO: poll I/O |
47 | 159 | // TODO: mem I/O access |
48 | 160 | // TODO: io I/O access |
49 | | - // TODO: map & unmap & copy memory |
50 | | - // TODO: buffer management |
| 161 | + // TODO: copy memory |
51 | 162 | // TODO: get/set attributes |
52 | 163 | // TODO: configuration / resource settings |
53 | 164 | } |
|
0 commit comments