Skip to content

Commit

Permalink
Rework env module
Browse files Browse the repository at this point in the history
  • Loading branch information
mkroening committed Mar 14, 2022
1 parent 6f93f96 commit 0dd7f62
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 99 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
192 changes: 99 additions & 93 deletions src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Env> = OnceBox::new();

static mut COMMAND_LINE_CPU_FREQUENCY: Option<u16> = None;
static mut IS_PROXY: bool = false;
static mut COMMAND_LINE_ENVIRONMENT: Vec<String> = Vec::new();
static mut COMMAND_LINE_APPLICATION: Option<Vec<String>> = None;
static mut COMMAND_LINE_PATH: Option<String> = 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::<u8>();
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<String>,
freq: Option<u16>,
env_vars: Vec<String>,
args: Vec<String>,
}

#[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::<u8>();
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<String>, 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<u16> {
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
}
11 changes: 5 additions & 6 deletions src/syscalls/interfaces/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 0dd7f62

Please sign in to comment.