File Index
Files and directories
Sign in
FILE

mem.rs

File preview with syntax highlighting.

linux-6.19.14/rust/kernel/io/mem.rs RUST
  1// SPDX-License-Identifier: GPL-2.0
  2
  3//! Generic memory-mapped IO.
  4
  5use core::ops::Deref;
  6
  7use crate::{
  8    c_str,
  9    device::{
 10        Bound,
 11        Device, //
 12    },
 13    devres::Devres,
 14    io::{
 15        self,
 16        resource::{
 17            Region,
 18            Resource, //
 19        },
 20        Io,
 21        IoRaw, //
 22    },
 23    prelude::*,
 24};
 25
 26/// An IO request for a specific device and resource.
 27pub struct IoRequest<'a> {
 28    device: &'a Device<Bound>,
 29    resource: &'a Resource,
 30}
 31
 32impl<'a> IoRequest<'a> {
 33    /// Creates a new [`IoRequest`] instance.
 34    ///
 35    /// # Safety
 36    ///
 37    /// Callers must ensure that `resource` is valid for `device` during the
 38    /// lifetime `'a`.
 39    pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> Self {
 40        IoRequest { device, resource }
 41    }
 42
 43    /// Maps an [`IoRequest`] where the size is known at compile time.
 44    ///
 45    /// This uses the [`ioremap()`] C API.
 46    ///
 47    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
 48    ///
 49    /// # Examples
 50    ///
 51    /// The following example uses a [`kernel::platform::Device`] for
 52    /// illustration purposes.
 53    ///
 54    /// ```no_run
 55    /// use kernel::{bindings, c_str, platform, of, device::Core};
 56    /// struct SampleDriver;
 57    ///
 58    /// impl platform::Driver for SampleDriver {
 59    ///    # type IdInfo = ();
 60    ///
 61    ///    fn probe(
 62    ///       pdev: &platform::Device<Core>,
 63    ///       info: Option<&Self::IdInfo>,
 64    ///    ) -> impl PinInit<Self, Error> {
 65    ///       let offset = 0; // Some offset.
 66    ///
 67    ///       // If the size is known at compile time, use [`Self::iomap_sized`].
 68    ///       //
 69    ///       // No runtime checks will apply when reading and writing.
 70    ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
 71    ///       let iomem = request.iomap_sized::<42>();
 72    ///       let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
 73    ///
 74    ///       let io = iomem.access(pdev.as_ref())?;
 75    ///
 76    ///       // Read and write a 32-bit value at `offset`.
 77    ///       let data = io.read32_relaxed(offset);
 78    ///
 79    ///       io.write32_relaxed(data, offset);
 80    ///
 81    ///       # Ok(SampleDriver)
 82    ///     }
 83    /// }
 84    /// ```
 85    pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a {
 86        IoMem::new(self)
 87    }
 88
 89    /// Same as [`Self::iomap_sized`] but with exclusive access to the
 90    /// underlying region.
 91    ///
 92    /// This uses the [`ioremap()`] C API.
 93    ///
 94    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
 95    pub fn iomap_exclusive_sized<const SIZE: usize>(
 96        self,
 97    ) -> impl PinInit<Devres<ExclusiveIoMem<SIZE>>, Error> + 'a {
 98        ExclusiveIoMem::new(self)
 99    }
