329 lines · 10.4 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 //! Generic memory-mapped IO. use crate::{    device::{        Bound,        Device, //    },    devres::DevresLt,    io::{        self,        resource::{            Region,            Resource, //        },        IoBase,        Mmio,        MmioBackend,        MmioRaw, //    },    prelude::*,    types::{        CovariantForLt,        ForLt, //    },}; /// 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,    ///     device::Core,    ///     io::Io,    ///     of,    ///     platform,    /// };    /// struct SampleDriver;    ///    /// impl platform::Driver for SampleDriver {    ///    # type IdInfo = ();    ///    # type Data<'bound> = Self;    ///    ///    fn probe<'bound>(    ///       pdev: &'bound platform::Device<Core<'_>>,    ///       info: Option<&'bound Self::IdInfo>,    ///    ) -> impl PinInit<Self, Error> + 'bound {    ///       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>()?;    ///    ///       // Read and write a 32-bit value at `offset`.    ///       let data = iomem.read32(offset);    ///    ///       iomem.write32(data, offset);    ///    ///       # Ok(SampleDriver)    ///     }    /// }    /// ```    pub fn iomap_sized<const SIZE: usize>(self) -> Result<IoMem<'a, SIZE>> {        IoMem::ioremap(self.device, self.resource)    }     /// 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) -> Result<ExclusiveIoMem<'a, SIZE>> {        ExclusiveIoMem::ioremap(self.device, self.resource)    }     /// 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,    ///     device::Core,    ///     io::Io,    ///     of,    ///     platform,    /// };    /// struct SampleDriver;    ///    /// impl platform::Driver for SampleDriver {    ///    # type IdInfo = ();    ///    # type Data<'bound> = Self;    ///    ///    fn probe<'bound>(    ///       pdev: &'bound platform::Device<Core<'_>>,    ///       info: Option<&'bound Self::IdInfo>,    ///    ) -> impl PinInit<Self, Error> + 'bound {    ///       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 data = iomem.try_read32(offset)?;    ///    ///       iomem.try_write32(data, offset)?;    ///    ///       # Ok(SampleDriver)    ///     }    /// }    /// ```    pub fn iomap(self) -> Result<IoMem<'a>> {        self.iomap_sized::<0>()    }     /// Same as [`Self::iomap`] but with exclusive access to the underlying    /// region.    pub fn iomap_exclusive(self) -> Result<ExclusiveIoMem<'a, 0>> {        self.iomap_exclusive_sized::<0>()    }} /// An exclusive memory-mapped IO region.////// # Invariants////// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].pub struct ExclusiveIoMem<'a, const SIZE: usize> {    /// The underlying `IoMem` instance.    iomem: IoMem<'a, 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> ForLt for ExclusiveIoMem<'static, SIZE> {    type Of<'a> = ExclusiveIoMem<'a, SIZE>;} // SAFETY: `ExclusiveIoMem<'a, SIZE>` is covariant over `'a`; it holds an `IoMem<'a, SIZE>`,// which holds `&'a Device<Bound>`, which is covariant.unsafe impl<const SIZE: usize> CovariantForLt for ExclusiveIoMem<'static, SIZE> {} /// A device-managed exclusive I/O memory region.////// See [`ExclusiveIoMem::into_devres`].pub type DevresExclusiveIoMem<const SIZE: usize> = DevresLt<ExclusiveIoMem<'static, SIZE>>; impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> {    /// Creates a new `ExclusiveIoMem` instance.    fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {        let start = resource.start();        let size = resource.size();        let name = resource.name().unwrap_or_default();         let region = resource            .request_region(                start,                size,                name.to_cstring()?,                io::resource::Flags::IORESOURCE_MEM,            )            .ok_or(EBUSY)?;         let iomem = IoMem::ioremap(dev, resource)?;         Ok(ExclusiveIoMem {            iomem,            _region: region,        })    }     /// Consume the `ExclusiveIoMem` and register it as a device-managed resource.    ///    /// The returned [`DevresExclusiveIoMem`] can outlive the original borrow and be stored in    /// driver data. Access to the I/O memory is revoked automatically when the device is unbound.    pub fn into_devres(self) -> Result<DevresExclusiveIoMem<SIZE>> {        let dev = self.iomem.dev;        // SAFETY: `ExclusiveIoMem` only holds a device reference and an I/O mapping, both of        // which remain valid for the device's full bound scope, not just for `'a`.        unsafe { DevresLt::new(dev, self) }    }} impl<'a, const SIZE: usize> IoBase<'a> for &'a ExclusiveIoMem<'_, SIZE> {    type Backend = MmioBackend;    type Target = super::Region<SIZE>;     #[inline]    fn as_view(self) -> Mmio<'a, Self::Target> {        self.iomem.as_view()    }} /// 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 [`MmioRaw`] instance that holds a valid pointer to the/// start of the I/O memory mapped region.pub struct IoMem<'a, const SIZE: usize = 0> {    dev: &'a Device<Bound>,    io: MmioRaw<super::Region<SIZE>>,} impl<const SIZE: usize> ForLt for IoMem<'static, SIZE> {    type Of<'a> = IoMem<'a, SIZE>;} // SAFETY: `IoMem<'a, SIZE>` is covariant over `'a`; it holds `&'a Device<Bound>`,// which is covariant.unsafe impl<const SIZE: usize> CovariantForLt for IoMem<'static, SIZE> {} /// A device-managed I/O memory region.////// See [`IoMem::into_devres`].pub type DevresIoMem<const SIZE: usize = 0> = DevresLt<IoMem<'static, SIZE>>; impl<'a, const SIZE: usize> IoMem<'a, SIZE> {    fn ioremap(dev: &'a Device<Bound>, 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 = MmioRaw::new_region(addr as usize, size)?;        Ok(IoMem { dev, io })    }     /// Consume the `IoMem` and register it as a device-managed resource.    ///    /// The returned [`DevresIoMem`] can outlive the original borrow and be stored in driver data.    /// Access to the I/O memory is revoked automatically when the device is unbound.    pub fn into_devres(self) -> Result<DevresIoMem<SIZE>> {        let dev = self.dev;        // SAFETY: `IoMem` only holds a device reference and an I/O mapping, both of which        // remain valid for the device's full bound scope, not just for `'a`.        unsafe { DevresLt::new(dev, self) }    }} 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<'a, const SIZE: usize> IoBase<'a> for &'a IoMem<'_, SIZE> {    type Backend = MmioBackend;    type Target = super::Region<SIZE>;     #[inline]    fn as_view(self) -> Mmio<'a, Self::Target> {        // SAFETY: Safe as by the invariant of `IoMem`.        unsafe { Mmio::from_raw(self.io) }    }}