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