sel4/state/
mod.rs

1//
2// Copyright 2023, Colias Group, LLC
3//
4// SPDX-License-Identifier: MIT
5//
6
7use core::cell::UnsafeCell;
8
9use crate::{InvocationContext, IpcBuffer};
10
11mod token;
12
13#[allow(unused_imports)]
14use token::{Accessor, BorrowError, BorrowMutError, SyncToken, TokenCell, UnsyncToken};
15
16// // //
17
18#[repr(transparent)]
19struct SyncUnsafeCell<T>(UnsafeCell<T>);
20
21unsafe impl<T: Sync> Sync for SyncUnsafeCell<T> {}
22
23#[repr(transparent)]
24struct TokenCellWrapper<A>(TokenCell<TokenImpl, A>);
25
26cfg_if::cfg_if! {
27    if #[cfg(all(any(target_thread_local, feature = "tls"), not(feature = "non-thread-local-state")))] {
28        type TokenImpl = UnsyncToken;
29
30        const STATE_IS_THREAD_LOCAL: bool = true;
31
32        macro_rules! maybe_add_thread_local_attr {
33            { $item:item } => {
34                #[thread_local]
35                $item
36            }
37        }
38    } else if #[cfg(not(feature = "thread-local-state"))] {
39        cfg_if::cfg_if! {
40            if #[cfg(feature = "single-threaded")] {
41                unsafe impl<A> Sync for TokenCellWrapper<A> {}
42
43                type TokenImpl = UnsyncToken;
44            } else {
45                type TokenImpl = SyncToken;
46            }
47        }
48
49        const STATE_IS_THREAD_LOCAL: bool = false;
50
51        macro_rules! maybe_add_thread_local_attr {
52            { $item:item } => {
53                $item
54            }
55        }
56    } else {
57        compile_error!(r#"invalid configuration"#);
58    }
59}
60
61macro_rules! maybe_extern {
62    { $ident:ident: $ty:ty = $init:expr; } => {
63        cfg_if::cfg_if! {
64            if #[cfg(feature = "extern-state")] {
65                unsafe extern "C" {
66                    maybe_add_thread_local_attr! {
67                        static $ident: $ty;
68                    }
69                }
70            } else {
71                maybe_add_thread_local_attr! {
72                    #[allow(non_upper_case_globals)]
73                    #[cfg_attr(feature = "exposed-state", no_mangle)]
74                    static $ident: $ty = $init;
75                }
76            }
77        }
78    }
79}
80
81// // //
82
83maybe_extern! {
84    __sel4_ipc_buffer: SyncUnsafeCell<Option<&'static mut IpcBuffer>> =
85        SyncUnsafeCell(UnsafeCell::new(None));
86}
87
88struct IpcBufferAccessor;
89
90impl Accessor<Option<&'static mut IpcBuffer>> for IpcBufferAccessor {
91    #[allow(unused_unsafe)]
92    fn with<F, U>(&self, f: F) -> U
93    where
94        F: FnOnce(&UnsafeCell<Option<&'static mut IpcBuffer>>) -> U,
95    {
96        f(unsafe { &__sel4_ipc_buffer.0 })
97    }
98}
99
100maybe_add_thread_local_attr! {
101    static IPC_BUFFER: TokenCellWrapper<IpcBufferAccessor> = unsafe {
102        TokenCellWrapper(TokenCell::new(IpcBufferAccessor))
103    };
104}
105
106/// Provides low-level access to this thread's IPC buffer.
107///
108/// This function does not modify kernel state. It only affects this crate's thread-local state.
109///
110/// Requires the `"state"` feature to be enabled.
111pub fn try_with_ipc_buffer_slot<F, T>(f: F) -> T
112where
113    F: FnOnce(Result<&Option<&'static mut IpcBuffer>, BorrowError>) -> T,
114{
115    IPC_BUFFER.0.try_with(f)
116}
117
118/// Provides low-level mutable access to this thread's IPC buffer.
119///
120/// This function does not modify kernel state. It only affects this crate's thread-local state.
121///
122/// Requires the `"state"` feature to be enabled.
123pub fn try_with_ipc_buffer_slot_mut<F, T>(f: F) -> T
124where
125    F: FnOnce(Result<&mut Option<&'static mut IpcBuffer>, BorrowMutError>) -> T,
126{
127    IPC_BUFFER.0.try_with_mut(f)
128}
129
130/// Provides access to this thread's IPC buffer.
131///
132/// Requires the `"state"` feature to be enabled.
133pub fn with_ipc_buffer<F, T>(f: F) -> T
134where
135    F: FnOnce(&IpcBuffer) -> T,
136{
137    try_with_ipc_buffer_slot(|buf| {
138        f(buf
139            .expect("IPC buffer already mutably borrowed")
140            .as_ref()
141            .expect("IPC buffer not set"))
142    })
143}
144
145/// Provides mutable access to this thread's IPC buffer.
146///
147/// Requires the `"state"` feature to be enabled.
148pub fn with_ipc_buffer_mut<F, T>(f: F) -> T
149where
150    F: FnOnce(&mut IpcBuffer) -> T,
151{
152    try_with_ipc_buffer_slot_mut(|buf| {
153        f(buf
154            .expect("IPC buffer already borrowed")
155            .as_mut()
156            .expect("IPC buffer not set"))
157    })
158}
159
160/// Sets the IPC buffer that this crate will use for this thread.
161///
162/// This function does not modify kernel state. It only affects this crate's thread-local state.
163///
164/// Requires the `"state"` feature to be enabled.
165pub fn set_ipc_buffer(ipc_buffer: &'static mut IpcBuffer) {
166    try_with_ipc_buffer_slot_mut(|slot| {
167        *slot.unwrap() = Some(ipc_buffer);
168    })
169}
170
171/// Returns whether this crate's IPC buffer slot is thread-local.
172///
173/// Requires the `"state"` feature to be enabled.
174pub const fn ipc_buffer_is_thread_local() -> bool {
175    STATE_IS_THREAD_LOCAL
176}
177
178/// The strategy for discovering the current thread's IPC buffer which uses thread-local state.
179///
180/// This thread-local state can be modified using [`with_ipc_buffer`] and [`set_ipc_buffer`].
181///
182/// Requires the `"state"` feature to be enabled.
183#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
184pub struct ImplicitInvocationContext;
185
186impl ImplicitInvocationContext {
187    pub const fn new() -> Self {
188        Self
189    }
190}
191
192impl InvocationContext for ImplicitInvocationContext {
193    fn with_context<T>(&mut self, f: impl FnOnce(&mut IpcBuffer) -> T) -> T {
194        with_ipc_buffer_mut(f)
195    }
196}