From 4a9f48d73dc6b674861abc39f4d4e40e205cc6a7 Mon Sep 17 00:00:00 2001 From: Charly Castes Date: Sun, 2 Jun 2024 21:13:57 +0200 Subject: [PATCH] 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: https://github.com/rust-lang/rust/pull/124941 --- src/config.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/debug.rs | 18 +------------- src/main.rs | 8 +++--- 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/src/config.rs b/src/config.rs index 08046fed..c6d5982f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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 { + 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. @@ -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 = parse(option_env!("MIRAGE_DEBUG_MAX_FIRMWARE_EXITS")); diff --git a/src/debug.rs b/src/debug.rs index cdb44bef..246554fd 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -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 { - 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 ————————————————————————————— // diff --git a/src/main.rs b/src/main.rs index a612fbbc..1f8ae17f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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) { +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); @@ -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();