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 Io, 12 IoCapable, 13 IoKnownSize, 14 Mmio, 15 MmioRaw, // 16 }, 17 prelude::*, 18 sync::aref::ARef, // 19 }; 20 use core::{ 21 marker::PhantomData, 22 ops::Deref, // 23 }; 24 25 /// Represents the size of a PCI configuration space. 26 /// 27 /// PCI devices can have either a *normal* (legacy) configuration space of 256 bytes, 28 /// or an *extended* configuration space of 4096 bytes as defined in the PCI Express 29 /// specification. 30 #[repr(usize)] 31 #[derive(Eq, PartialEq)] 32 pub enum ConfigSpaceSize { 33 /// 256-byte legacy PCI configuration space. 34 Normal = 256, 35 36 /// 4096-byte PCIe extended configuration space. 37 Extended = 4096, 38 } 39 40 impl ConfigSpaceSize { 41 /// Get the raw value of this enum. 42 #[inline(always)] 43 pub const fn into_raw(self) -> usize { 44 // CAST: PCI configuration space size is at most 4096 bytes, so the value always fits 45 // within `usize` without truncation or sign change. 46 self as usize 47 } 48 } 49 50 /// Marker type for normal (256-byte) PCI configuration space. 51 pub struct Normal; 52 53 /// Marker type for extended (4096-byte) PCIe configuration space. 54 pub struct Extended; 55 56 /// Trait for PCI configuration space size markers. 57 /// 58 /// This trait is implemented by [`Normal`] and [`Extended`] to provide 59 /// compile-time knowledge of the configuration space size. 60 pub trait ConfigSpaceKind { 61 /// The size of this configuration space in bytes. 62 const SIZE: usize; 63 } 64 65 impl ConfigSpaceKind for Normal { 66 const SIZE: usize = 256; 67 } 68 69 impl ConfigSpaceKind for Extended { 70 const SIZE: usize = 4096; 71 } 72 73 /// The PCI configuration space of a device. 74 /// 75 /// Provides typed read and write accessors for configuration registers 76 /// using the standard `pci_read_config_*` and `pci_write_config_*` helpers. 77 /// 78 /// The generic parameter `S` indicates the maximum size of the configuration space. 79 /// Use [`Normal`] for 256-byte legacy configuration space or [`Extended`] for 80 /// 4096-byte PCIe extended configuration space (default). 81 pub struct ConfigSpace<'a, S: ConfigSpaceKind = Extended> { 82 pub(crate) pdev: &'a Device<device::Bound>, 83 _marker: PhantomData<S>, 84 } 85 86 /// Implements [`IoCapable`] on [`ConfigSpace`] for `$ty` using `$read_fn` and `$write_fn`. 87 macro_rules! impl_config_space_io_capable { 88 ($ty:ty, $read_fn:ident, $write_fn:ident) => { 89 impl<'a, S: ConfigSpaceKind> IoCapable<$ty> for ConfigSpace<'a, S> { 90 unsafe fn io_read(&self, address: usize) -> $ty { 91 let mut val: $ty = 0; 92 93 // Return value from C function is ignored in infallible accessors. 94 let _ret = 95 // SAFETY: By the type invariant `self.pdev` is a valid address. 96 // CAST: The offset is cast to `i32` because the C functions expect a 32-bit 97 // signed offset parameter. PCI configuration space size is at most 4096 bytes, 98 // so the value always fits within `i32` without truncation or sign change. 99 unsafe { bindings::$read_fn(self.pdev.as_raw(), address as i32, &mut val) }; 100 101 val 102 } 103 104 unsafe fn io_write(&self, value: $ty, address: usize) { 105 // Return value from C function is ignored in infallible accessors. 106 let _ret = 107 // SAFETY: By the type invariant `self.pdev` is a valid address. 108 // CAST: The offset is cast to `i32` because the C functions expect a 32-bit 109 // signed offset parameter. PCI configuration space size is at most 4096 bytes, 110 // so the value always fits within `i32` without truncation or sign change. 111 unsafe { bindings::$write_fn(self.pdev.as_raw(), address as i32, value) }; 112 } 113 } 114 }; 115 } 116 117 // PCI configuration space supports 8, 16, and 32-bit accesses. 118 impl_config_space_io_capable!(u8, pci_read_config_byte, pci_write_config_byte); 119 impl_config_space_io_capable!(u16, pci_read_config_word, pci_write_config_word); 120 impl_config_space_io_capable!(u32, pci_read_config_dword, pci_write_config_dword); 121 122 impl<'a, S: ConfigSpaceKind> Io for ConfigSpace<'a, S> { 123 /// Returns the base address of the I/O region. It is always 0 for configuration space. 124 #[inline] 125 fn addr(&self) -> usize { 126 0 127 } 128 129 /// Returns the maximum size of the configuration space. 130 #[inline] 131 fn maxsize(&self) -> usize { 132 self.pdev.cfg_size().into_raw() 133 } 134 } 135 136 impl<'a, S: ConfigSpaceKind> IoKnownSize for ConfigSpace<'a, S> { 137 const MIN_SIZE: usize = S::SIZE; 138 } 139 140 /// A PCI BAR to perform I/O-Operations on. 141 /// 142 /// I/O backend assumes that the device is little-endian and will automatically 143 /// convert from little-endian to CPU endianness. 144 /// 145 /// # Invariants 146 /// 147 /// `Bar` always holds an `IoRaw` instance that holds a valid pointer to the start of the I/O 148 /// memory mapped PCI BAR and its size. 149 pub struct Bar<const SIZE: usize = 0> { 150 pdev: ARef<Device>, 151 io: MmioRaw<SIZE>, 152 num: i32, 153 } 154 155 impl<const SIZE: usize> Bar<SIZE> { 156 pub(super) fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> { 157 let len = pdev.resource_len(num)?; 158 if len == 0 { 159 return Err(ENOMEM); 160 } 161 162 // Convert to `i32`, since that's what all the C bindings use. 163 let num = i32::try_from(num)?; 164 165 // SAFETY: 166 // `pdev` is valid by the invariants of `Device`. 167 // `num` is checked for validity by a previous call to `Device::resource_len`. 168 // `name` is always valid. 169 let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) }; 170 if ret != 0 { 171 return Err(EBUSY); 172 } 173 174 // SAFETY: 175 // `pdev` is valid by the invariants of `Device`. 176 // `num` is checked for validity by a previous call to `Device::resource_len`. 177 // `name` is always valid. 178 let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize; 179 if ioptr == 0 { 180 // SAFETY: 181 // `pdev` is valid by the invariants of `Device`. 182 // `num` is checked for validity by a previous call to `Device::resource_len`. 183 unsafe { bindings::pci_release_region(pdev.as_raw(), num) }; 184 return Err(ENOMEM); 185 } 186 187 let io = match MmioRaw::new(ioptr, len as usize) { 188 Ok(io) => io, 189 Err(err) => { 190 // SAFETY: 191 // `pdev` is valid by the invariants of `Device`. 192 // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region. 193 // `num` is checked for validity by a previous call to `Device::resource_len`. 194 unsafe { Self::do_release(pdev, ioptr, num) }; 195 return Err(err); 196 } 197 }; 198 199 Ok(Bar { 200 pdev: pdev.into(), 201 io, 202 num, 203 }) 204 } 205 206 /// # Safety 207 /// 208 /// `ioptr` must be a valid pointer to the memory mapped PCI BAR number `num`. 209 unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { 210 // SAFETY: 211 // `pdev` is valid by the invariants of `Device`. 212 // `ioptr` is valid by the safety requirements. 213 // `num` is valid by the safety requirements. 214 unsafe { 215 bindings::pci_iounmap(pdev.as_raw(), ioptr as *mut c_void); 216 bindings::pci_release_region(pdev.as_raw(), num); 217 } 218 } 219 220 fn release(&self) { 221 // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`. 222 unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) }; 223 } 224 } 225 226 impl Bar { 227 #[inline] 228 pub(super) fn index_is_valid(index: u32) -> bool { 229 // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries. 230 index < bindings::PCI_NUM_RESOURCES 231 } 232 } 233 234 impl<const SIZE: usize> Drop for Bar<SIZE> { 235 fn drop(&mut self) { 236 self.release(); 237 } 238 } 239 240 impl<const SIZE: usize> Deref for Bar<SIZE> { 241 type Target = Mmio<SIZE>; 242 243 fn deref(&self) -> &Self::Target { 244 // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. 245 unsafe { Mmio::from_raw(&self.io) } 246 } 247 } 248 249 impl Device<device::Bound> { 250 /// Maps an entire PCI BAR after performing a region-request on it. I/O operation bound checks 251 /// can be performed on compile time for offsets (plus the requested type size) < SIZE. 252 pub fn iomap_region_sized<'a, const SIZE: usize>( 253 &'a self, 254 bar: u32, 255 name: &'a CStr, 256 ) -> impl PinInit<Devres<Bar<SIZE>>, Error> + 'a { 257 Devres::new(self.as_ref(), Bar::<SIZE>::new(self, bar, name)) 258 } 259 260 /// Maps an entire PCI BAR after performing a region-request on it. 261 pub fn iomap_region<'a>( 262 &'a self, 263 bar: u32, 264 name: &'a CStr, 265 ) -> impl PinInit<Devres<Bar>, Error> + 'a { 266 self.iomap_region_sized::<0>(bar, name) 267 } 268 269 /// Returns the size of configuration space. 270 pub fn cfg_size(&self) -> ConfigSpaceSize { 271 // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`. 272 let size = unsafe { (*self.as_raw()).cfg_size }; 273 match size { 274 256 => ConfigSpaceSize::Normal, 275 4096 => ConfigSpaceSize::Extended, 276 _ => { 277 // PANIC: The PCI subsystem only ever reports the configuration space size as either 278 // `ConfigSpaceSize::Normal` or `ConfigSpaceSize::Extended`. 279 unreachable!(); 280 } 281 } 282 } 283 284 /// Return an initialized normal (256-byte) config space object. 285 pub fn config_space<'a>(&'a self) -> ConfigSpace<'a, Normal> { 286 ConfigSpace { 287 pdev: self, 288 _marker: PhantomData, 289 } 290 } 291 292 /// Return an initialized extended (4096-byte) config space object. 293 pub fn config_space_extended<'a>(&'a self) -> Result<ConfigSpace<'a, Extended>> { 294 if self.cfg_size() != ConfigSpaceSize::Extended { 295 return Err(EINVAL); 296 } 297 298 Ok(ConfigSpace { 299 pdev: self, 300 _marker: PhantomData, 301 }) 302 } 303 } 304