Skip to content

Commit 493fc33

Browse files
danielalmeida-collaboraDanilo Krummrich
authored andcommitted
rust: io: add resource abstraction
In preparation for ioremap support, add a Rust abstraction for struct resource. A future commit will introduce the Rust API to ioremap a resource from a platform device. The current abstraction, therefore, adds only the minimum API needed to get that done. Acked-by: Miguel Ojeda <ojeda@kernel.org> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Co-developed-by: Fiona Behrens <me@kloenk.dev> Signed-off-by: Fiona Behrens <me@kloenk.dev> Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com> Link: https://lore.kernel.org/r/20250717-topics-tyr-platform_iomem-v15-1-beca780b77e3@collabora.com [ Capitalize safety comments and end it with a period. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
1 parent 931d925 commit 493fc33

4 files changed

Lines changed: 270 additions & 0 deletions

File tree

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include <linux/file.h>
5454
#include <linux/firmware.h>
5555
#include <linux/fs.h>
56+
#include <linux/ioport.h>
5657
#include <linux/jiffies.h>
5758
#include <linux/jump_label.h>
5859
#include <linux/mdio.h>

rust/helpers/io.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: GPL-2.0
22

33
#include <linux/io.h>
4+
#include <linux/ioport.h>
45

56
void __iomem *rust_helper_ioremap(phys_addr_t offset, size_t size)
67
{
@@ -99,3 +100,38 @@ void rust_helper_writeq_relaxed(u64 value, void __iomem *addr)
99100
writeq_relaxed(value, addr);
100101
}
101102
#endif
103+
104+
resource_size_t rust_helper_resource_size(struct resource *res)
105+
{
106+
return resource_size(res);
107+
}
108+
109+
struct resource *rust_helper_request_mem_region(resource_size_t start,
110+
resource_size_t n,
111+
const char *name)
112+
{
113+
return request_mem_region(start, n, name);
114+
}
115+
116+
void rust_helper_release_mem_region(resource_size_t start, resource_size_t n)
117+
{
118+
release_mem_region(start, n);
119+
}
120+
121+
struct resource *rust_helper_request_region(resource_size_t start,
122+
resource_size_t n, const char *name)
123+
{
124+
return request_region(start, n, name);
125+
}
126+
127+
struct resource *rust_helper_request_muxed_region(resource_size_t start,
128+
resource_size_t n,
129+
const char *name)
130+
{
131+
return request_muxed_region(start, n, name);
132+
}
133+
134+
void rust_helper_release_region(resource_size_t start, resource_size_t n)
135+
{
136+
release_region(start, n);
137+
}

rust/kernel/io.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
use crate::error::{code::EINVAL, Result};
88
use crate::{bindings, build_assert};
99

10+
pub mod resource;
11+
12+
pub use resource::Resource;
13+
1014
/// Raw representation of an MMIO region.
1115
///
1216
/// By itself, the existence of an instance of this structure does not provide any guarantees that

rust/kernel/io/resource.rs

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
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

Comments
 (0)