Skip to main content

kernel/io/
mem.rs

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