1 // SPDX-License-Identifier: GPL-2.0 2 3 //! PCI memory-mapped I/O infrastructure. 4 5 use super::Device; 6 use crate::{ 7 bindings, 8 device, 9 devres::Devres, 10 io::{ 11 IoBackend, 12 IoBase, 13 IoCapable, 14 Mmio, 15 MmioBackend, 16 MmioRaw, 17 Region, // 18 }, 19 prelude::*, 20 ptr::KnownSize, // 21 }; 22 23 /// Represents the size of a PCI configuration space. 24 /// 25 /// PCI devices can have either a *normal* (legacy) configuration space of 256 bytes, 26 /// or an *extended* configuration space of 4096 bytes as defined in the PCI Express 27 /// specification. 28 #[repr(usize)] 29 #[derive(Eq, PartialEq)] 30 pub enum ConfigSpaceSize { 31 /// 256-byte legacy PCI configuration space. 32 Normal = 256, 33 34 /// 4096-byte PCIe extended configuration space. 35 Extended = 4096, 36 } 37 38 impl ConfigSpaceSize { 39 /// Get the raw value of this enum. 40 #[inline(always)] 41 pub const fn into_raw(self) -> usize { 42 // CAST: PCI configuration space size is at most 4096 bytes, so the value always fits 43 // within `usize` without truncation or sign change. 44 self as usize 45 } 46 } 47 48 /// Alias for normal (256-byte) PCI configuration space. 49 pub type Normal = Region<256>; 50 51 /// Alias for extended (4096-byte) PCIe configuration space. 52 pub type Extended = Region<4096>; 53 54 /// A view of PCI configuration space of a device. 55 /// 56 /// Provides typed read and write accessors for configuration registers 57 /// using the standard `pci_read_config_*` and `pci_write_config_*` helpers. 58 /// 59 /// The generic parameter `T` is the type of the view. The full configuration space is also a 60 /// special type of view; in such cases, `T` can be [`Normal`] for 256-byte legacy configuration 61 /// space or [`Extended`] for 4096-byte PCIe extended configuration space (default). 62 /// 63 /// # Invariants 64 /// 65 /// `ptr` is aligned and range `ptr..ptr + KnownSize::size(ptr)` is within 66 /// `0..pdev.cfg_size().into_raw()`. 67 pub struct ConfigSpace<'a, T: ?Sized = Extended> { 68 pub(crate) pdev: &'a Device<device::Bound>, 69 ptr: *mut T, 70 } 71 72 impl<T: ?Sized> Copy for ConfigSpace<'_, T> {} 73 impl<T: ?Sized> Clone for ConfigSpace<'_, T> { 74 #[inline] 75 fn clone(&self) -> Self { 76 *self 77 } 78 } 79 80 // SAFETY: `ConfigSpace<'_, T>` is conceptually `&T` but in I/O memory. 81 unsafe impl<T: ?Sized + Sync> Send for ConfigSpace<'_, T> {} 82 83 // SAFETY: `ConfigSpace<'_, T>` is conceptually `&T` but in I/O memory. 84 unsafe impl<T: ?Sized + Sync> Sync for ConfigSpace<'_, T> {} 85 86 /// I/O Backend for PCI configuration space. 87 pub struct ConfigSpaceBackend; 88 89 impl IoBackend for ConfigSpaceBackend { 90 type View<'a, T: ?Sized + KnownSize> = ConfigSpace<'a, T>; 91 92 #[inline] 93 fn as_ptr<'a, T: ?Sized + KnownSize>(view: ConfigSpace<'a, T>) -> *mut T { 94 view.ptr 95 } 96 97 #[inline] 98 unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>( 99 view: Self::View<'a, T>, 100 ptr: *mut U, 101 ) -> Self::View<'a, U> { 102 // INVARIANT: Per safety requirement. 103 ConfigSpace { 104 pdev: view.pdev, 105 ptr, 106 } 107 } 108 } 109 110 /// Implements [`IoCapable`] on [`ConfigSpace`] for `$ty` using `$read_fn` and `$write_fn`. 111 macro_rules! impl_config_space_io_capable { 112 ($ty:ty, $read_fn:ident, $write_fn:ident) => { 113 impl IoCapable<$ty> for ConfigSpaceBackend { 114 fn io_read(view: ConfigSpace<'_, $ty>) -> $ty { 115 // CAST: The offset is cast to `i32` because the C functions expect a 32-bit 116 // signed offset parameter. PCI configuration space size is at most 4096 bytes, 117 // so the value always fits within `i32` without truncation or sign change. 118 let addr = view.ptr.addr() as i32; 119 120 let mut val: $ty = 0; 121 122 // Return value from C function is ignored in infallible accessors. 123 // SAFETY: By the type invariant `pdev` is a valid address. 124 let _ = unsafe { bindings::$read_fn(view.pdev.as_raw(), addr, &mut val) }; 125 val 126 } 127 128 fn io_write(view: ConfigSpace<'_, $ty>, value: $ty) { 129 // CAST: The offset is cast to `i32` because the C functions expect a 32-bit 130 // signed offset parameter. PCI configuration space size is at most 4096 bytes, 131 // so the value always fits within `i32` without truncation or sign change. 132 let addr = view.ptr.addr() as i32; 133 134 // Return value from C function is ignored in infallible accessors. 135 // SAFETY: By the type invariant `pdev` is a valid address. 136 let _ = unsafe { bindings::$write_fn(view.pdev.as_raw(), addr, value) }; 137 } 138 } 139 }; 140 } 141 142 // PCI configuration space supports 8, 16, and 32-bit accesses. 143 impl_config_space_io_capable!(u8, pci_read_config_byte, pci_write_config_byte); 144 impl_config_space_io_capable!(u16, pci_read_config_word, pci_write_config_word); 145 impl_config_space_io_capable!(u32, pci_read_config_dword, pci_write_config_dword); 146 147 impl<'a, T: ?Sized + KnownSize> IoBase<'a> for ConfigSpace<'a, T> { 148 type Backend = ConfigSpaceBackend; 149 type Target = T; 150 151 #[inline] 152 fn as_view(self) -> ConfigSpace<'a, T> { 153 self 154 } 155 } 156 157 /// A PCI BAR to perform I/O-Operations on. 158 /// 159 /// I/O backend assumes that the device is little-endian and will automatically 160 /// convert from little-endian to CPU endianness. 161 /// 162 /// # Invariants 163 /// 164 /// `Bar` always holds an `IoRaw` instance that holds a valid pointer to the start of the I/O 165 /// memory mapped PCI BAR and its size. 166 pub struct Bar<'a, const SIZE: usize = 0> { 167 pdev: &'a Device<device::Bound>, 168 io: MmioRaw<crate::io::Region<SIZE>>, 169 num: i32, 170 } 171 172 impl<'a, const SIZE: usize> Bar<'a, SIZE> { 173 pub(super) fn new( 174 pdev: &'a Device<device::Bound>, 175 num: u32, 176 name: &'static CStr, 177 ) -> Result<Self> { 178 let len = pdev.resource_len(num)?; 179 if len == 0 { 180 return Err(ENOMEM); 181 } 182 183 // Convert to `i32`, since that's what all the C bindings use. 184 let num = i32::try_from(num)?; 185 186 // SAFETY: 187 // `pdev` is valid by the invariants of `Device`. 188 // `num` is checked for validity by a previous call to `Device::resource_len`. 189 // `name` is always valid. 190 let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) }; 191 if ret != 0 { 192 return Err(EBUSY); 193 } 194 195 // SAFETY: 196 // `pdev` is valid by the invariants of `Device`. 197 // `num` is checked for validity by a previous call to `Device::resource_len`. 198 // `name` is always valid. 199 let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize; 200 if ioptr == 0 { 201 // SAFETY: 202 // `pdev` is valid by the invariants of `Device`. 203 // `num` is checked for validity by a previous call to `Device::resource_len`. 204 unsafe { bindings::pci_release_region(pdev.as_raw(), num) }; 205 return Err(ENOMEM); 206 } 207 208 let io = match MmioRaw::new_region(ioptr, len as usize) { 209 Ok(io) => io, 210 Err(err) => { 211 // SAFETY: 212 // `pdev` is valid by the invariants of `Device`. 213 // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region. 214 // `num` is checked for validity by a previous call to `Device::resource_len`. 215 unsafe { Self::do_release(pdev, ioptr, num) }; 216 return Err(err); 217 } 218 }; 219 220 Ok(Bar { pdev, io, num }) 221 } 222 223 /// # Safety 224 /// 225 /// `ioptr` must be a valid pointer to the memory mapped PCI BAR number `num`. 226 unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { 227 // SAFETY: 228 // `pdev` is valid by the invariants of `Device`. 229 // `ioptr` is valid by the safety requirements. 230 // `num` is valid by the safety requirements. 231 unsafe { 232 bindings::pci_iounmap(pdev.as_raw(), ioptr as *mut c_void); 233 bindings::pci_release_region(pdev.as_raw(), num); 234 } 235 } 236 237 fn release(&self) { 238 // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`. 239 unsafe { Self::do_release(self.pdev, self.io.addr(), self.num) }; 240 } 241 242 /// Consume the `Bar` and register it as a device-managed resource. 243 /// 244 /// The returned `Devres<Bar<'static, SIZE>>` can outlive the original lifetime `'a`. Access 245 /// to the BAR is revoked when the device is unbound. 246 pub fn into_devres(self) -> Result<Devres<Bar<'static, SIZE>>> { 247 // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `Bar` does not 248 // actually outlive the device -- access is revoked and the resource is released when the 249 // device is unbound. 250 let bar: Bar<'static, SIZE> = unsafe { core::mem::transmute(self) }; 251 let pdev = bar.pdev; 252 Devres::new(pdev.as_ref(), bar) 253 } 254 } 255 256 impl Bar<'_> { 257 #[inline] 258 pub(super) fn index_is_valid(index: u32) -> bool { 259 // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries. 260 index < bindings::PCI_NUM_RESOURCES 261 } 262 } 263 264 impl<const SIZE: usize> Drop for Bar<'_, SIZE> { 265 fn drop(&mut self) { 266 self.release(); 267 } 268 } 269 270 impl<'a, const SIZE: usize> IoBase<'a> for &'a Bar<'_, SIZE> { 271 type Backend = MmioBackend; 272 type Target = crate::io::Region<SIZE>; 273 274 #[inline] 275 fn as_view(self) -> Mmio<'a, Self::Target> { 276 // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. 277 unsafe { Mmio::from_raw(self.io) } 278 } 279 } 280 281 impl Device<device::Bound> { 282 /// Maps an entire PCI BAR after performing a region-request on it. I/O operation bound checks 283 /// can be performed on compile time for offsets (plus the requested type size) < SIZE. 284 pub fn iomap_region_sized<'a, const SIZE: usize>( 285 &'a self, 286 bar: u32, 287 name: &'static CStr, 288 ) -> Result<Bar<'a, SIZE>> { 289 Bar::new(self, bar, name) 290 } 291 292 /// Maps an entire PCI BAR after performing a region-request on it. 293 pub fn iomap_region<'a>(&'a self, bar: u32, name: &'static CStr) -> Result<Bar<'a>> { 294 self.iomap_region_sized::<0>(bar, name) 295 } 296 297 /// Returns the size of configuration space. 298 pub fn cfg_size(&self) -> ConfigSpaceSize { 299 // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`. 300 let size = unsafe { (*self.as_raw()).cfg_size }; 301 match size { 302 256 => ConfigSpaceSize::Normal, 303 4096 => ConfigSpaceSize::Extended, 304 _ => { 305 // PANIC: The PCI subsystem only ever reports the configuration space size as either 306 // `ConfigSpaceSize::Normal` or `ConfigSpaceSize::Extended`. 307 unreachable!(); 308 } 309 } 310 } 311 312 /// Return a view of the normal (256-byte) config space. 313 pub fn config_space<'a>(&'a self) -> ConfigSpace<'a, Normal> { 314 // INVARIANT: null is aligned and the range is within config space. 315 ConfigSpace { 316 pdev: self, 317 ptr: Normal::ptr_from_raw_parts_mut(core::ptr::null_mut(), self.cfg_size().into_raw()), 318 } 319 } 320 321 /// Return a view of the extended (4096-byte) config space. 322 pub fn config_space_extended<'a>(&'a self) -> Result<ConfigSpace<'a, Extended>> { 323 if self.cfg_size() != ConfigSpaceSize::Extended { 324 return Err(EINVAL); 325 } 326 327 // INVARIANT: null is aligned and we just checked the `cfg_size`. 328 Ok(ConfigSpace { 329 pdev: self, 330 ptr: Extended::ptr_from_raw_parts_mut(core::ptr::null_mut(), 4096), 331 }) 332 } 333 } 334