Skip to content

Commit 38a7a16

Browse files
committed
kernel: fix and make robust the ReentrantLock kernel ID fetch function
1 parent 7259df0 commit 38a7a16

File tree

2 files changed

+94
-20
lines changed

2 files changed

+94
-20
lines changed

oro-kernel/src/lib.rs

+11-20
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub mod module;
2121
pub mod port;
2222
pub mod ring;
2323
pub mod scheduler;
24+
pub mod sync;
2425
pub mod thread;
2526

2627
use core::{
@@ -74,21 +75,6 @@ pub struct Kernel<A: Arch> {
7475
handle: A::CoreHandle,
7576
}
7677

77-
// NOTE(qix-): This is an ergonomics hack to avoid `A: Arch` in a lot of places.
78-
#[doc(hidden)]
79-
static mut KERNEL_ID_FN: MaybeUninit<fn() -> u32> = MaybeUninit::uninit();
80-
81-
#[doc(hidden)]
82-
#[no_mangle]
83-
unsafe extern "C" fn oro_sync_current_core_id() -> u32 {
84-
KERNEL_ID_FN.assume_init()()
85-
}
86-
87-
#[doc(hidden)]
88-
fn get_arch_kernel_id<A: Arch>() -> u32 {
89-
Kernel::<A>::get().id()
90-
}
91-
9278
impl<A: Arch> Kernel<A> {
9379
/// Initializes a new core-local instance of the Oro kernel.
9480
///
@@ -116,11 +102,6 @@ impl<A: Arch> Kernel<A> {
116102
) -> Result<&'static Self, MapError> {
117103
assert::fits::<Self, 4096>();
118104

119-
#[expect(static_mut_refs)]
120-
{
121-
KERNEL_ID_FN.write(get_arch_kernel_id::<A>);
122-
}
123-
124105
let mapper = AddressSpace::<A>::current_supervisor_space();
125106
let core_local_segment = AddressSpace::<A>::kernel_core_local();
126107

@@ -141,6 +122,13 @@ impl<A: Arch> Kernel<A> {
141122
mapper,
142123
});
143124

125+
// SAFETY(qix-): Now that the kernel has been mapped in, we can initialize the _real_
126+
// SAFETY(qix-): core-local ID function. This is effectively a no-op for secondary
127+
// SAFETY(qix-): cores, but doing it here ensures that any stray usage of `ReentrantLock`s
128+
// SAFETY(qix-): at least see _some_ core ID. This isn't ideal, it's a bit of a hack, but
129+
// SAFETY(qix-): it's a one-off situation that isn't trivial to avoid.
130+
sync::initialize_kernel_id_fn::<A>();
131+
144132
(*kernel_ptr)
145133
.scheduler
146134
.write(TicketMutex::new(Scheduler::new(&*kernel_ptr)));
@@ -242,6 +230,9 @@ impl<A: Arch> KernelState<A> {
242230
/// or else registry accesses will page fault.
243231
#[allow(clippy::missing_panics_doc)]
244232
pub unsafe fn init(this: &'static mut MaybeUninit<Self>) -> Result<(), MapError> {
233+
// SAFETY(qix-): Must be first, before anything else happens in the kernel.
234+
self::sync::install_dummy_kernel_id_fn();
235+
245236
let root_ring = ring::Ring::<A>::new_root()?;
246237
let root_ring_weak = Arc::downgrade(&root_ring);
247238

oro-kernel/src/sync.rs

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//! `oro-sync` backing functionality.
2+
//!
3+
//! These are mostly just implementations to allow certain advanced features of
4+
//! the `oro-sync` crate work (e.g. `ReentrantMutex`).
5+
6+
use core::mem::MaybeUninit;
7+
8+
use crate::arch::Arch;
9+
10+
/// Holds the "global" kernel ID function pointer, which retrieves the **core local**
11+
/// kernel ID for the calling context's current core.
12+
///
13+
/// This is an ergonomics hack to avoid `A: Arch` in a lot of places.
14+
static mut KERNEL_ID_FN: MaybeUninit<fn() -> u32> = MaybeUninit::uninit();
15+
16+
/// Debug field for ensuring the kernel ID function is set.
17+
#[cfg(debug_assertions)]
18+
static HAS_SET_KERNEL_ID_FN: core::sync::atomic::AtomicBool =
19+
core::sync::atomic::AtomicBool::new(false);
20+
21+
/// Retrieves the current core's kernel ID. This is linked to by `oro-sync` for the
22+
/// [`oro_sync::ReentrantLock`] implementation.
23+
#[doc(hidden)]
24+
#[no_mangle]
25+
unsafe extern "C" fn oro_sync_current_core_id() -> u32 {
26+
#[cfg(debug_assertions)]
27+
{
28+
assert!(
29+
HAS_SET_KERNEL_ID_FN.load(core::sync::atomic::Ordering::Relaxed),
30+
"kernel ID function not set"
31+
);
32+
}
33+
34+
KERNEL_ID_FN.assume_init()()
35+
}
36+
37+
/// The generic kernel ID fetcher, based on the [`Arch`] type.
38+
#[doc(hidden)]
39+
fn get_arch_kernel_id<A: Arch>() -> u32 {
40+
crate::Kernel::<A>::get().id()
41+
}
42+
43+
/// Initializes the kernel ID function pointer.
44+
///
45+
/// # Safety
46+
/// This must be called **exactly once** prior to any [`oro_sync::ReentrantMutex`] locking.
47+
///
48+
/// Further, `A` **must** be the same [`Arch`] type as the kernel being initialized,
49+
/// and **must** be the same across **all cores**.
50+
pub unsafe fn initialize_kernel_id_fn<A: Arch>() {
51+
#[cfg(debug_assertions)]
52+
{
53+
HAS_SET_KERNEL_ID_FN.store(true, core::sync::atomic::Ordering::Relaxed);
54+
}
55+
56+
// SAFETY(qix-): We have offloaded safety considerations to the caller here.
57+
#[expect(static_mut_refs)]
58+
{
59+
KERNEL_ID_FN.write(get_arch_kernel_id::<A>);
60+
}
61+
}
62+
63+
/// Installs a dummy kernel ID for use during early boot.
64+
///
65+
/// # Safety
66+
/// This function *may* be called prior to [`initialize_kernel_id_fn`], but
67+
/// **must** be called prior to any locking with [`oro_sync::ReentrantMutex`]
68+
/// if it is.
69+
///
70+
/// **The handler installed by this function must not be used once multiple
71+
/// cores are active.**
72+
pub unsafe fn install_dummy_kernel_id_fn() {
73+
#[cfg(debug_assertions)]
74+
{
75+
HAS_SET_KERNEL_ID_FN.store(true, core::sync::atomic::Ordering::Relaxed);
76+
}
77+
78+
// SAFETY(qix-): We have offloaded safety considerations to the caller here.
79+
#[expect(static_mut_refs)]
80+
{
81+
KERNEL_ID_FN.write(|| 0);
82+
}
83+
}

0 commit comments

Comments
 (0)