From 0dd7f62b7456756d4e95d8d32d38521042e3dbef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Mon, 14 Mar 2022 12:57:26 +0100 Subject: [PATCH] Rework env module --- Cargo.toml | 1 + src/env.rs | 192 +++++++++++++++++---------------- src/syscalls/interfaces/mod.rs | 11 +- 3 files changed, 105 insertions(+), 99 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0e753ecd37..f1037c1521 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,7 @@ log = { version = "0.4", default-features = false } num = { version = "0.4", default-features = false } num-derive = "0.3" num-traits = { version = "0.2", default-features = false } +once_cell = { version = "1.10", default-features = false, features = ["alloc"] } pci-ids = { version = "0.2", optional = true } scopeguard = { version = "1.1", default-features = false } shell-words = { version = "1.1", default-features = false } diff --git a/src/env.rs b/src/env.rs index d6497c7f4f..549e529d58 100644 --- a/src/env.rs +++ b/src/env.rs @@ -2,122 +2,128 @@ //! vs. multi-kernel, hypervisor, etc.) as well as central parsing of the //! command-line parameters. +use alloc::{boxed::Box, string::String, vec::Vec}; +use core::{slice, str}; + +use once_cell::race::OnceBox; + pub use crate::arch::kernel::{ get_base_address, get_cmdline, get_cmdsize, get_image_size, get_ram_address, get_tls_align, get_tls_filesz, get_tls_memsz, get_tls_start, is_single_kernel, is_uhyve, }; -use alloc::string::String; -use alloc::vec::Vec; -use core::{slice, str}; +static ENV: OnceBox = OnceBox::new(); -static mut COMMAND_LINE_CPU_FREQUENCY: Option = None; -static mut IS_PROXY: bool = false; -static mut COMMAND_LINE_ENVIRONMENT: Vec = Vec::new(); -static mut COMMAND_LINE_APPLICATION: Option> = None; -static mut COMMAND_LINE_PATH: Option = None; +pub fn init() { + ENV.set(Box::new(Env::default())).unwrap(); +} -unsafe fn parse_command_line() { - let cmdsize = get_cmdsize(); - if cmdsize == 0 { - return; - } +#[derive(Debug)] +struct Env { + cli: Cli, + is_proxy: bool, +} - // Convert the command-line into a Rust string slice. - let cmdline = get_cmdline().as_ptr::(); - let slice = unsafe { slice::from_raw_parts(cmdline, cmdsize) }; - let cmdline_str = unsafe { str::from_utf8_unchecked(slice) }; - - // Split at spaces, but not while in quotes - let tokens = shell_words::split(cmdline_str).unwrap(); - debug!("Got cmdline tokens as {:?}", tokens); - - let mut tokeniter = tokens.into_iter(); - while let Some(token) = tokeniter.next() { - match token.as_str() { - "-freq" => { - let mhz_str = tokeniter.next().expect("Invalid -freq command line"); - unsafe { - COMMAND_LINE_CPU_FREQUENCY = mhz_str.parse().ok(); - } - } - "-ip" => unsafe { - COMMAND_LINE_ENVIRONMENT.push(format!( - "HERMIT_IP={}", - tokeniter.next().expect("Invalid -ip command line") - )); - }, - "-mask" => unsafe { - COMMAND_LINE_ENVIRONMENT.push(format!( - "HERMIT_MASK={}", - tokeniter.next().expect("Invalid -mask command line") - )); - }, - "-gateway" => unsafe { - COMMAND_LINE_ENVIRONMENT.push(format!( - "HERMIT_GATEWAY={}", - tokeniter.next().expect("Invalid -gateway command line") - )); - }, - "-proxy" => unsafe { - IS_PROXY = true; - }, - "--" => { - // Collect remaining arguments as applications argv - //ToDo -> we know the length here, so we could (should convert this into a safe - // rust type (at least for rust applications) - unsafe { - COMMAND_LINE_APPLICATION = Some(tokeniter.collect()); - } - break; - } - _ if unsafe { COMMAND_LINE_PATH.is_none() } => { - // Qemu passes in the kernel path (rusty-loader) as first argument - unsafe { COMMAND_LINE_PATH = Some(token) } - } - _ => { - warn!("Unknown cmdline option: {} [{}]", token, cmdline_str); - } - }; +impl Default for Env { + fn default() -> Self { + let cli = Cli::default(); + + // Uhyve or baremetal implies unikernel mode and no communication with "proxy". + // Else we are running side-by-side to Linux, which implies communication with "proxy". + let is_proxy = !is_uhyve() && !is_single_kernel(); + + Self { cli, is_proxy } } } -/// Returns the cmdline argument passed in after "--" -pub fn get_command_line_argv() -> Option<&'static [String]> { - unsafe { COMMAND_LINE_APPLICATION.as_deref() } +#[derive(Debug)] +struct Cli { + image_path: Option, + freq: Option, + env_vars: Vec, + args: Vec, } -#[allow(dead_code)] -/// Returns the first cmdline argument, if not otherwise recognized. With qemu this is the host-path to the kernel (rusty-loader) -pub fn get_command_line_path() -> Option<&'static str> { - unsafe { COMMAND_LINE_PATH.as_deref() } +fn get_cmdline_str() -> &'static str { + let cmdsize = get_cmdsize(); + let cmdline = get_cmdline().as_ptr::(); + if cmdline.is_null() { + "" + } else { + // SAFETY: cmdline and cmdsize are valid forever. + let slice = unsafe { slice::from_raw_parts(cmdline, cmdsize) }; + str::from_utf8(slice).unwrap() + } } -pub fn get_command_line_envv() -> &'static [String] { - unsafe { COMMAND_LINE_ENVIRONMENT.as_slice() } -} +impl Default for Cli { + fn default() -> Self { + let mut image_path = None; + let mut freq = None; + let mut env_vars = Vec::new(); + let mut args = Vec::new(); -pub fn init() { - unsafe { - parse_command_line(); - - if is_uhyve() || is_single_kernel() { - // We are running under uhyve or baremetal, which implies unikernel mode and no communication with "proxy". - IS_PROXY = false; - } else { - // We are running side-by-side to Linux, which implies communication with "proxy". - IS_PROXY = true; + let words = shell_words::split(get_cmdline_str()).unwrap(); + debug!("cli_words = {words:?}"); + + let mut words = words.into_iter(); + let expect_arg = |arg: Option, name: &str| { + arg.unwrap_or_else(|| { + panic!("The argument '{name}' requires a value but none was supplied") + }) + }; + while let Some(word) = words.next() { + match word.as_str() { + "-freq" => { + let s = expect_arg(words.next(), word.as_str()); + freq = Some(s.parse().unwrap()); + } + "-ip" => { + let ip = expect_arg(words.next(), word.as_str()); + env_vars.push(format!("HERMIT_IP={ip}")); + } + "-mask" => { + let mask = expect_arg(words.next(), word.as_str()); + env_vars.push(format!("HERMIT_MASK={mask}")); + } + "-gateway" => { + let gateway = expect_arg(words.next(), word.as_str()); + env_vars.push(format!("HERMIT_GATEWAY={gateway}")); + } + "--" => args.extend(&mut words), + _ if image_path.is_none() => image_path = Some(word), + word => panic!( + "Found argument '{word}' which wasn't expected, or isn't valid in this context + + If you tried to supply `{word}` as a value rather than a flag, use `-- {word}`" + ), + }; + } + + Self { + image_path, + freq, + env_vars, + args, } } } -/// CPU Frequency in MHz if given through the -freq command-line parameter, otherwise zero. +/// CPU Frequency in MHz if given through the -freq command-line parameter. pub fn get_command_line_cpu_frequency() -> Option { - unsafe { COMMAND_LINE_CPU_FREQUENCY } + ENV.get().unwrap().cli.freq +} + +pub fn get_command_line_envv() -> &'static [String] { + ENV.get().unwrap().cli.env_vars.as_slice() +} + +/// Returns the cmdline argument passed in after "--" +pub fn get_command_line_argv() -> &'static [String] { + ENV.get().unwrap().cli.args.as_slice() } /// Whether HermitCore shall communicate with the "proxy" application over a network interface. -/// Only valid after calling init()! pub fn is_proxy() -> bool { - unsafe { IS_PROXY } + ENV.get().unwrap().is_proxy } diff --git a/src/syscalls/interfaces/mod.rs b/src/syscalls/interfaces/mod.rs index 24b9ec4a48..fba2f3d3d9 100644 --- a/src/syscalls/interfaces/mod.rs +++ b/src/syscalls/interfaces/mod.rs @@ -90,12 +90,11 @@ pub trait SyscallInterface: Send + Sync { let name = Box::leak(Box::new("{name}\0")).as_ptr(); argv.push(name); - if let Some(args) = env::get_command_line_argv() { - debug!("Setting argv as: {:?}", args); - for a in args { - let ptr = Box::leak(format!("{}\0", a).into_boxed_str()).as_ptr(); - argv.push(ptr); - } + let args = env::get_command_line_argv(); + debug!("Setting argv as: {:?}", args); + for arg in args { + let ptr = Box::leak(format!("{arg}\0").into_boxed_str()).as_ptr(); + argv.push(ptr); } let mut envv = Vec::new();