Skip to content

Commit 0e0dd2f

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 7a29522 commit 0e0dd2f

6 files changed

Lines changed: 280 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
@@ -32,6 +32,7 @@
3232
#include <linux/siphash.h>
3333
#include <linux/sched.h>
3434
#include <linux/slab.h>
35+
#include <linux/soc/apple/rtkit.h>
3536
#include <linux/timekeeping.h>
3637
#include <linux/wait.h>
3738
#include <linux/workqueue.h>

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub mod rbtree;
6767
pub mod revocable;
6868
pub mod siphash;
6969
pub mod sizes;
70+
pub mod soc;
7071
mod static_assert;
7172
#[doc(hidden)]
7273
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: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
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::{box_ext::BoxExt, flags::*},
9+
bindings, device,
10+
error::{code::*, from_err_ptr, from_result, to_result, Result},
11+
str::CStr,
12+
types::{ForeignOwnable, ScopeGuard},
13+
};
14+
15+
use alloc::boxed::Box;
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<'_>) {}
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>(cookie: *mut core::ffi::c_void) {
96+
// SAFETY: cookie is always a T::Data in this API
97+
T::crashed(unsafe { T::Data::borrow(cookie) });
98+
}
99+
100+
unsafe extern "C" fn recv_message_callback<T: Operations>(
101+
cookie: *mut core::ffi::c_void,
102+
endpoint: u8,
103+
message: u64,
104+
) {
105+
// SAFETY: cookie is always a T::Data in this API
106+
T::recv_message(unsafe { T::Data::borrow(cookie) }, endpoint, message);
107+
}
108+
109+
unsafe extern "C" fn recv_message_early_callback<T: Operations>(
110+
cookie: *mut core::ffi::c_void,
111+
endpoint: u8,
112+
message: u64,
113+
) -> bool {
114+
// SAFETY: cookie is always a T::Data in this API
115+
T::recv_message_early(unsafe { T::Data::borrow(cookie) }, endpoint, message)
116+
}
117+
118+
unsafe extern "C" fn shmem_setup_callback<T: Operations>(
119+
cookie: *mut core::ffi::c_void,
120+
bfr: *mut bindings::apple_rtkit_shmem,
121+
) -> core::ffi::c_int {
122+
// SAFETY: `bfr` is a valid buffer
123+
let bfr_mut = unsafe { &mut *bfr };
124+
125+
from_result(|| {
126+
let mut buf = if bfr_mut.iova != 0 {
127+
bfr_mut.is_mapped = true;
128+
T::shmem_map(
129+
// SAFETY: `cookie` came from a previous call to `into_foreign`.
130+
unsafe { T::Data::borrow(cookie) },
131+
bfr_mut.iova as usize,
132+
bfr_mut.size,
133+
)?
134+
} else {
135+
bfr_mut.is_mapped = false;
136+
// SAFETY: `cookie` came from a previous call to `into_foreign`.
137+
T::shmem_alloc(unsafe { T::Data::borrow(cookie) }, bfr_mut.size)?
138+
};
139+
140+
let iova = buf.iova()?;
141+
let slice = buf.buf()?;
142+
143+
if slice.len() < bfr_mut.size {
144+
return Err(ENOMEM);
145+
}
146+
147+
bfr_mut.iova = iova as u64;
148+
bfr_mut.buffer = slice.as_mut_ptr() as *mut _;
149+
150+
// Now box the returned buffer type and stash it in the private pointer of the
151+
// `apple_rtkit_shmem` struct for safekeeping.
152+
let boxed = Box::new(buf, GFP_KERNEL)?;
153+
bfr_mut.private = Box::into_raw(boxed) as *mut _;
154+
Ok(0)
155+
})
156+
}
157+
158+
unsafe extern "C" fn shmem_destroy_callback<T: Operations>(
159+
_cookie: *mut core::ffi::c_void,
160+
bfr: *mut bindings::apple_rtkit_shmem,
161+
) {
162+
// SAFETY: `bfr` is a valid buffer
163+
let bfr_mut = unsafe { &mut *bfr };
164+
if !bfr_mut.private.is_null() {
165+
// SAFETY: Per shmem_setup_callback, this has to be a pointer to a Buffer if it is set.
166+
unsafe {
167+
core::mem::drop(Box::from_raw(bfr_mut.private as *mut T::Buffer));
168+
}
169+
bfr_mut.private = core::ptr::null_mut();
170+
}
171+
}
172+
173+
impl<T: Operations> RtKit<T> {
174+
const VTABLE: bindings::apple_rtkit_ops = bindings::apple_rtkit_ops {
175+
crashed: Some(crashed_callback::<T>),
176+
recv_message: Some(recv_message_callback::<T>),
177+
recv_message_early: Some(recv_message_early_callback::<T>),
178+
shmem_setup: if T::HAS_SHMEM_ALLOC || T::HAS_SHMEM_MAP {
179+
Some(shmem_setup_callback::<T>)
180+
} else {
181+
None
182+
},
183+
shmem_destroy: if T::HAS_SHMEM_ALLOC || T::HAS_SHMEM_MAP {
184+
Some(shmem_destroy_callback::<T>)
185+
} else {
186+
None
187+
},
188+
};
189+
190+
/// Creates a new RTKit client for a given device and optional mailbox name or index.
191+
pub fn new(
192+
dev: &dyn device::RawDevice,
193+
mbox_name: Option<&'static CStr>,
194+
mbox_idx: usize,
195+
data: T::Data,
196+
) -> Result<Self> {
197+
let ptr = data.into_foreign() as *mut _;
198+
let guard = ScopeGuard::new(|| {
199+
// SAFETY: `ptr` came from a previous call to `into_foreign`.
200+
unsafe { T::Data::from_foreign(ptr) };
201+
});
202+
// SAFETY: This just calls the C init function.
203+
let rtk = unsafe {
204+
from_err_ptr(bindings::apple_rtkit_init(
205+
dev.raw_device(),
206+
ptr,
207+
match mbox_name {
208+
Some(s) => s.as_char_ptr(),
209+
None => ptr::null(),
210+
},
211+
mbox_idx.try_into()?,
212+
&Self::VTABLE,
213+
))
214+
}?;
215+
216+
guard.dismiss();
217+
// INVARIANT: `rtk` and `data` are valid here.
218+
Ok(Self {
219+
rtk,
220+
data: ptr,
221+
_p: PhantomData,
222+
})
223+
}
224+
225+
/// Boots (wakes up) the RTKit coprocessor.
226+
pub fn boot(&mut self) -> Result {
227+
// SAFETY: `rtk` is valid per the type invariant.
228+
to_result(unsafe { bindings::apple_rtkit_boot(self.rtk) })
229+
}
230+
231+
/// Starts a non-system endpoint.
232+
pub fn start_endpoint(&mut self, endpoint: u8) -> Result {
233+
// SAFETY: `rtk` is valid per the type invariant.
234+
to_result(unsafe { bindings::apple_rtkit_start_ep(self.rtk, endpoint) })
235+
}
236+
237+
/// Sends a message to a given endpoint.
238+
pub fn send_message(&mut self, endpoint: u8, message: u64) -> Result {
239+
// SAFETY: `rtk` is valid per the type invariant.
240+
to_result(unsafe {
241+
bindings::apple_rtkit_send_message(self.rtk, endpoint, message, ptr::null_mut(), false)
242+
})
243+
}
244+
}
245+
246+
// SAFETY: `RtKit` operations require a mutable reference
247+
unsafe impl<T: Operations> Sync for RtKit<T> {}
248+
249+
// SAFETY: `RtKit` operations require a mutable reference
250+
unsafe impl<T: Operations> Send for RtKit<T> {}
251+
252+
impl<T: Operations> Drop for RtKit<T> {
253+
fn drop(&mut self) {
254+
// SAFETY: The pointer is valid by the type invariant.
255+
unsafe { bindings::apple_rtkit_free(self.rtk) };
256+
257+
// Free context data.
258+
//
259+
// SAFETY: This matches the call to `into_foreign` from `new` in the success case.
260+
unsafe { T::Data::from_foreign(self.data) };
261+
}
262+
}

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)