Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rust: pci: implement I/O mappable `pci::Bar`

Implement `pci::Bar`, `pci::Device::iomap_region` and
`pci::Device::iomap_region_sized` to allow for I/O mappings of PCI BARs.

To ensure that a `pci::Bar`, and hence the I/O memory mapping, can't
out-live the PCI device, the `pci::Bar` type is always embedded into a
`Devres` container, such that the `pci::Bar` is revoked once the device
is unbound and hence the I/O mapped memory is unmapped.

A `pci::Bar` can be requested with (`pci::Device::iomap_region_sized`) or
without (`pci::Device::iomap_region`) a const generic representing the
minimal requested size of the I/O mapped memory region. In case of the
latter only runtime checked I/O reads / writes are possible.

Co-developed-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Tested-by: Dirk Behme <dirk.behme@de.bosch.com>
Link: https://lore.kernel.org/r/20241219170425.12036-11-dakr@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Danilo Krummrich and committed by
Greg Kroah-Hartman
bf9651f8 1bd8b6b2

+141 -1
+141 -1
rust/kernel/pci.rs
··· 5 5 //! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h) 6 6 7 7 use crate::{ 8 + alloc::flags::*, 8 9 bindings, container_of, device, 9 10 device_id::RawDeviceId, 11 + devres::Devres, 10 12 driver, 11 13 error::{to_result, Result}, 14 + io::Io, 15 + io::IoRaw, 12 16 str::CStr, 13 17 types::{ARef, ForeignOwnable, Opaque}, 14 18 ThisModule, 15 19 }; 16 - use core::ptr::addr_of_mut; 20 + use core::{ops::Deref, ptr::addr_of_mut}; 17 21 use kernel::prelude::*; 18 22 19 23 /// An adapter for the registration of PCI drivers. ··· 239 235 /// 240 236 /// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI 241 237 /// device, hence, also increments the base device' reference count. 238 + /// 239 + /// # Invariants 240 + /// 241 + /// `Device` hold a valid reference of `ARef<device::Device>` whose underlying `struct device` is a 242 + /// member of a `struct pci_dev`. 242 243 #[derive(Clone)] 243 244 pub struct Device(ARef<device::Device>); 245 + 246 + /// A PCI BAR to perform I/O-Operations on. 247 + /// 248 + /// # Invariants 249 + /// 250 + /// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O 251 + /// memory mapped PCI bar and its size. 252 + pub struct Bar<const SIZE: usize = 0> { 253 + pdev: Device, 254 + io: IoRaw<SIZE>, 255 + num: i32, 256 + } 257 + 258 + impl<const SIZE: usize> Bar<SIZE> { 259 + fn new(pdev: Device, num: u32, name: &CStr) -> Result<Self> { 260 + let len = pdev.resource_len(num)?; 261 + if len == 0 { 262 + return Err(ENOMEM); 263 + } 264 + 265 + // Convert to `i32`, since that's what all the C bindings use. 266 + let num = i32::try_from(num)?; 267 + 268 + // SAFETY: 269 + // `pdev` is valid by the invariants of `Device`. 270 + // `num` is checked for validity by a previous call to `Device::resource_len`. 271 + // `name` is always valid. 272 + let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) }; 273 + if ret != 0 { 274 + return Err(EBUSY); 275 + } 276 + 277 + // SAFETY: 278 + // `pdev` is valid by the invariants of `Device`. 279 + // `num` is checked for validity by a previous call to `Device::resource_len`. 280 + // `name` is always valid. 281 + let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize; 282 + if ioptr == 0 { 283 + // SAFETY: 284 + // `pdev` valid by the invariants of `Device`. 285 + // `num` is checked for validity by a previous call to `Device::resource_len`. 286 + unsafe { bindings::pci_release_region(pdev.as_raw(), num) }; 287 + return Err(ENOMEM); 288 + } 289 + 290 + let io = match IoRaw::new(ioptr, len as usize) { 291 + Ok(io) => io, 292 + Err(err) => { 293 + // SAFETY: 294 + // `pdev` is valid by the invariants of `Device`. 295 + // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region. 296 + // `num` is checked for validity by a previous call to `Device::resource_len`. 297 + unsafe { Self::do_release(&pdev, ioptr, num) }; 298 + return Err(err); 299 + } 300 + }; 301 + 302 + Ok(Bar { pdev, io, num }) 303 + } 304 + 305 + /// # Safety 306 + /// 307 + /// `ioptr` must be a valid pointer to the memory mapped PCI bar number `num`. 308 + unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { 309 + // SAFETY: 310 + // `pdev` is valid by the invariants of `Device`. 311 + // `ioptr` is valid by the safety requirements. 312 + // `num` is valid by the safety requirements. 313 + unsafe { 314 + bindings::pci_iounmap(pdev.as_raw(), ioptr as _); 315 + bindings::pci_release_region(pdev.as_raw(), num); 316 + } 317 + } 318 + 319 + fn release(&self) { 320 + // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`. 321 + unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) }; 322 + } 323 + } 324 + 325 + impl Bar { 326 + fn index_is_valid(index: u32) -> bool { 327 + // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries. 328 + index < bindings::PCI_NUM_RESOURCES 329 + } 330 + } 331 + 332 + impl<const SIZE: usize> Drop for Bar<SIZE> { 333 + fn drop(&mut self) { 334 + self.release(); 335 + } 336 + } 337 + 338 + impl<const SIZE: usize> Deref for Bar<SIZE> { 339 + type Target = Io<SIZE>; 340 + 341 + fn deref(&self) -> &Self::Target { 342 + // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. 343 + unsafe { Io::from_raw(&self.io) } 344 + } 345 + } 244 346 245 347 impl Device { 246 348 /// Create a PCI Device instance from an existing `device::Device`. ··· 392 282 pub fn set_master(&self) { 393 283 // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`. 394 284 unsafe { bindings::pci_set_master(self.as_raw()) }; 285 + } 286 + 287 + /// Returns the size of the given PCI bar resource. 288 + pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> { 289 + if !Bar::index_is_valid(bar) { 290 + return Err(EINVAL); 291 + } 292 + 293 + // SAFETY: 294 + // - `bar` is a valid bar number, as guaranteed by the above call to `Bar::index_is_valid`, 295 + // - by its type invariant `self.as_raw` is always a valid pointer to a `struct pci_dev`. 296 + Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into()?) }) 297 + } 298 + 299 + /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks 300 + /// can be performed on compile time for offsets (plus the requested type size) < SIZE. 301 + pub fn iomap_region_sized<const SIZE: usize>( 302 + &self, 303 + bar: u32, 304 + name: &CStr, 305 + ) -> Result<Devres<Bar<SIZE>>> { 306 + let bar = Bar::<SIZE>::new(self.clone(), bar, name)?; 307 + let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?; 308 + 309 + Ok(devres) 310 + } 311 + 312 + /// Mapps an entire PCI-BAR after performing a region-request on it. 313 + pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result<Devres<Bar>> { 314 + self.iomap_region_sized::<0>(bar, name) 395 315 } 396 316 } 397 317