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