Skip to content

Commit 8209f16

Browse files
authored
Support CPU-local variables with user-defined types (#1024)
* The `cpu_local` macro now generates four functions for user-defined types: ```rust pub fn replace(&self, value: #ty) -> #ty; pub fn replace_guarded<G>(&self, mut value: #ty, guard: &G) -> #ty where G: CpuAtomicGuard pub fn set(&self, value: #ty); pub fn set_guarded<G>(&self, mut value: #ty, guard: &G) where G: CpuAtomicGuard ``` * The new `CpuAtomicGuard` trait is sealed, but implemented for `preemption::PreemptionGuard` and `irq_safety::HeldInterrupts`. Passing a reference to one of those guard types into either `set_guarded()` or `replace_guarded()` can efficiently ensure that either preemption or interrupts are disabled, meaning that the CPU-local variable can be safely accessed in an atomic manner across multiple assembly instructions. * However, this doesn't cover all use cases, so we also add two optional arguments to the `cpu_local` macro: 1. `cls_dep`: a boolean that defaults to true, but can be set to false to prevent the macro from generating functions that depend on `cls`. This is only used by the `preemption` crate to avoid a circular dependency. 2. `stores_guard`: accepts a type denoting that the CLS variable stores an `Option<impl cls::CpuAtomicGuard>`, such as the task switch preemption guard. This argument modifies the signatures of `replace` and `set` to accept the guard type by value, because the guard type obviates the need to pass in a reference to another redundant guard (normally used to ensure atomicity). Signed-off-by: Klimenty Tsoutsman <[email protected]>
1 parent 8a1e561 commit 8209f16

File tree

12 files changed

+391
-207
lines changed

12 files changed

+391
-207
lines changed

Cargo.lock

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

kernel/cls/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,12 @@ edition = "2021"
77

88
[dependencies]
99
cls_macros = { path = "cls_macros" }
10+
irq_safety = { git = "https://github.com/theseus-os/irq_safety" }
11+
preemption = { path = "../preemption" }
12+
13+
[target.'cfg(target_arch = "x86_64")'.dependencies]
14+
x86_64 = "0.14.8"
15+
16+
[target.'cfg(target_arch = "aarch64")'.dependencies]
17+
tock-registers = "0.7.0"
18+
cortex-a = "7.5.0"

kernel/cls/cls_macros/src/int.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use proc_macro2::TokenStream;
2+
use quote::{quote, ToTokens};
3+
use syn::{LitInt, Type};
4+
5+
pub(crate) fn int_functions(ty: Type, offset: LitInt) -> Option<TokenStream> {
6+
let ((x64_asm_width, x64_reg_class), (aarch64_reg_modifier, aarch64_instr_width)) =
7+
match ty.to_token_stream().to_string().as_ref() {
8+
"u8" => (("byte", quote! { reg_byte }), (":w", "b")),
9+
"u16" => (("word", quote! { reg }), (":w", "w")),
10+
"u32" => (("dword", quote! { reg }), (":w", "")),
11+
"u64" => (("qword", quote! { reg }), ("", "")),
12+
_ => {
13+
return None;
14+
}
15+
};
16+
let x64_width_modifier = format!("{x64_asm_width} ptr ");
17+
let x64_cls_location = format!("gs:[{offset}]");
18+
19+
Some(quote! {
20+
#[inline]
21+
pub fn load(&self) -> #ty {
22+
#[cfg(target_arch = "x86_64")]
23+
{
24+
let ret;
25+
unsafe {
26+
::core::arch::asm!(
27+
::core::concat!("mov {}, ", #x64_cls_location),
28+
out(#x64_reg_class) ret,
29+
options(preserves_flags, nostack),
30+
)
31+
};
32+
ret
33+
}
34+
#[cfg(target_arch = "aarch64")]
35+
{
36+
let ret;
37+
unsafe {
38+
::core::arch::asm!(
39+
"2:",
40+
// Load value.
41+
"mrs {tp_1}, tpidr_el1",
42+
concat!(
43+
"ldr", #aarch64_instr_width,
44+
" {ret", #aarch64_reg_modifier,"},",
45+
" [{tp_1},#", stringify!(#offset), "]",
46+
),
47+
48+
// Make sure task wasn't migrated between mrs and ldr.
49+
"mrs {tp_2}, tpidr_el1",
50+
"cmp {tp_1}, {tp_2}",
51+
"b.ne 2b",
52+
53+
tp_1 = out(reg) _,
54+
ret = out(reg) ret,
55+
tp_2 = out(reg) _,
56+
57+
options(nostack),
58+
)
59+
};
60+
ret
61+
}
62+
}
63+
64+
#[inline]
65+
pub fn fetch_add(&self, mut operand: #ty) -> #ty {
66+
#[cfg(target_arch = "x86_64")]
67+
{
68+
unsafe {
69+
::core::arch::asm!(
70+
::core::concat!("xadd ", #x64_width_modifier, #x64_cls_location, ", {}"),
71+
inout(#x64_reg_class) operand,
72+
options(nostack),
73+
)
74+
};
75+
operand
76+
}
77+
#[cfg(target_arch = "aarch64")]
78+
{
79+
let ret;
80+
unsafe {
81+
::core::arch::asm!(
82+
"2:",
83+
// Load value.
84+
"mrs {tp_1}, tpidr_el1",
85+
concat!("add {ptr}, {tp_1}, ", stringify!(#offset)),
86+
concat!("ldxr", #aarch64_instr_width, " {value", #aarch64_reg_modifier,"}, [{ptr}]"),
87+
88+
// Make sure task wasn't migrated between msr and ldxr.
89+
"mrs {tp_2}, tpidr_el1",
90+
"cmp {tp_1}, {tp_2}",
91+
"b.ne 2b",
92+
93+
// Compute and store value (reuse tp_1 register).
94+
"add {tp_1}, {value}, {operand}",
95+
concat!("stxr", #aarch64_instr_width, " {cond:w}, {tp_1", #aarch64_reg_modifier,"}, [{ptr}]"),
96+
97+
// Make sure task wasn't migrated between ldxr and stxr.
98+
"cbnz {cond}, 2b",
99+
100+
tp_1 = out(reg) ret,
101+
ptr = out(reg) _,
102+
value = out(reg) ret,
103+
tp_2 = out(reg) _,
104+
operand = in(reg) operand,
105+
cond = out(reg) _,
106+
107+
options(nostack),
108+
)
109+
};
110+
ret
111+
}
112+
}
113+
114+
#[inline]
115+
pub fn fetch_sub(&self, mut operand: #ty) -> #ty {
116+
operand = operand.overflowing_neg().0;
117+
self.fetch_add(operand)
118+
}
119+
})
120+
}

0 commit comments

Comments
 (0)