Skip to content

Commit 52c1011

Browse files
hoshinolinajannau
authored andcommitted
rust: soc: apple: rtkit: Add Apple RTKit abstraction
RTKit is Apple's proprietary real-time operating system framework, used across many subdevices on Apple Silicon platforms including NVMe, system management, GPU, etc. Add Rust abstractions for this subsystem, so that it can be used by upcoming Rust drivers. FIXME: order in drivers/soc/apple/Kconfig to avoid merge conflicts in asahi tree Signed-off-by: Asahi Lina <lina@asahilina.net>
1 parent 2245a81 commit 52c1011

6 files changed

Lines changed: 307 additions & 0 deletions

File tree

drivers/soc/apple/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ config APPLE_SART
3838

3939
Say 'y' here if you have an Apple SoC.
4040

41+
config RUST_APPLE_RTKIT
42+
bool
43+
depends on PM
44+
depends on RUST
45+
select APPLE_RTKIT
46+
4147
endmenu
4248

4349
endif

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
#include <linux/sched.h>
8181
#include <linux/security.h>
8282
#include <linux/slab.h>
83+
#include <linux/soc/apple/rtkit.h>
8384
#include <linux/task_work.h>
8485
#include <linux/tracepoint.h>
8586
#include <linux/usb.h>

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ pub mod security;
155155
pub mod seq_file;
156156
pub mod sizes;
157157
pub mod slice;
158+
pub mod soc;
158159
mod static_assert;
159160
#[doc(hidden)]
160161
pub mod std_vendor;

rust/kernel/soc/apple/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
3+
//! Apple SoC drivers
4+
5+
#[cfg(CONFIG_RUST_APPLE_RTKIT = "y")]
6+
pub mod rtkit;