100
101    /// Maps an [`IoRequest`] where the size is not known at compile time,
102    ///
103    /// This uses the [`ioremap()`] C API.
104    ///
105    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
106    ///
107    /// # Examples
108    ///
109    /// The following example uses a [`kernel::platform::Device`] for
110    /// illustration purposes.
111    ///
112    /// ```no_run
113    /// use kernel::{bindings, c_str, platform, of, device::Core};
114    /// struct SampleDriver;
115    ///
116    /// impl platform::Driver for SampleDriver {
117    ///    # type IdInfo = ();
118    ///
119    ///    fn probe(
120    ///       pdev: &platform::Device<Core>,
121    ///       info: Option<&Self::IdInfo>,
122    ///    ) -> impl PinInit<Self, Error> {
123    ///       let offset = 0; // Some offset.
124    ///
125    ///       // Unlike [`Self::iomap_sized`], here the size of the memory region
126    ///       // is not known at compile time, so only the `try_read*` and `try_write*`
127    ///       // family of functions should be used, leading to runtime checks on every
128    ///       // access.
129    ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
130    ///       let iomem = request.iomap();
131    ///       let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
132    ///
133    ///       let io = iomem.access(pdev.as_ref())?;
134    ///
135    ///       let data = io.try_read32_relaxed(offset)?;
136    ///
137    ///       io.try_write32_relaxed(data, offset)?;
138    ///
139    ///       # Ok(SampleDriver)
140    ///     }
141    /// }
142    /// ```
143    pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a {
144        Self::iomap_sized::<0>(self)
145    }
146
147    /// Same as [`Self::iomap`] but with exclusive access to the underlying
148    /// region.
149    pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a {
150        Self::iomap_exclusive_sized::<0>(self)
151    }
152}
153
154/// An exclusive memory-mapped IO region.
155///
156/// # Invariants
157///
158/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].
159pub struct ExclusiveIoMem<const SIZE: usize> {
160    /// The underlying `IoMem` instance.
161    iomem: IoMem<SIZE>,
162
163    /// The region abstraction. This represents exclusive access to the
164    /// range represented by the underlying `iomem`.
165    ///
166    /// This field is needed for ownership of the region.
167    _region: Region,
168}
169
170impl<const SIZE: usize> ExclusiveIoMem<SIZE> {
171    /// Creates a new `ExclusiveIoMem` instance.
172    fn ioremap(resource: &Resource) -> Result<Self> {
173        let start = resource.start();
174        let size = resource.size();
175        let name = resource.name().unwrap_or(c_str!(""));
176
177        let region = resource
178            .request_region(
179                start,
180                size,
181                name.to_cstring()?,
182                io::resource::Flags::IORESOURCE_MEM,
183            )
184            .ok_or(EBUSY)?;
185
186        let iomem = IoMem::ioremap(resource)?;
187
188        let iomem = ExclusiveIoMem {
189            iomem,
190            _region: region,
191        };
192
193        Ok(iomem)
194    }
195
196    /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`].
197    pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
198        let dev = io_request.device;
199        let res = io_request.resource;
200
201        Devres::new(dev, Self::ioremap(res))
202    }
203}
204
205impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
206    type Target = Io<SIZE>;
207
208    fn deref(&self) -> &Self::Target {
209        &self.iomem
210    }
211}
212
213/// A generic memory-mapped IO region.
214///
215/// Accesses to the underlying region is checked either at compile time, if the
216/// region's size is known at that point, or at runtime otherwise.
217///
218/// # Invariants
219///
220/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the
221/// start of the I/O memory mapped region.
222pub struct IoMem<const SIZE: usize = 0> {
223    io: IoRaw<SIZE>,
224}
225
226impl<const SIZE: usize> IoMem<SIZE> {
227    fn ioremap(resource: &Resource) -> Result<Self> {
228        // Note: Some ioremap() implementations use types that depend on the CPU
229        // word width rather than the bus address width.
230        //
231        // TODO: Properly address this in the C code to avoid this `try_into`.
232        let size = resource.size().try_into()?;
233        if size == 0 {
234            return Err(EINVAL);
235        }
236
237        let res_start = resource.start();
238
239        let addr = if resource
240            .flags()
241            .contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED)
242        {
243            // SAFETY:
244            // - `res_start` and `size` are read from a presumably valid `struct resource`.
245            // - `size` is known not to be zero at this point.
246            unsafe { bindings::ioremap_np(res_start, size) }
247        } else {
248            // SAFETY:
249            // - `res_start` and `size` are read from a presumably valid `struct resource`.
250            // - `size` is known not to be zero at this point.
251            unsafe { bindings::ioremap(res_start, size) }
252        };
253
254        if addr.is_null() {
255            return Err(ENOMEM);
256        }
257
258        let io = IoRaw::new(addr as usize, size)?;
259        let io = IoMem { io };
260
261        Ok(io)
262    }
263
264    /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`].
265    pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
266        let dev = io_request.device;
267        let res = io_request.resource;
268
269        Devres::new(dev, Self::ioremap(res))
270    }
271}
272
273impl<const SIZE: usize> Drop for IoMem<SIZE> {
274    fn drop(&mut self) {
275        // SAFETY: Safe as by the invariant of `Io`.
276        unsafe { bindings::iounmap(self.io.addr() as *mut c_void) }
277    }
278}
279
280impl<const SIZE: usize> Deref for IoMem<SIZE> {
281    type Target = Io<SIZE>;
282
283    fn deref(&self) -> &Self::Target {
284        // SAFETY: Safe as by the invariant of `IoMem`.
285        unsafe { Io::from_raw(&self.io) }
286    }
287}
~/ Read-only file preview.