Skip to content

Commit 3ab99cc

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. Note: Although ARM64 support is not yet merged, this can be built on amd64 with CONFIG_COMPILE_TEST=y. FIXME: order in drivers/soc/apple/Kconfig to avoid merge conflicts in asahi tree Signed-off-by: Asahi Lina <lina@asahilina.net>
1 parent 39d9cd6 commit 3ab99cc

6 files changed

Lines changed: 297 additions & 0 deletions

File tree

drivers/soc/apple/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ config APPLE_SART
4141

4242
Say 'y' here if you have an Apple SoC.
4343

44+
config RUST_APPLE_RTKIT
45+
bool
46+
depends on RUST
47+
depends on APPLE_RTKIT
48+
4449
endmenu
4550

4651
endif

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
#include <linux/sched.h>
7272
#include <linux/security.h>
7373
#include <linux/slab.h>
74+
#include <linux/soc/apple/rtkit.h>
7475
#include <linux/tracepoint.h>
7576
#include <linux/wait.h>
7677
#include <linux/workqueue.h>

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ pub mod revocable;
132132
pub mod security;
133133
pub mod seq_file;
134134
pub mod sizes;
135+
pub mod soc;
135136
mod static_assert;
136137
#[doc(hidden)]
137138
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_APPLE_RTKIT = "y")]
6+
pub mod rtkit;

rust/kernel/soc/apple/rtkit.rs

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

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)