1use alloc::string::{String, ToString};
4use alloc::vec::Vec;
5use core::{ptr, str};
6
7use ahash::RandomState;
8use fdt::Fdt;
9use hashbrown::HashMap;
10use hashbrown::hash_map::Iter;
11use hermit_entry::boot_info::{BootInfo, PlatformInfo, RawBootInfo};
12use hermit_sync::OnceCell;
13
14pub(crate) use crate::arch::kernel::{self, get_base_address, get_image_size, get_ram_address};
15
16static BOOT_INFO: OnceCell<BootInfo> = OnceCell::new();
17
18pub fn boot_info() -> &'static BootInfo {
19 BOOT_INFO.get().unwrap()
20}
21
22pub fn set_boot_info(raw_boot_info: RawBootInfo) {
23 let boot_info = BootInfo::from(raw_boot_info);
24 BOOT_INFO.set(boot_info).unwrap();
25}
26
27static CLI: OnceCell<Cli> = OnceCell::new();
28
29pub fn init() {
30 CLI.set(Cli::default()).unwrap();
31}
32
33#[derive(Debug)]
34struct Cli {
35 #[allow(dead_code)]
36 image_path: Option<String>,
37 #[cfg(not(target_arch = "riscv64"))]
38 freq: Option<u16>,
39 env_vars: HashMap<String, String, RandomState>,
40 args: Vec<String>,
41 #[allow(dead_code)]
42 mmio: Vec<String>,
43}
44
45pub fn is_uhyve() -> bool {
47 matches!(boot_info().platform_info, PlatformInfo::Uhyve { .. })
48}
49
50pub fn is_uefi() -> bool {
51 fdt().is_some_and(|fdt| fdt.root().compatible().first() == "hermit,uefi")
52}
53
54pub fn fdt() -> Option<Fdt<'static>> {
55 boot_info().hardware_info.device_tree.map(|fdt| {
56 let ptr = ptr::with_exposed_provenance(fdt.get().try_into().unwrap());
57 unsafe { Fdt::from_ptr(ptr).unwrap() }
58 })
59}
60
61#[cfg(all(target_arch = "x86_64", feature = "acpi"))]
63pub fn rsdp() -> Option<core::num::NonZero<usize>> {
64 let rsdp = fdt()?
65 .find_node("/hermit,rsdp")?
66 .reg()?
67 .next()?
68 .starting_address
69 .addr();
70 core::num::NonZero::new(rsdp)
71}
72
73pub fn fdt_args() -> Option<&'static str> {
74 fdt().and_then(|fdt| fdt.chosen().bootargs())
75}
76
77impl Default for Cli {
78 fn default() -> Self {
79 let mut image_path = None;
80 #[cfg(not(target_arch = "riscv64"))]
81 let mut freq = None;
82 let mut env_vars = HashMap::<String, String, RandomState>::with_hasher(
83 RandomState::with_seeds(0, 0, 0, 0),
84 );
85
86 let args = kernel::args().or_else(fdt_args).unwrap_or_default();
87 info!("bootargs = {args}");
88 let words = shell_words::split(args).unwrap();
89
90 let mut words = words.into_iter();
91 let expect_arg = |arg: Option<String>, name: &str| {
92 arg.unwrap_or_else(|| {
93 panic!("The argument '{name}' requires a value but none was supplied")
94 })
95 };
96
97 let mut args = Vec::new();
98 let mut mmio = Vec::new();
99 while let Some(word) = words.next() {
100 if word.as_str().starts_with("virtio_mmio.device=") {
101 let v: Vec<&str> = word.as_str().split('=').collect();
102 mmio.push(v[1].to_string());
103 continue;
104 }
105
106 match word.as_str() {
107 #[cfg(not(target_arch = "riscv64"))]
108 "-freq" => {
109 let s = expect_arg(words.next(), word.as_str());
110 freq = Some(s.parse().unwrap());
111 }
112 "-ip" => {
113 let ip = expect_arg(words.next(), word.as_str());
114 env_vars.insert(String::from("HERMIT_IP"), ip);
115 }
116 "-mask" => {
117 let mask = expect_arg(words.next(), word.as_str());
118 env_vars.insert(String::from("HERMIT_MASK"), mask);
119 }
120 "-gateway" => {
121 let gateway = expect_arg(words.next(), word.as_str());
122 env_vars.insert(String::from("HERMIT_GATEWAY"), gateway);
123 }
124 "-mount" => {
125 let gateway = expect_arg(words.next(), word.as_str());
126 env_vars.insert(String::from("UHYVE_MOUNT"), gateway);
127 }
128 "--" => args.extend(&mut words),
129 word if word.contains('=') => {
130 let (arg, value) = word.split_once('=').unwrap();
131
132 match arg {
133 "env" => {
134 let Some((key, value)) = value.split_once('=') else {
135 error!("could not parse bootarg: {word}");
136 continue;
137 };
138 env_vars.insert(key.to_string(), value.to_string());
139 }
140 _ => error!("could not parse bootarg: {word}"),
141 }
142 }
143 _ if image_path.is_none() => image_path = Some(word),
144 word => error!("could not parse bootarg: {word}"),
145 };
146 }
147
148 Self {
149 image_path,
150 #[cfg(not(target_arch = "riscv64"))]
151 freq,
152 env_vars,
153 args,
154 #[allow(dead_code)]
155 mmio,
156 }
157 }
158}
159
160#[cfg(not(target_arch = "riscv64"))]
162pub fn freq() -> Option<u16> {
163 CLI.get().unwrap().freq
164}
165
166#[allow(dead_code)]
167pub fn var(key: &str) -> Option<&String> {
168 CLI.get().unwrap().env_vars.get(key)
169}
170
171pub fn vars() -> Iter<'static, String, String> {
172 CLI.get().unwrap().env_vars.iter()
173}
174
175pub fn args() -> &'static [String] {
177 CLI.get().unwrap().args.as_slice()
178}
179
180#[allow(dead_code)]
182pub fn mmio() -> &'static [String] {
183 CLI.get().unwrap().mmio.as_slice()
184}