kernel/arch/
user_context.rs1use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
19
20use crate::device::fdt::FdtManager;
21
22const FLAG_USER_FPU: u8 = 1 << 0;
23const FLAG_USER_VECTOR: u8 = 1 << 1;
24
25static INITIALIZED: AtomicBool = AtomicBool::new(false);
26static FLAGS: AtomicU8 = AtomicU8::new(0);
27
28pub fn init_from_fdt() {
32 if INITIALIZED
33 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
34 .is_err()
35 {
36 return;
37 }
38
39 let (mut enable_fpu, mut enable_vector) = arch_defaults_from_fdt();
40
41 if let Some(fdt) = FdtManager::get_manager().get_fdt() {
43 if let Some(chosen) = fdt.find_node("/chosen") {
44 if let Some(v) = read_boolish_property(&chosen, "scarlet,user-fpu") {
45 enable_fpu = v;
46 }
47 if let Some(v) = read_boolish_property(&chosen, "scarlet,user-vector") {
48 enable_vector = v;
49 }
50 }
51 }
52
53 let mut flags = 0u8;
54 if cfg!(feature = "user-fpu") && enable_fpu {
55 flags |= FLAG_USER_FPU;
56 }
57 if cfg!(feature = "user-vector") && enable_vector {
58 flags |= FLAG_USER_VECTOR;
59 }
60 FLAGS.store(flags, Ordering::Release);
61
62 crate::early_println!(
64 "[userctx] enabled: user-fpu={} user-vector={}",
65 user_fpu_enabled(),
66 user_vector_enabled()
67 );
68}
69
70#[inline]
72pub fn user_fpu_enabled() -> bool {
73 cfg!(feature = "user-fpu") && (FLAGS.load(Ordering::Acquire) & FLAG_USER_FPU != 0)
74}
75
76#[inline]
78pub fn user_vector_enabled() -> bool {
79 cfg!(feature = "user-vector") && (FLAGS.load(Ordering::Acquire) & FLAG_USER_VECTOR != 0)
80}
81
82fn read_boolish_property(node: &fdt::node::FdtNode, name: &str) -> Option<bool> {
83 let prop = node.property(name)?;
84
85 if prop.value.is_empty() {
87 return Some(true);
88 }
89
90 match prop.value.len() {
91 4 => Some(u32::from_be_bytes(prop.value[0..4].try_into().ok()?) != 0),
92 8 => Some(u64::from_be_bytes(prop.value[0..8].try_into().ok()?) != 0),
93 _ => {
94 let s = bytes_to_cstr(prop.value)?;
96 match s.trim() {
97 "0" | "false" | "no" | "off" => Some(false),
98 "1" | "true" | "yes" | "on" => Some(true),
99 _ => None,
100 }
101 }
102 }
103}
104
105fn bytes_to_cstr(bytes: &[u8]) -> Option<&str> {
106 let len = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
107 core::str::from_utf8(&bytes[..len]).ok()
108}
109
110fn arch_defaults_from_fdt() -> (bool, bool) {
111 #[cfg(target_arch = "riscv64")]
116 {
117 if let Some(fdt) = FdtManager::get_manager().get_fdt() {
118 if let Some((has_fpu, has_vec)) = riscv_extensions_from_fdt(fdt) {
119 return (has_fpu, has_vec);
120 }
121 }
122 return (true, false);
123 }
124
125 #[cfg(target_arch = "aarch64")]
126 {
127 return (true, false);
130 }
131
132 #[cfg(not(any(target_arch = "riscv64", target_arch = "aarch64")))]
133 {
134 (true, false)
135 }
136}
137
138#[cfg(target_arch = "riscv64")]
139fn riscv_extensions_from_fdt(fdt: &fdt::Fdt) -> Option<(bool, bool)> {
140 let cpus = fdt.find_node("/cpus")?;
141
142 let mut saw_cpu = false;
143 let mut all_have_fpu = true;
144 let mut all_have_vec = true;
145
146 for cpu in cpus.children() {
147 if let Some(dev_type) = cpu.property("device_type") {
149 if bytes_to_cstr(dev_type.value)
150 .map(|s| s != "cpu")
151 .unwrap_or(false)
152 {
153 continue;
154 }
155 }
156
157 let (has_fpu, has_vec) = if let Some(p) = cpu.property("riscv,isa-extensions") {
159 riscv_extensions_from_isa_extensions(p.value)?
160 } else if let Some(p) = cpu.property("riscv,isa") {
161 riscv_extensions_from_isa_string(p.value)?
162 } else {
163 continue;
164 };
165
166 saw_cpu = true;
167 all_have_fpu &= has_fpu;
168 all_have_vec &= has_vec;
169 }
170
171 if saw_cpu {
172 Some((all_have_fpu, all_have_vec))
173 } else {
174 None
175 }
176}
177
178#[cfg(target_arch = "riscv64")]
179fn riscv_extensions_from_isa_string(isa: &[u8]) -> Option<(bool, bool)> {
180 let len = isa.iter().position(|&b| b == 0).unwrap_or(isa.len());
181 let isa = &isa[..len];
182 if isa.len() < 4 {
183 return None;
184 }
185
186 let b0 = isa[0] | 0x20;
188 let b1 = isa[1] | 0x20;
189 if b0 != b'r' || b1 != b'v' {
190 return None;
191 }
192
193 let start = 4;
196 let mut end = isa.len();
197 for (i, &b) in isa[start..].iter().enumerate() {
198 if b == b'_' {
199 end = start + i;
200 break;
201 }
202 }
203
204 let letters = &isa[start..end];
205 let has_fpu = letters.iter().any(|&b| {
206 let c = b | 0x20;
207 c == b'f' || c == b'd'
208 });
209
210 let mut has_vec = letters.iter().any(|&b| (b | 0x20) == b'v');
213
214 if !has_vec && end < isa.len() {
215 has_vec = riscv_isa_suffix_has_vector_tokens(&isa[end + 1..]);
216 }
217
218 Some((has_fpu, has_vec))
219}
220
221#[cfg(target_arch = "riscv64")]
222fn riscv_isa_suffix_has_vector_tokens(mut s: &[u8]) -> bool {
223 while !s.is_empty() {
225 let next = s.iter().position(|&b| b == b'_').unwrap_or(s.len());
226 let tok = &s[..next];
227 if riscv_token_is_vector(tok) {
228 return true;
229 }
230 s = if next < s.len() { &s[next + 1..] } else { &[] };
231 }
232 false
233}
234
235#[cfg(target_arch = "riscv64")]
236fn riscv_extensions_from_isa_extensions(list: &[u8]) -> Option<(bool, bool)> {
237 let mut has_fpu = false;
239 let mut has_vec = false;
240
241 let mut i = 0;
242 while i < list.len() {
243 let end = list[i..]
244 .iter()
245 .position(|&b| b == 0)
246 .map(|p| i + p)
247 .unwrap_or(list.len());
248 let tok = &list[i..end];
249 if tok.is_empty() {
250 break;
251 }
252 if riscv_token_is_fpu(tok) {
253 has_fpu = true;
254 }
255 if riscv_token_is_vector(tok) {
256 has_vec = true;
257 }
258 if end == list.len() {
259 break;
260 }
261 i = end + 1;
262 }
263
264 Some((has_fpu, has_vec))
265}
266
267#[cfg(target_arch = "riscv64")]
268fn riscv_token_is_fpu(tok: &[u8]) -> bool {
269 tok.len() == 1 && {
270 let c = tok[0] | 0x20;
271 c == b'f' || c == b'd'
272 }
273}
274
275#[cfg(target_arch = "riscv64")]
276fn riscv_token_is_vector(tok: &[u8]) -> bool {
277 if tok.is_empty() {
278 return false;
279 }
280 if tok.len() == 1 && ((tok[0] | 0x20) == b'v') {
282 return true;
283 }
284 if tok.len() >= 3 {
285 let t0 = tok[0] | 0x20;
286 let t1 = tok[1] | 0x20;
287 let t2 = tok[2] | 0x20;
288 if t0 == b'z' && t1 == b'v' && (t2 == b'e' || t2 == b'l') {
290 return true;
291 }
292 }
293 false
294}