diff options
Diffstat (limited to 'rust/kernel/io')
| -rw-r--r-- | rust/kernel/io/mem.rs | 279 | ||||
| -rw-r--r-- | rust/kernel/io/resource.rs | 230 | 
2 files changed, 509 insertions, 0 deletions
diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs new file mode 100644 index 000000000000..6f99510bfc3a --- /dev/null +++ b/rust/kernel/io/mem.rs @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic memory-mapped IO. + +use core::ops::Deref; + +use crate::c_str; +use crate::device::Bound; +use crate::device::Device; +use crate::devres::Devres; +use crate::io; +use crate::io::resource::Region; +use crate::io::resource::Resource; +use crate::io::Io; +use crate::io::IoRaw; +use crate::prelude::*; + +/// An IO request for a specific device and resource. +pub struct IoRequest<'a> { +    device: &'a Device<Bound>, +    resource: &'a Resource, +} + +impl<'a> IoRequest<'a> { +    /// Creates a new [`IoRequest`] instance. +    /// +    /// # Safety +    /// +    /// Callers must ensure that `resource` is valid for `device` during the +    /// lifetime `'a`. +    pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> Self { +        IoRequest { device, resource } +    } + +    /// Maps an [`IoRequest`] where the size is known at compile time. +    /// +    /// This uses the [`ioremap()`] C API. +    /// +    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device +    /// +    /// # Examples +    /// +    /// The following example uses a [`kernel::platform::Device`] for +    /// illustration purposes. +    /// +    /// ```no_run +    /// use kernel::{bindings, c_str, platform, of, device::Core}; +    /// struct SampleDriver; +    /// +    /// impl platform::Driver for SampleDriver { +    ///    # type IdInfo = (); +    /// +    ///    fn probe( +    ///       pdev: &platform::Device<Core>, +    ///       info: Option<&Self::IdInfo>, +    ///    ) -> Result<Pin<KBox<Self>>> { +    ///       let offset = 0; // Some offset. +    /// +    ///       // If the size is known at compile time, use [`Self::iomap_sized`]. +    ///       // +    ///       // No runtime checks will apply when reading and writing. +    ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; +    ///       let iomem = request.iomap_sized::<42>(); +    ///       let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; +    /// +    ///       let io = iomem.access(pdev.as_ref())?; +    /// +    ///       // Read and write a 32-bit value at `offset`. +    ///       let data = io.read32_relaxed(offset); +    /// +    ///       io.write32_relaxed(data, offset); +    /// +    ///       # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) +    ///     } +    /// } +    /// ``` +    pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a { +        IoMem::new(self) +    } + +    /// Same as [`Self::iomap_sized`] but with exclusive access to the +    /// underlying region. +    /// +    /// This uses the [`ioremap()`] C API. +    /// +    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device +    pub fn iomap_exclusive_sized<const SIZE: usize>( +        self, +    ) -> impl PinInit<Devres<ExclusiveIoMem<SIZE>>, Error> + 'a { +        ExclusiveIoMem::new(self) +    } + +    /// Maps an [`IoRequest`] where the size is not known at compile time, +    /// +    /// This uses the [`ioremap()`] C API. +    /// +    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device +    /// +    /// # Examples +    /// +    /// The following example uses a [`kernel::platform::Device`] for +    /// illustration purposes. +    /// +    /// ```no_run +    /// use kernel::{bindings, c_str, platform, of, device::Core}; +    /// struct SampleDriver; +    /// +    /// impl platform::Driver for SampleDriver { +    ///    # type IdInfo = (); +    /// +    ///    fn probe( +    ///       pdev: &platform::Device<Core>, +    ///       info: Option<&Self::IdInfo>, +    ///    ) -> Result<Pin<KBox<Self>>> { +    ///       let offset = 0; // Some offset. +    /// +    ///       // Unlike [`Self::iomap_sized`], here the size of the memory region +    ///       // is not known at compile time, so only the `try_read*` and `try_write*` +    ///       // family of functions should be used, leading to runtime checks on every +    ///       // access. +    ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; +    ///       let iomem = request.iomap(); +    ///       let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; +    /// +    ///       let io = iomem.access(pdev.as_ref())?; +    /// +    ///       let data = io.try_read32_relaxed(offset)?; +    /// +    ///       io.try_write32_relaxed(data, offset)?; +    /// +    ///       # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) +    ///     } +    /// } +    /// ``` +    pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a { +        Self::iomap_sized::<0>(self) +    } + +    /// Same as [`Self::iomap`] but with exclusive access to the underlying +    /// region. +    pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a { +        Self::iomap_exclusive_sized::<0>(self) +    } +} + +/// An exclusive memory-mapped IO region. +/// +/// # Invariants +/// +/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`]. +pub struct ExclusiveIoMem<const SIZE: usize> { +    /// The underlying `IoMem` instance. +    iomem: IoMem<SIZE>, + +    /// The region abstraction. This represents exclusive access to the +    /// range represented by the underlying `iomem`. +    /// +    /// This field is needed for ownership of the region. +    _region: Region, +} + +impl<const SIZE: usize> ExclusiveIoMem<SIZE> { +    /// Creates a new `ExclusiveIoMem` instance. +    fn ioremap(resource: &Resource) -> Result<Self> { +        let start = resource.start(); +        let size = resource.size(); +        let name = resource.name().unwrap_or(c_str!("")); + +        let region = resource +            .request_region( +                start, +                size, +                name.to_cstring()?, +                io::resource::Flags::IORESOURCE_MEM, +            ) +            .ok_or(EBUSY)?; + +        let iomem = IoMem::ioremap(resource)?; + +        let iomem = ExclusiveIoMem { +            iomem, +            _region: region, +        }; + +        Ok(iomem) +    } + +    /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`]. +    pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a { +        let dev = io_request.device; +        let res = io_request.resource; + +        Devres::new(dev, Self::ioremap(res)) +    } +} + +impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> { +    type Target = Io<SIZE>; + +    fn deref(&self) -> &Self::Target { +        &self.iomem +    } +} + +/// A generic memory-mapped IO region. +/// +/// Accesses to the underlying region is checked either at compile time, if the +/// region's size is known at that point, or at runtime otherwise. +/// +/// # Invariants +/// +/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the +/// start of the I/O memory mapped region. +pub struct IoMem<const SIZE: usize = 0> { +    io: IoRaw<SIZE>, +} + +impl<const SIZE: usize> IoMem<SIZE> { +    fn ioremap(resource: &Resource) -> Result<Self> { +        // Note: Some ioremap() implementations use types that depend on the CPU +        // word width rather than the bus address width. +        // +        // TODO: Properly address this in the C code to avoid this `try_into`. +        let size = resource.size().try_into()?; +        if size == 0 { +            return Err(EINVAL); +        } + +        let res_start = resource.start(); + +        let addr = if resource +            .flags() +            .contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED) +        { +            // SAFETY: +            // - `res_start` and `size` are read from a presumably valid `struct resource`. +            // - `size` is known not to be zero at this point. +            unsafe { bindings::ioremap_np(res_start, size) } +        } else { +            // SAFETY: +            // - `res_start` and `size` are read from a presumably valid `struct resource`. +            // - `size` is known not to be zero at this point. +            unsafe { bindings::ioremap(res_start, size) } +        }; + +        if addr.is_null() { +            return Err(ENOMEM); +        } + +        let io = IoRaw::new(addr as usize, size)?; +        let io = IoMem { io }; + +        Ok(io) +    } + +    /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`]. +    pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a { +        let dev = io_request.device; +        let res = io_request.resource; + +        Devres::new(dev, Self::ioremap(res)) +    } +} + +impl<const SIZE: usize> Drop for IoMem<SIZE> { +    fn drop(&mut self) { +        // SAFETY: Safe as by the invariant of `Io`. +        unsafe { bindings::iounmap(self.io.addr() as *mut c_void) } +    } +} + +impl<const SIZE: usize> Deref for IoMem<SIZE> { +    type Target = Io<SIZE>; + +    fn deref(&self) -> &Self::Target { +        // SAFETY: Safe as by the invariant of `IoMem`. +        unsafe { Io::from_raw(&self.io) } +    } +} diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs new file mode 100644 index 000000000000..bea3ee0ed87b --- /dev/null +++ b/rust/kernel/io/resource.rs @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Abstractions for [system +//! resources](https://docs.kernel.org/core-api/kernel-api.html#resources-management). +//! +//! C header: [`include/linux/ioport.h`](srctree/include/linux/ioport.h) + +use core::ops::Deref; +use core::ptr::NonNull; + +use crate::prelude::*; +use crate::str::{CStr, CString}; +use crate::types::Opaque; + +/// Resource Size type. +/// +/// This is a type alias to either `u32` or `u64` depending on the config option +/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures. +pub type ResourceSize = bindings::phys_addr_t; + +/// A region allocated from a parent [`Resource`]. +/// +/// # Invariants +/// +/// - `self.0` points to a valid `bindings::resource` that was obtained through +///   `bindings::__request_region`. +pub struct Region { +    /// The resource returned when the region was requested. +    resource: NonNull<bindings::resource>, +    /// The name that was passed in when the region was requested. We need to +    /// store it for ownership reasons. +    _name: CString, +} + +impl Deref for Region { +    type Target = Resource; + +    fn deref(&self) -> &Self::Target { +        // SAFETY: Safe as per the invariant of `Region`. +        unsafe { Resource::from_raw(self.resource.as_ptr()) } +    } +} + +impl Drop for Region { +    fn drop(&mut self) { +        let (flags, start, size) = { +            let res = &**self; +            (res.flags(), res.start(), res.size()) +        }; + +        let release_fn = if flags.contains(Flags::IORESOURCE_MEM) { +            bindings::release_mem_region +        } else { +            bindings::release_region +        }; + +        // SAFETY: Safe as per the invariant of `Region`. +        unsafe { release_fn(start, size) }; +    } +} + +// SAFETY: `Region` only holds a pointer to a C `struct resource`, which is safe to be used from +// any thread. +unsafe impl Send for Region {} + +// SAFETY: `Region` only holds a pointer to a C `struct resource`, references to which are +// safe to be used from any thread. +unsafe impl Sync for Region {} + +/// A resource abstraction. +/// +/// # Invariants +/// +/// [`Resource`] is a transparent wrapper around a valid `bindings::resource`. +#[repr(transparent)] +pub struct Resource(Opaque<bindings::resource>); + +impl Resource { +    /// Creates a reference to a [`Resource`] from a valid pointer. +    /// +    /// # Safety +    /// +    /// The caller must ensure that for the duration of 'a, the pointer will +    /// point at a valid `bindings::resource`. +    /// +    /// The caller must also ensure that the [`Resource`] is only accessed via the +    /// returned reference for the duration of 'a. +    pub(crate) const unsafe fn from_raw<'a>(ptr: *mut bindings::resource) -> &'a Self { +        // SAFETY: Self is a transparent wrapper around `Opaque<bindings::resource>`. +        unsafe { &*ptr.cast() } +    } + +    /// Requests a resource region. +    /// +    /// Exclusive access will be given and the region will be marked as busy. +    /// Further calls to [`Self::request_region`] will return [`None`] if +    /// the region, or a part of it, is already in use. +    pub fn request_region( +        &self, +        start: ResourceSize, +        size: ResourceSize, +        name: CString, +        flags: Flags, +    ) -> Option<Region> { +        // SAFETY: +        // - Safe as per the invariant of `Resource`. +        // - `__request_region` will store a reference to the name, but that is +        // safe as we own it and it will not be dropped until the `Region` is +        // dropped. +        let region = unsafe { +            bindings::__request_region( +                self.0.get(), +                start, +                size, +                name.as_char_ptr(), +                flags.0 as c_int, +            ) +        }; + +        Some(Region { +            resource: NonNull::new(region)?, +            _name: name, +        }) +    } + +    /// Returns the size of the resource. +    pub fn size(&self) -> ResourceSize { +        let inner = self.0.get(); +        // SAFETY: Safe as per the invariants of `Resource`. +        unsafe { bindings::resource_size(inner) } +    } + +    /// Returns the start address of the resource. +    pub fn start(&self) -> ResourceSize { +        let inner = self.0.get(); +        // SAFETY: Safe as per the invariants of `Resource`. +        unsafe { (*inner).start } +    } + +    /// Returns the name of the resource. +    pub fn name(&self) -> Option<&CStr> { +        let inner = self.0.get(); + +        // SAFETY: Safe as per the invariants of `Resource`. +        let name = unsafe { (*inner).name }; + +        if name.is_null() { +            return None; +        } + +        // SAFETY: In the C code, `resource::name` either contains a null +        // pointer or points to a valid NUL-terminated C string, and at this +        // point we know it is not null, so we can safely convert it to a +        // `CStr`. +        Some(unsafe { CStr::from_char_ptr(name) }) +    } + +    /// Returns the flags associated with the resource. +    pub fn flags(&self) -> Flags { +        let inner = self.0.get(); +        // SAFETY: Safe as per the invariants of `Resource`. +        let flags = unsafe { (*inner).flags }; + +        Flags(flags) +    } +} + +// SAFETY: `Resource` only holds a pointer to a C `struct resource`, which is +// safe to be used from any thread. +unsafe impl Send for Resource {} + +// SAFETY: `Resource` only holds a pointer to a C `struct resource`, references +// to which are safe to be used from any thread. +unsafe impl Sync for Resource {} + +/// Resource flags as stored in the C `struct resource::flags` field. +/// +/// They can be combined with the operators `|`, `&`, and `!`. +/// +/// Values can be used from the associated constants such as +/// [`Flags::IORESOURCE_IO`]. +#[derive(Clone, Copy, PartialEq)] +pub struct Flags(c_ulong); + +impl Flags { +    /// Check whether `flags` is contained in `self`. +    pub fn contains(self, flags: Flags) -> bool { +        (self & flags) == flags +    } +} + +impl core::ops::BitOr for Flags { +    type Output = Self; +    fn bitor(self, rhs: Self) -> Self::Output { +        Self(self.0 | rhs.0) +    } +} + +impl core::ops::BitAnd for Flags { +    type Output = Self; +    fn bitand(self, rhs: Self) -> Self::Output { +        Self(self.0 & rhs.0) +    } +} + +impl core::ops::Not for Flags { +    type Output = Self; +    fn not(self) -> Self::Output { +        Self(!self.0) +    } +} + +impl Flags { +    /// PCI/ISA I/O ports. +    pub const IORESOURCE_IO: Flags = Flags::new(bindings::IORESOURCE_IO); + +    /// Resource is software muxed. +    pub const IORESOURCE_MUXED: Flags = Flags::new(bindings::IORESOURCE_MUXED); + +    /// Resource represents a memory region. +    pub const IORESOURCE_MEM: Flags = Flags::new(bindings::IORESOURCE_MEM); + +    /// Resource represents a memory region that must be ioremaped using `ioremap_np`. +    pub const IORESOURCE_MEM_NONPOSTED: Flags = Flags::new(bindings::IORESOURCE_MEM_NONPOSTED); + +    const fn new(value: u32) -> Self { +        crate::build_assert!(value as u64 <= c_ulong::MAX as u64); +        Flags(value as c_ulong) +    } +}  | 
