Skip to content

Commit 4a9f48d

Browse files
committed
Parse config strings at compile time
This commit makes it possible to parse config strings at compile time. Unfortunately `usize::from_str_radix` is not yet stable (but should be soon), so we had to hand-roll a `const` parser. We can fall back to `from_str_radix` once it gets stabilized as a const function, see: rust-lang/rust#124941
1 parent 7d8e121 commit 4a9f48d

File tree

3 files changed

+70
-23
lines changed

3 files changed

+70
-23
lines changed

src/config.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,71 @@ macro_rules! is_enabled {
2121
};
2222
}
2323

24+
// ————————————————————————————— String Parsing ————————————————————————————— //
25+
// Required to parse environment variables at compile time.
26+
// Can be removed once usize::from_str_radix stabilized as const, hopefully soon.
27+
// See https://github.com/rust-lang/rust/pull/124941
28+
//
29+
// Source (and license), adapted for Mirage:
30+
// https://gist.github.com/DutchGhost/d8604a3c796479777fe9f5e25d855cfd
31+
// —————————————————————————————————————————————————————————————————————————— //
32+
33+
const fn parse_byte(b: u8, pow10: usize) -> usize {
34+
let r = b - 48; // Remove ascii offset
35+
36+
if r > 9 {
37+
panic!("Failed to parse config: expected usize")
38+
} else {
39+
(r as usize) * pow10
40+
}
41+
}
42+
43+
const POW10: [usize; 20] = {
44+
let mut array = [0; 20];
45+
let mut current = 1;
46+
47+
let mut index = 20;
48+
49+
loop {
50+
index -= 1;
51+
array[index] = current;
52+
53+
if index == 0 {
54+
break;
55+
}
56+
57+
current *= 10;
58+
}
59+
60+
array
61+
};
62+
63+
const fn parse(env_var: Option<&str>) -> Option<usize> {
64+
let Some(env_var) = env_var else {
65+
return None;
66+
};
67+
68+
let bytes = env_var.as_bytes();
69+
let mut result: usize = 0;
70+
71+
let len = bytes.len();
72+
73+
// Start at the correct index of the table,
74+
// (skip the power's that are too large)
75+
let mut index_const_table = POW10.len().wrapping_sub(len);
76+
let mut index = 0;
77+
78+
while index < env_var.len() {
79+
let pow = POW10[index_const_table];
80+
result += parse_byte(bytes[index], pow);
81+
82+
index += 1;
83+
index_const_table += 1;
84+
}
85+
86+
Some(result)
87+
}
88+
2489
// ———————————————————————— Configuration Parameters ———————————————————————— //
2590

2691
/// Weather the platform supports S mode.
@@ -30,4 +95,4 @@ pub const HAS_S_MODE: bool = is_enabled!("MIRAGE_PLATFORM_S_MODE");
3095
pub const LOG_LEVEL: Option<&'static str> = option_env!("MIRAGE_LOG_LEVEL");
3196

3297
/// The maximum number of firmware exits before quitting.
33-
pub const MAX_FIRMWARE_EXIT: Option<&'static str> = option_env!("MIRAGE_DEBUG_MAX_FIRMWARE_EXITS");
98+
pub const MAX_FIRMWARE_EXIT: Option<usize> = parse(option_env!("MIRAGE_DEBUG_MAX_FIRMWARE_EXITS"));

src/debug.rs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,6 @@
11
//! Debug utils for Mirage
22
3-
use crate::{_stack_bottom, _stack_top, config};
4-
5-
// ——————————————————————————— Max Firmware Exits ——————————————————————————— //
6-
7-
/// Returns the maximum number of firmware exits before panicking.
8-
///
9-
/// If the value returned is None, there is no maximum cap.
10-
///
11-
/// NOTE: For now we still need some basic runtime parsing, but once
12-
/// https://github.com/rust-lang/rust/pull/99322 gets merged we can convert this function to a
13-
/// constant.
14-
pub fn get_max_firmware_exits() -> Option<usize> {
15-
match config::MAX_FIRMWARE_EXIT {
16-
Some(env_var) => usize::from_str_radix(env_var, 10).ok(),
17-
None => None,
18-
}
19-
}
3+
use crate::{_stack_bottom, _stack_top};
204

215
// ———————————————————————————— Max Stack Usage ————————————————————————————— //
226

src/main.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,18 +86,16 @@ pub(crate) extern "C" fn main(hart_id: usize, device_tree_blob_addr: usize) -> !
8686
}
8787

8888
fn main_loop(mut ctx: VirtContext, mut mctx: MirageContext) -> ! {
89-
let max_exit = debug::get_max_firmware_exits();
90-
9189
loop {
9290
unsafe {
9391
Arch::run_vcpu(&mut ctx);
94-
handle_trap(&mut ctx, &mut mctx, max_exit);
92+
handle_trap(&mut ctx, &mut mctx);
9593
log::trace!("{:x?}", &ctx);
9694
}
9795
}
9896
}
9997

100-
fn handle_trap(ctx: &mut VirtContext, mctx: &mut MirageContext, max_exit: Option<usize>) {
98+
fn handle_trap(ctx: &mut VirtContext, mctx: &mut MirageContext) {
10199
log::trace!("Trapped!");
102100
log::trace!(" mcause: {:?}", ctx.trap_info.mcause);
103101
log::trace!(" mstatus: 0x{:x}", ctx.trap_info.mstatus);
@@ -106,7 +104,7 @@ fn handle_trap(ctx: &mut VirtContext, mctx: &mut MirageContext, max_exit: Option
106104
log::trace!(" exits: {}", ctx.nb_exits + 1);
107105
log::trace!(" mode: {:?}", ctx.mode);
108106

109-
if let Some(max_exit) = max_exit {
107+
if let Some(max_exit) = config::MAX_FIRMWARE_EXIT {
110108
if ctx.nb_exits + 1 >= max_exit {
111109
log::error!("Reached maximum number of exits: {}", ctx.nb_exits);
112110
Plat::exit_failure();

0 commit comments

Comments
 (0)