Skip to content

Commit

Permalink
Parse config strings at compile time
Browse files Browse the repository at this point in the history
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
  • Loading branch information
CharlyCst committed Jun 9, 2024
1 parent 7d8e121 commit 4a9f48d
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 23 deletions.
67 changes: 66 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,71 @@ macro_rules! is_enabled {
};
}

// ————————————————————————————— String Parsing ————————————————————————————— //
// Required to parse environment variables at compile time.
// Can be removed once usize::from_str_radix stabilized as const, hopefully soon.
// See https://github.com/rust-lang/rust/pull/124941
//
// Source (and license), adapted for Mirage:
// https://gist.github.com/DutchGhost/d8604a3c796479777fe9f5e25d855cfd
// —————————————————————————————————————————————————————————————————————————— //

const fn parse_byte(b: u8, pow10: usize) -> usize {
let r = b - 48; // Remove ascii offset

if r > 9 {
panic!("Failed to parse config: expected usize")
} else {
(r as usize) * pow10
}
}

const POW10: [usize; 20] = {
let mut array = [0; 20];
let mut current = 1;

let mut index = 20;

loop {
index -= 1;
array[index] = current;

if index == 0 {
break;
}

current *= 10;
}

array
};

const fn parse(env_var: Option<&str>) -> Option<usize> {
let Some(env_var) = env_var else {
return None;
};

let bytes = env_var.as_bytes();
let mut result: usize = 0;

let len = bytes.len();

// Start at the correct index of the table,
// (skip the power's that are too large)
let mut index_const_table = POW10.len().wrapping_sub(len);
let mut index = 0;

while index < env_var.len() {
let pow = POW10[index_const_table];
result += parse_byte(bytes[index], pow);

index += 1;
index_const_table += 1;
}

Some(result)
}

// ———————————————————————— Configuration Parameters ———————————————————————— //

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

/// The maximum number of firmware exits before quitting.
pub const MAX_FIRMWARE_EXIT: Option<&'static str> = option_env!("MIRAGE_DEBUG_MAX_FIRMWARE_EXITS");
pub const MAX_FIRMWARE_EXIT: Option<usize> = parse(option_env!("MIRAGE_DEBUG_MAX_FIRMWARE_EXITS"));
18 changes: 1 addition & 17 deletions src/debug.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,6 @@
//! Debug utils for Mirage
use crate::{_stack_bottom, _stack_top, config};

// ——————————————————————————— Max Firmware Exits ——————————————————————————— //

/// Returns the maximum number of firmware exits before panicking.
///
/// If the value returned is None, there is no maximum cap.
///
/// NOTE: For now we still need some basic runtime parsing, but once
/// https://github.com/rust-lang/rust/pull/99322 gets merged we can convert this function to a
/// constant.
pub fn get_max_firmware_exits() -> Option<usize> {
match config::MAX_FIRMWARE_EXIT {
Some(env_var) => usize::from_str_radix(env_var, 10).ok(),
None => None,
}
}
use crate::{_stack_bottom, _stack_top};

// ———————————————————————————— Max Stack Usage ————————————————————————————— //

Expand Down
8 changes: 3 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,16 @@ pub(crate) extern "C" fn main(hart_id: usize, device_tree_blob_addr: usize) -> !
}

fn main_loop(mut ctx: VirtContext, mut mctx: MirageContext) -> ! {
let max_exit = debug::get_max_firmware_exits();

loop {
unsafe {
Arch::run_vcpu(&mut ctx);
handle_trap(&mut ctx, &mut mctx, max_exit);
handle_trap(&mut ctx, &mut mctx);
log::trace!("{:x?}", &ctx);
}
}
}

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

if let Some(max_exit) = max_exit {
if let Some(max_exit) = config::MAX_FIRMWARE_EXIT {
if ctx.nb_exits + 1 >= max_exit {
log::error!("Reached maximum number of exits: {}", ctx.nb_exits);
Plat::exit_failure();
Expand Down

0 comments on commit 4a9f48d

Please sign in to comment.