|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! Abstractions for [system |
| 4 | +//! resources](https://docs.kernel.org/core-api/kernel-api.html#resources-management). |
| 5 | +//! |
| 6 | +//! C header: [`include/linux/ioport.h`](srctree/include/linux/ioport.h) |
| 7 | +
|
| 8 | +use core::ops::Deref; |
| 9 | +use core::ptr::NonNull; |
| 10 | + |
| 11 | +use crate::prelude::*; |
| 12 | +use crate::str::{CStr, CString}; |
| 13 | +use crate::types::Opaque; |
| 14 | + |
| 15 | +/// Resource Size type. |
| 16 | +/// |
| 17 | +/// This is a type alias to either `u32` or `u64` depending on the config option |
| 18 | +/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures. |
| 19 | +pub type ResourceSize = bindings::phys_addr_t; |
| 20 | + |
| 21 | +/// A region allocated from a parent [`Resource`]. |
| 22 | +/// |
| 23 | +/// # Invariants |
| 24 | +/// |
| 25 | +/// - `self.0` points to a valid `bindings::resource` that was obtained through |
| 26 | +/// `bindings::__request_region`. |
| 27 | +pub struct Region { |
| 28 | + /// The resource returned when the region was requested. |
| 29 | + resource: NonNull<bindings::resource>, |
| 30 | + /// The name that was passed in when the region was requested. We need to |
| 31 | + /// store it for ownership reasons. |
| 32 | + _name: CString, |
| 33 | +} |
| 34 | + |
| 35 | +impl Deref for Region { |
| 36 | + type Target = Resource; |
| 37 | + |
| 38 | + fn deref(&self) -> &Self::Target { |
| 39 | + // SAFETY: Safe as per the invariant of `Region`. |
| 40 | + unsafe { Resource::from_raw(self.resource.as_ptr()) } |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +impl Drop for Region { |
| 45 | + fn drop(&mut self) { |
| 46 | + let (flags, start, size) = { |
| 47 | + let res = &**self; |
| 48 | + (res.flags(), res.start(), res.size()) |
| 49 | + }; |
| 50 | + |
| 51 | + let release_fn = if flags.contains(Flags::IORESOURCE_MEM) { |
| 52 | + bindings::release_mem_region |
| 53 | + } else { |
| 54 | + bindings::release_region |
| 55 | + }; |
| 56 | + |
| 57 | + // SAFETY: Safe as per the invariant of `Region`. |
| 58 | + unsafe { release_fn(start, size) }; |
| 59 | + } |
| 60 | +} |
| 61 | + |
| 62 | +// SAFETY: `Region` only holds a pointer to a C `struct resource`, which is safe to be used from |
| 63 | +// any thread. |
| 64 | +unsafe impl Send for Region {} |
| 65 | + |
| 66 | +// SAFETY: `Region` only holds a pointer to a C `struct resource`, references to which are |
| 67 | +// safe to be used from any thread. |
| 68 | +unsafe impl Sync for Region {} |
| 69 | + |
| 70 | +/// A resource abstraction. |
| 71 | +/// |
| 72 | +/// # Invariants |
| 73 | +/// |
| 74 | +/// [`Resource`] is a transparent wrapper around a valid `bindings::resource`. |
| 75 | +#[repr(transparent)] |
| 76 | +pub struct Resource(Opaque<bindings::resource>); |
| 77 | + |
| 78 | +impl Resource { |
| 79 | + /// Creates a reference to a [`Resource`] from a valid pointer. |
| 80 | + /// |
| 81 | + /// # Safety |
| 82 | + /// |
| 83 | + /// The caller must ensure that for the duration of 'a, the pointer will |
| 84 | + /// point at a valid `bindings::resource`. |
| 85 | + /// |
| 86 | + /// The caller must also ensure that the [`Resource`] is only accessed via the |
| 87 | + /// returned reference for the duration of 'a. |
| 88 | + pub(crate) const unsafe fn from_raw<'a>(ptr: *mut bindings::resource) -> &'a Self { |
| 89 | + // SAFETY: Self is a transparent wrapper around `Opaque<bindings::resource>`. |
| 90 | + unsafe { &*ptr.cast() } |
| 91 | + } |
| 92 | + |
| 93 | + /// Requests a resource region. |
| 94 | + /// |
| 95 | + /// Exclusive access will be given and the region will be marked as busy. |
| 96 | + /// Further calls to [`Self::request_region`] will return [`None`] if |
| 97 | + /// the region, or a part of it, is already in use. |
| 98 | + pub fn request_region( |
| 99 | + &self, |
| 100 | + start: ResourceSize, |
| 101 | + size: ResourceSize, |
| 102 | + name: CString, |
| 103 | + flags: Flags, |
| 104 | + ) -> Option<Region> { |
| 105 | + // SAFETY: |
| 106 | + // - Safe as per the invariant of `Resource`. |
| 107 | + // - `__request_region` will store a reference to the name, but that is |
| 108 | + // safe as we own it and it will not be dropped until the `Region` is |
| 109 | + // dropped. |
| 110 | + let region = unsafe { |
| 111 | + bindings::__request_region( |
| 112 | + self.0.get(), |
| 113 | + start, |
| 114 | + size, |
| 115 | + name.as_char_ptr(), |
| 116 | + flags.0 as c_int, |
| 117 | + ) |
| 118 | + }; |
| 119 | + |
| 120 | + Some(Region { |
| 121 | + resource: NonNull::new(region)?, |
| 122 | + _name: name, |
| 123 | + }) |
| 124 | + } |
| 125 | + |
| 126 | + /// Returns the size of the resource. |
| 127 | + pub fn size(&self) -> ResourceSize { |
| 128 | + let inner = self.0.get(); |
| 129 | + // SAFETY: Safe as per the invariants of `Resource`. |
| 130 | + unsafe { bindings::resource_size(inner) } |
| 131 | + } |
| 132 | + |
| 133 | + /// Returns the start address of the resource. |
| 134 | + pub fn start(&self) -> ResourceSize { |
| 135 | + let inner = self.0.get(); |
| 136 | + // SAFETY: Safe as per the invariants of `Resource`. |
| 137 | + unsafe { (*inner).start } |
| 138 | + } |
| 139 | + |
| 140 | + /// Returns the name of the resource. |
| 141 | + pub fn name(&self) -> Option<&CStr> { |
| 142 | + let inner = self.0.get(); |
| 143 | + |
| 144 | + // SAFETY: Safe as per the invariants of `Resource`. |
| 145 | + let name = unsafe { (*inner).name }; |
| 146 | + |
| 147 | + if name.is_null() { |
| 148 | + return None; |
| 149 | + } |
| 150 | + |
| 151 | + // SAFETY: In the C code, `resource::name` either contains a null |
| 152 | + // pointer or points to a valid NUL-terminated C string, and at this |
| 153 | + // point we know it is not null, so we can safely convert it to a |
| 154 | + // `CStr`. |
| 155 | + Some(unsafe { CStr::from_char_ptr(name) }) |
| 156 | + } |
| 157 | + |
| 158 | + /// Returns the flags associated with the resource. |
| 159 | + pub fn flags(&self) -> Flags { |
| 160 | + let inner = self.0.get(); |
| 161 | + // SAFETY: Safe as per the invariants of `Resource`. |
| 162 | + let flags = unsafe { (*inner).flags }; |
| 163 | + |
| 164 | + Flags(flags) |
| 165 | + } |
| 166 | +} |
| 167 | + |
| 168 | +// SAFETY: `Resource` only holds a pointer to a C `struct resource`, which is |
| 169 | +// safe to be used from any thread. |
| 170 | +unsafe impl Send for Resource {} |
| 171 | + |
| 172 | +// SAFETY: `Resource` only holds a pointer to a C `struct resource`, references |
| 173 | +// to which are safe to be used from any thread. |
| 174 | +unsafe impl Sync for Resource {} |
| 175 | + |
| 176 | +/// Resource flags as stored in the C `struct resource::flags` field. |
| 177 | +/// |
| 178 | +/// They can be combined with the operators `|`, `&`, and `!`. |
| 179 | +/// |
| 180 | +/// Values can be used from the [`flags`] module. |
| 181 | +#[derive(Clone, Copy, PartialEq)] |
| 182 | +pub struct Flags(c_ulong); |
| 183 | + |
| 184 | +impl Flags { |
| 185 | + /// Check whether `flags` is contained in `self`. |
| 186 | + pub fn contains(self, flags: Flags) -> bool { |
| 187 | + (self & flags) == flags |
| 188 | + } |
| 189 | +} |
| 190 | + |
| 191 | +impl core::ops::BitOr for Flags { |
| 192 | + type Output = Self; |
| 193 | + fn bitor(self, rhs: Self) -> Self::Output { |
| 194 | + Self(self.0 | rhs.0) |
| 195 | + } |
| 196 | +} |
| 197 | + |
| 198 | +impl core::ops::BitAnd for Flags { |
| 199 | + type Output = Self; |
| 200 | + fn bitand(self, rhs: Self) -> Self::Output { |
| 201 | + Self(self.0 & rhs.0) |
| 202 | + } |
| 203 | +} |
| 204 | + |
| 205 | +impl core::ops::Not for Flags { |
| 206 | + type Output = Self; |
| 207 | + fn not(self) -> Self::Output { |
| 208 | + Self(!self.0) |
| 209 | + } |
| 210 | +} |
| 211 | + |
| 212 | +impl Flags { |
| 213 | + /// PCI/ISA I/O ports. |
| 214 | + pub const IORESOURCE_IO: Flags = Flags::new(bindings::IORESOURCE_IO); |
| 215 | + |
| 216 | + /// Resource is software muxed. |
| 217 | + pub const IORESOURCE_MUXED: Flags = Flags::new(bindings::IORESOURCE_MUXED); |
| 218 | + |
| 219 | + /// Resource represents a memory region. |
| 220 | + pub const IORESOURCE_MEM: Flags = Flags::new(bindings::IORESOURCE_MEM); |
| 221 | + |
| 222 | + /// Resource represents a memory region that must be ioremaped using `ioremap_np`. |
| 223 | + pub const IORESOURCE_MEM_NONPOSTED: Flags = Flags::new(bindings::IORESOURCE_MEM_NONPOSTED); |
| 224 | + |
| 225 | + const fn new(value: u32) -> Self { |
| 226 | + crate::build_assert!(value as u64 <= c_ulong::MAX as u64); |
| 227 | + Flags(value as c_ulong) |
| 228 | + } |
| 229 | +} |
0 commit comments