use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::{ptr, str};
use ahash::RandomState;
use fdt::Fdt;
use hashbrown::HashMap;
use hashbrown::hash_map::Iter;
use hermit_entry::boot_info::{BootInfo, PlatformInfo, RawBootInfo};
use hermit_sync::OnceCell;
pub(crate) use crate::arch::kernel::{self, get_base_address, get_image_size, get_ram_address};
static BOOT_INFO: OnceCell<BootInfo> = OnceCell::new();
pub fn boot_info() -> &'static BootInfo {
BOOT_INFO.get().unwrap()
}
pub fn set_boot_info(raw_boot_info: RawBootInfo) {
let boot_info = BootInfo::from(raw_boot_info);
BOOT_INFO.set(boot_info).unwrap();
}
static CLI: OnceCell<Cli> = OnceCell::new();
pub fn init() {
CLI.set(Cli::default()).unwrap();
}
#[derive(Debug)]
struct Cli {
#[allow(dead_code)]
image_path: Option<String>,
#[cfg(not(target_arch = "riscv64"))]
freq: Option<u16>,
env_vars: HashMap<String, String, RandomState>,
args: Vec<String>,
#[allow(dead_code)]
mmio: Vec<String>,
}
pub fn is_uhyve() -> bool {
matches!(boot_info().platform_info, PlatformInfo::Uhyve { .. })
}
pub fn is_uefi() -> bool {
fdt().is_some_and(|fdt| fdt.root().compatible().first() == "hermit,uefi")
}
pub fn fdt() -> Option<Fdt<'static>> {
boot_info().hardware_info.device_tree.map(|fdt| {
let ptr = ptr::with_exposed_provenance(fdt.get().try_into().unwrap());
unsafe { Fdt::from_ptr(ptr).unwrap() }
})
}
#[cfg(all(target_arch = "x86_64", feature = "acpi"))]
pub fn rsdp() -> Option<core::num::NonZero<usize>> {
let rsdp = fdt()?
.find_node("/hermit,rsdp")?
.reg()?
.next()?
.starting_address
.addr();
core::num::NonZero::new(rsdp)
}
pub fn fdt_args() -> Option<&'static str> {
fdt().and_then(|fdt| fdt.chosen().bootargs())
}
impl Default for Cli {
fn default() -> Self {
let mut image_path = None;
#[cfg(not(target_arch = "riscv64"))]
let mut freq = None;
let mut env_vars = HashMap::<String, String, RandomState>::with_hasher(
RandomState::with_seeds(0, 0, 0, 0),
);
let args = kernel::args().or_else(fdt_args).unwrap_or_default();
info!("bootargs = {args}");
let words = shell_words::split(args).unwrap();
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")
})
};
let mut args = Vec::new();
let mut mmio = Vec::new();
while let Some(word) = words.next() {
if word.as_str().starts_with("virtio_mmio.device=") {
let v: Vec<&str> = word.as_str().split('=').collect();
mmio.push(v[1].to_string());
continue;
}
match word.as_str() {
#[cfg(not(target_arch = "riscv64"))]
"-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.insert(String::from("HERMIT_IP"), ip);
}
"-mask" => {
let mask = expect_arg(words.next(), word.as_str());
env_vars.insert(String::from("HERMIT_MASK"), mask);
}
"-gateway" => {
let gateway = expect_arg(words.next(), word.as_str());
env_vars.insert(String::from("HERMIT_GATEWAY"), gateway);
}
"-mount" => {
let gateway = expect_arg(words.next(), word.as_str());
env_vars.insert(String::from("UHYVE_MOUNT"), gateway);
}
"--" => args.extend(&mut words),
word if word.contains('=') => {
let (arg, value) = word.split_once('=').unwrap();
match arg {
"env" => {
let Some((key, value)) = value.split_once('=') else {
error!("could not parse bootarg: {word}");
continue;
};
env_vars.insert(key.to_string(), value.to_string());
}
_ => error!("could not parse bootarg: {word}"),
}
}
_ if image_path.is_none() => image_path = Some(word),
word => error!("could not parse bootarg: {word}"),
};
}
Self {
image_path,
#[cfg(not(target_arch = "riscv64"))]
freq,
env_vars,
args,
#[allow(dead_code)]
mmio,
}
}
}
#[cfg(not(target_arch = "riscv64"))]
pub fn freq() -> Option<u16> {
CLI.get().unwrap().freq
}
#[allow(dead_code)]
pub fn var(key: &str) -> Option<&String> {
CLI.get().unwrap().env_vars.get(key)
}
pub fn vars() -> Iter<'static, String, String> {
CLI.get().unwrap().env_vars.iter()
}
pub fn args() -> &'static [String] {
CLI.get().unwrap().args.as_slice()
}
#[allow(dead_code)]
pub fn mmio() -> &'static [String] {
CLI.get().unwrap().mmio.as_slice()
}