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 IoRaw, // 13 }, 14 prelude::*, 15 sync::aref::ARef, // 16 }; 17 use core::ops::Deref; 18 19 /// A PCI BAR to perform I/O-Operations on. 20 /// 21 /// # Invariants 22 /// 23 /// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O 24 /// memory mapped PCI BAR and its size. 25 pub struct Bar<const SIZE: usize = 0> { 26 pdev: ARef<Device>, 27 io: IoRaw<SIZE>, 28 num: i32, 29 } 30 31 impl<const SIZE: usize> Bar<SIZE> { 32 pub(super) fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> { 33 let len = pdev.resource_len(num)?; 34 if len == 0 { 35 return Err(ENOMEM); 36 } 37 38 // Convert to `i32`, since that's what all the C bindings use. 39 let num = i32::try_from(num)?; 40 41 // SAFETY: 42 // `pdev` is valid by the invariants of `Device`. 43 // `num` is checked for validity by a previous call to `Device::resource_len`. 44 // `name` is always valid. 45 let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) }; 46 if ret != 0 { 47 return Err(EBUSY); 48 } 49 50 // SAFETY: 51 // `pdev` is valid by the invariants of `Device`. 52 // `num` is checked for validity by a previous call to `Device::resource_len`. 53 // `name` is always valid. 54 let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize; 55 if ioptr == 0 { 56 // SAFETY: 57 // `pdev` valid by the invariants of `Device`. 58 // `num` is checked for validity by a previous call to `Device::resource_len`. 59 unsafe { bindings::pci_release_region(pdev.as_raw(), num) }; 60 return Err(ENOMEM); 61 } 62 63 let io = match IoRaw::new(ioptr, len as usize) { 64 Ok(io) => io, 65 Err(err) => { 66 // SAFETY: 67 // `pdev` is valid by the invariants of `Device`. 68 // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region. 69 // `num` is checked for validity by a previous call to `Device::resource_len`. 70 unsafe { Self::do_release(pdev, ioptr, num) }; 71 return Err(err); 72 } 73 }; 74 75 Ok(Bar { 76 pdev: pdev.into(), 77 io, 78 num, 79 }) 80 } 81 82 /// # Safety 83 /// 84 /// `ioptr` must be a valid pointer to the memory mapped PCI BAR number `num`. 85 unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { 86 // SAFETY: 87 // `pdev` is valid by the invariants of `Device`. 88 // `ioptr` is valid by the safety requirements. 89 // `num` is valid by the safety requirements. 90 unsafe { 91 bindings::pci_iounmap(pdev.as_raw(), ioptr as *mut c_void); 92 bindings::pci_release_region(pdev.as_raw(), num); 93 } 94 } 95 96 fn release(&self) { 97 // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`. 98 unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) }; 99 } 100 } 101 102 impl Bar { 103 #[inline] 104 pub(super) fn index_is_valid(index: u32) -> bool { 105 // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries. 106 index < bindings::PCI_NUM_RESOURCES 107 } 108 } 109 110 impl<const SIZE: usize> Drop for Bar<SIZE> { 111 fn drop(&mut self) { 112 self.release(); 113 } 114 } 115 116 impl<const SIZE: usize> Deref for Bar<SIZE> { 117 type Target = Io<SIZE>; 118 119 fn deref(&self) -> &Self::Target { 120 // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. 121 unsafe { Io::from_raw(&self.io) } 122 } 123 } 124 125 impl Device<device::Bound> { 126 /// Maps an entire PCI BAR after performing a region-request on it. I/O operation bound checks 127 /// can be performed on compile time for offsets (plus the requested type size) < SIZE. 128 pub fn iomap_region_sized<'a, const SIZE: usize>( 129 &'a self, 130 bar: u32, 131 name: &'a CStr, 132 ) -> impl PinInit<Devres<Bar<SIZE>>, Error> + 'a { 133 Devres::new(self.as_ref(), Bar::<SIZE>::new(self, bar, name)) 134 } 135 136 /// Maps an entire PCI BAR after performing a region-request on it. 137 pub fn iomap_region<'a>( 138 &'a self, 139 bar: u32, 140 name: &'a CStr, 141 ) -> impl PinInit<Devres<Bar>, Error> + 'a { 142 self.iomap_region_sized::<0>(bar, name) 143 } 144 } 145