rust/kernel/soc/apple/rtkit.rs

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
// SPDX-License-Identifier: GPL-2.0-only OR MIT
2+
3+
//! Support for Apple RTKit coprocessors.
4+
//!
5+
//! C header: [`include/linux/soc/apple/rtkit.h`](../../../../include/linux/gpio/driver.h)
6+
7+
use crate::{
8+
alloc::flags::*,
9+
bindings,
10+
device,
11+
error::{
12+
from_err_ptr,
13+
from_result,
14+
to_result, //
15+
},
16+
iosys_map::IoSysMapRef,
17+
prelude::*,
18+
str::CStrExt,
19+
types::{
20+
ForeignOwnable,
21+
ScopeGuard, //
22+
}, //
23+
};
24+
25+
use core::marker::PhantomData;
26+
use core::ptr;
27+
use macros::vtable;
28+
29+
/// Trait to represent allocatable buffers for the RTKit core.
30+
///
31+
/// Users must implement this trait for their own representation of those allocations.
32+
pub trait Buffer {
33+
/// Returns the IOVA (virtual address) of the buffer from RTKit's point of view, or an error if
34+
/// unavailable.
35+
fn iova(&self) -> Result<usize>;
36+
37+
/// Returns a mutable byte slice of the buffer contents, or an
38+
/// error if unavailable.
39+
fn buf(&mut self) -> Result<IoSysMapRef<'_, u8>>;
40+
}
41+
42+
/// Callback operations for an RTKit client.
43+
#[vtable]
44+
pub trait Operations {
45+
/// Arbitrary user context type.
46+
type Data: ForeignOwnable + Send + Sync;
47+
48+
/// Type representing an allocated buffer for RTKit.
49+
type Buffer: Buffer;
50+
51+
/// Called when RTKit crashes.
52+
fn crashed(_data: <Self::Data as ForeignOwnable>::Borrowed<'_>, _crashlog: Option<&[u8]>) {}
53+
54+
/// Called when a message was received on a non-system endpoint. Called in non-IRQ context.
55+
fn recv_message(
56+
_data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
57+
_endpoint: u8,
58+
_message: u64,
59+
) {
60+
}
61+
62+
/// Called in IRQ context when a message was received on a non-system endpoint.
63+
///
64+
/// Must return `true` if the message is handled, or `false` to process it in
65+
/// the handling thread.
66+
fn recv_message_early(
67+
_data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
68+
_endpoint: u8,
69+
_message: u64,
70+
) -> bool {
71+
false
72+
}
73+
74+
/// Allocate a buffer for use by RTKit.
75+
fn shmem_alloc(
76+
_data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
77+
_size: usize,
78+
) -> Result<Self::Buffer> {
79+
Err(EINVAL)
80+
}
81+
82+
/// Map an existing buffer used by RTKit at a device-specified virtual address.
83+
fn shmem_map(
84+
_data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
85+
_iova: usize,
86+
_size: usize,
87+
) -> Result<Self::Buffer> {
88+
Err(EINVAL)
89+
}
90+
}
91+
92+
/// Represents `struct apple_rtkit *`.
93+
///
94+
/// # Invariants
95+
///
96+
/// The rtk pointer is valid.
97+
/// The data pointer is a valid pointer from T::Data::into_foreign().
98+
pub struct RtKit<T: Operations> {
99+
rtk: *mut bindings::apple_rtkit,
100+
data: *mut core::ffi::c_void,
101+
_p: PhantomData<T>,
102+
}
103+
104+
unsafe extern "C" fn crashed_callback<T: Operations>(
105+
cookie: *mut core::ffi::c_void,
106+
crashlog: *const core::ffi::c_void,
107+
crashlog_size: usize,
108+
) {
109+
let crashlog = if !crashlog.is_null() && crashlog_size > 0 {
110+
// SAFETY: The crashlog is either missing or a byte buffer of the specified size
111+
Some(unsafe { core::slice::from_raw_parts(crashlog as *const u8, crashlog_size) })
112+
} else {
113+
None
114+
};
115+
// SAFETY: cookie is always a T::Data in this API
116+
T::crashed(unsafe { T::Data::borrow(cookie.cast()) }, crashlog);
117+
}
118+
119+
unsafe extern "C" fn recv_message_callback<T: Operations>(
120+
cookie: *mut core::ffi::c_void,
121+
endpoint: u8,
122+
message: u64,
123+
) {
124+
// SAFETY: cookie is always a T::Data in this API
125+
T::recv_message(unsafe { T::Data::borrow(cookie.cast()) }, endpoint, message);
126+
}
127+
128+
unsafe extern "C" fn recv_message_early_callback<T: Operations>(
129+
cookie: *mut core::ffi::c_void,
130+
endpoint: u8,
131+
message: u64,
132+
) -> bool {
133+
// SAFETY: cookie is always a T::Data in this API
134+
T::recv_message_early(unsafe { T::Data::borrow(cookie.cast()) }, endpoint, message)
135+
}
136+
137+
unsafe extern "C" fn shmem_setup_callback<T: Operations>(
138+
cookie: *mut core::ffi::c_void,
139+
bfr: *mut bindings::apple_rtkit_shmem,
140+
) -> core::ffi::c_int {
141+
// SAFETY: `bfr` is a valid buffer
142+
let bfr_mut = unsafe { &mut *bfr };
143+
144+
from_result(|| {
145+
let mut buf = if bfr_mut.iova != 0 {
146+
bfr_mut.is_mapped = true;
147+
T::shmem_map(
148+
// SAFETY: `cookie` came from a previous call to `into_foreign`.
149+
unsafe { T::Data::borrow(cookie.cast()) },
150+
bfr_mut.iova as usize,
151+
bfr_mut.size,
152+
)?
153+
} else {
154+
bfr_mut.is_mapped = false;
155+
// SAFETY: `cookie` came from a previous call to `into_foreign`.
156+
T::shmem_alloc(unsafe { T::Data::borrow(cookie.cast()) }, bfr_mut.size)?
157+
};
158+
159+
let iova = buf.iova()?;
160+
let iosys_map = buf.buf()?;
161+
162+
if iosys_map.size() < bfr_mut.size {
163+
return Err(ENOMEM);
164+
}
165+
166+
bfr_mut.iova = iova as u64;
167+
bfr_mut.buffer = iosys_map.as_mut_ptr() as *mut _;
168+
169+
// Now box the returned buffer type and stash it in the private pointer of the
170+
// `apple_rtkit_shmem` struct for safekeeping.
171+
let boxed = KBox::new(buf, GFP_KERNEL)?;
172+
bfr_mut.private = KBox::into_raw(boxed) as *mut _;
173+
Ok(0)
174+
})
175+
}
176+
177+
unsafe extern "C" fn shmem_destroy_callback<T: Operations>(
178+
_cookie: *mut core::ffi::c_void,
179+
bfr: *mut bindings::apple_rtkit_shmem,
180+
) {
181+
// SAFETY: `bfr` is a valid buffer
182+
let bfr_mut = unsafe { &mut *bfr };
183+
if !bfr_mut.private.is_null() {
184+
// SAFETY: Per shmem_setup_callback, this has to be a pointer to a Buffer if it is set.
185+
unsafe {
186+
core::mem::drop(KBox::from_raw(bfr_mut.private as *mut T::Buffer));
187+
}
188+
bfr_mut.private = core::ptr::null_mut();
189+
}
190+
}
191+
192+
impl<T: Operations> RtKit<T> {
193+
const VTABLE: bindings::apple_rtkit_ops = bindings::apple_rtkit_ops {
194+
crashed: Some(crashed_callback::<T>),
195+
recv_message: Some(recv_message_callback::<T>),
196+
recv_message_early: Some(recv_message_early_callback::<T>),
197+
shmem_setup: if T::HAS_SHMEM_ALLOC || T::HAS_SHMEM_MAP {
198+
Some(shmem_setup_callback::<T>)
199+
} else {
200+
None
201+
},
202+
shmem_destroy: if T::HAS_SHMEM_ALLOC || T::HAS_SHMEM_MAP {
203+
Some(shmem_destroy_callback::<T>)
204+
} else {
205+
None
206+
},
207+
};
208+
209+
/// Creates a new RTKit client for a given device and optional mailbox name or index.
210+
pub fn new(
211+
dev: &device::Device,
212+
mbox_name: Option<&'static CStr>,
213+
mbox_idx: usize,
214+
data: T::Data,
215+
) -> Result<Self> {
216+
let ptr: *mut crate::ffi::c_void = data.into_foreign().cast();
217+
let guard = ScopeGuard::new(|| {
218+
// SAFETY: `ptr` came from a previous call to `into_foreign`.
219+
unsafe { T::Data::from_foreign(ptr.cast()) };
220+
});
221+
// SAFETY: `dev` is valid by its type invarants and otherwise his just
222+
// calls the C init function.
223+
let rtk = unsafe {
224+
from_err_ptr(bindings::apple_rtkit_init(
225+
dev.as_raw(),
226+
ptr,
227+
match mbox_name {
228+
Some(s) => s.as_char_ptr(),
229+
None => ptr::null(),
230+
},
231+
mbox_idx.try_into()?,
232+
&Self::VTABLE,
233+
))
234+
}?;
235+
236+
guard.dismiss();
237+
// INVARIANT: `rtk` and `data` are valid here.
238+
Ok(Self {
239+
rtk,
240+
data: ptr,
241+
_p: PhantomData,
242+
})
243+
}
244+
245+
/// Boots (wakes up) the RTKit coprocessor.
246+
pub fn wake(self: Pin<&mut Self>) -> Result {
247+
// SAFETY: `rtk` is valid per the type invariant.
248+
to_result(unsafe { bindings::apple_rtkit_wake(self.rtk) })
249+
}
250+
251+
/// Waits for the RTKit coprocessor to finish booting.
252+
pub fn boot(self: Pin<&mut Self>) -> Result {
253+
// SAFETY: `rtk` is valid per the type invariant.
254+
to_result(unsafe { bindings::apple_rtkit_boot(self.rtk) })
255+
}
256+
257+
/// Starts a non-system endpoint.
258+
pub fn start_endpoint(self: Pin<&mut Self>, endpoint: u8) -> Result {
259+
// SAFETY: `rtk` is valid per the type invariant.
260+
to_result(unsafe { bindings::apple_rtkit_start_ep(self.rtk, endpoint) })
261+
}
262+
263+
/// Sends a message to a given endpoint.
264+
pub fn send_message(self: Pin<&mut Self>, endpoint: u8, message: u64) -> Result {
265+
// SAFETY: `rtk` is valid per the type invariant.
266+
to_result(unsafe {
267+
bindings::apple_rtkit_send_message(self.rtk, endpoint, message, ptr::null_mut(), false)
268+
})
269+
}
270+
}
271+
272+
// SAFETY: `RtKit` operations require a mutable reference
273+
unsafe impl<T: Operations> Sync for RtKit<T> {}
274+
275+
// SAFETY: `RtKit` operations require a mutable reference
276+
unsafe impl<T: Operations> Send for RtKit<T> {}
277+
278+
impl<T: Operations> Drop for RtKit<T> {
279+
fn drop(&mut self) {
280+
// SAFETY: The pointer is valid by the type invariant.
281+
unsafe { bindings::apple_rtkit_free(self.rtk) };
282+
283+
// Free context data.
284+
//
285+
// SAFETY: This matches the call to `into_foreign` from `new` in the success case.
286+
unsafe { T::Data::from_foreign(self.data.cast()) };
287+
}
288+
}

rust/kernel/soc/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! SoC drivers
4+
5+
pub mod apple;

0 commit comments

Comments
 (0)