1use parser::{CmdlineParse, ParseError, ParseErrorKind, Result};
2use tokenizer::Tokenizer;
3use tracing::{error, info};
4
5use crate::{multiboot2::MultiBoot2Info, sync::once::OnceLock};
6
7mod macros;
8mod parser;
9mod tokenizer;
10
11static CMDLINE: OnceLock<Cmd> = OnceLock::new();
12
13fn parse_cmdline(inp: &str) -> Result<'_, Cmd<'_>> {
14 let mut tokenizer = Tokenizer::new(inp);
15 Cmd::parse_cmdline(&mut tokenizer)
16}
17
18const fn default_cmdline() -> Cmd<'static> {
19 Cmd {
20 uart: true,
21 uart_baud: 115200,
22 max_log_level: LogLevel::Info,
23 log_file: "/kernel.log",
24 allow_hpet: true,
25 log_aml: LogAml::Off,
26 }
27}
28
29pub fn init(multiboot_info: &'static MultiBoot2Info) {
30 let cmdline = multiboot_info
31 .cmdline()
32 .and_then(|cmdline| {
33 parse_cmdline(cmdline).ok()
35 })
36 .unwrap_or(default_cmdline());
37
38 CMDLINE.set(cmdline).expect("Should only be called once");
39}
40
41pub fn print_cmdline_parse(multiboot_info: &MultiBoot2Info) {
43 if let Some(cmdline) = multiboot_info.cmdline() {
44 let parsed = parse_cmdline(cmdline);
45 info!("Command line: {cmdline:?}");
46 match parsed {
47 Ok(parsed) => info!("Parsed command line: {parsed:?}"),
48 Err(e) => error!("Failed to parse command line: {e:?}"),
49 }
50 };
51}
52
53pub fn cmdline() -> &'static Cmd<'static> {
54 CMDLINE.get_or_init(default_cmdline)
56}
57
58#[macro_rules_attribute::apply(macros::cmdline_struct!)]
59#[derive(Debug)]
60pub struct Cmd<'a> {
61 #[default = true]
63 pub uart: bool,
64 #[default = 115200]
66 pub uart_baud: u32,
67 #[default = LogLevel::Info]
69 pub max_log_level: LogLevel,
70 #[default = "/kernel.log"]
72 pub log_file: &'a str,
73 #[default = true]
75 pub allow_hpet: bool,
76 #[default = LogAml::Off]
78 pub log_aml: LogAml,
79}
80
81#[derive(Default, Debug, Clone, Copy)]
82pub enum LogLevel {
83 Trace,
84 Debug,
85 #[default]
86 Info,
87 Warn,
88 Error,
89}
90
91impl From<LogLevel> for tracing::Level {
92 fn from(val: LogLevel) -> Self {
93 match val {
94 LogLevel::Trace => tracing::Level::TRACE,
95 LogLevel::Debug => tracing::Level::DEBUG,
96 LogLevel::Info => tracing::Level::INFO,
97 LogLevel::Warn => tracing::Level::WARN,
98 LogLevel::Error => tracing::Level::ERROR,
99 }
100 }
101}
102
103impl<'a> CmdlineParse<'a> for LogLevel {
104 fn parse_cmdline(tokenizer: &mut Tokenizer<'a>) -> Result<'a, Self> {
105 let (loc, value) = tokenizer.next_value().ok_or_else(|| {
106 ParseError::new(
107 ParseErrorKind::Unexpected {
108 need: "trace/debug/info/warn/error",
109 got: None,
110 },
111 tokenizer.current_index(),
112 )
113 })?;
114
115 match value {
116 "trace" => Ok(Self::Trace),
117 "debug" => Ok(Self::Debug),
118 "info" => Ok(Self::Info),
119 "warn" => Ok(Self::Warn),
120 "error" => Ok(Self::Error),
121 _ => Err(ParseError::new(
122 ParseErrorKind::Unexpected {
123 need: "trace/debug/info/warn/error",
124 got: Some(value),
125 },
126 loc,
127 )),
128 }
129 }
130}
131
132#[derive(Default, Debug, Clone, Copy)]
133pub enum LogAml {
134 #[default]
136 Off,
137 Normal,
139 Structured,
142}
143
144impl<'a> CmdlineParse<'a> for LogAml {
145 fn parse_cmdline(tokenizer: &mut Tokenizer<'a>) -> Result<'a, Self> {
146 let (loc, value) = tokenizer.next_value().ok_or_else(|| {
147 ParseError::new(
148 ParseErrorKind::Unexpected {
149 need: "off/normal/structured",
150 got: None,
151 },
152 tokenizer.current_index(),
153 )
154 })?;
155
156 match value {
157 "off" => Ok(Self::Off),
158 "normal" => Ok(Self::Normal),
159 "structured" => Ok(Self::Structured),
160 _ => Err(ParseError::new(
161 ParseErrorKind::Unexpected {
162 need: "off/normal/structured",
163 got: Some(value),
164 },
165 loc,
166 )),
167 }
168 }
169}