xref: /linux/rust/kernel/pci/io.rs (revision fdc290ff4ab19c7e0dde36c4cd1e2771b61f6bf5)
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