asterinas/kernel/src/kcmdline.rs

184 lines
6.2 KiB
Rust
Raw Normal View History

2024-01-03 03:22:36 +00:00
// SPDX-License-Identifier: MPL-2.0
#![expect(unused_variables)]
//! The module to parse kernel command-line arguments.
//!
//! The format of the Asterinas command line string conforms
//! to the Linux kernel command line rules:
//!
2024-03-15 02:27:14 +00:00
//! <https://www.kernel.org/doc/html/v6.4/admin-guide/kernel-parameters.html>
//!
use alloc::{
collections::BTreeMap,
ffi::CString,
string::{String, ToString},
vec,
vec::Vec,
};
#[derive(PartialEq, Debug)]
struct InitprocArgs {
path: Option<String>,
argv: Vec<CString>,
envp: Vec<CString>,
}
2024-05-23 08:55:39 +00:00
/// Kernel module arguments
2023-11-08 15:59:23 +00:00
#[derive(PartialEq, Debug, Clone)]
pub enum ModuleArg {
2024-05-23 08:55:39 +00:00
/// A string argument
2023-11-08 15:59:23 +00:00
Arg(CString),
2024-05-23 08:55:39 +00:00
/// A key-value argument
2023-11-08 15:59:23 +00:00
KeyVal(CString, CString),
}
/// The struct to store the parsed kernel command-line arguments.
#[derive(Debug)]
pub struct KCmdlineArg {
initproc: InitprocArgs,
2023-11-08 15:59:23 +00:00
module_args: BTreeMap<String, Vec<ModuleArg>>,
}
// Define get APIs.
impl KCmdlineArg {
2024-05-30 11:25:58 +00:00
/// Gets the path of the initprocess.
pub fn get_initproc_path(&self) -> Option<&str> {
2023-09-04 03:04:42 +00:00
self.initproc.path.as_deref()
}
2024-05-30 11:25:58 +00:00
/// Gets the argument vector(argv) of the initprocess.
pub fn get_initproc_argv(&self) -> &Vec<CString> {
&self.initproc.argv
}
2024-05-30 11:25:58 +00:00
/// Gets the environment vector(envp) of the initprocess.
pub fn get_initproc_envp(&self) -> &Vec<CString> {
&self.initproc.envp
}
2024-05-30 11:25:58 +00:00
/// Gets the argument vector of a kernel module.
2023-11-08 15:59:23 +00:00
pub fn get_module_args(&self, module: &str) -> Option<&Vec<ModuleArg>> {
self.module_args.get(module)
}
}
2024-05-30 11:25:58 +00:00
// Splits the command line string by spaces but preserve
// ones that are protected by double quotes(`"`).
fn split_arg(input: &str) -> impl Iterator<Item = &str> {
let mut inside_quotes = false;
input.split(move |c: char| {
if c == '"' {
inside_quotes = !inside_quotes;
}
!inside_quotes && c.is_whitespace()
})
}
// Define the way to parse a string to `KCmdlineArg`.
impl From<&str> for KCmdlineArg {
fn from(cmdline: &str) -> Self {
// What we construct.
let mut result: KCmdlineArg = KCmdlineArg {
initproc: InitprocArgs {
path: None,
argv: Vec::new(),
envp: Vec::new(),
},
module_args: BTreeMap::new(),
};
// Every thing after the "--" mark is the initproc arguments.
let mut kcmdline_end = false;
// The main parse loop. The processing steps are arranged (not very strictly)
// by the analysis over the BackusNaur form syntax tree.
for arg in split_arg(cmdline) {
// Cmdline => KernelArg "--" InitArg
// KernelArg => Arg "\s+" KernelArg | %empty
// InitArg => Arg "\s+" InitArg | %empty
if kcmdline_end {
2023-09-04 03:04:42 +00:00
if result.initproc.path.is_none() {
panic!("Initproc arguments provided but no initproc path specified!");
}
result.initproc.argv.push(CString::new(arg).unwrap());
continue;
}
if arg == "--" {
kcmdline_end = true;
continue;
}
// Arg => Entry | Entry "=" Value
2023-09-04 03:04:42 +00:00
let arg_pattern: Vec<_> = arg.split('=').collect();
let (entry, value) = match arg_pattern.len() {
1 => (arg_pattern[0], None),
2 => (arg_pattern[0], Some(arg_pattern[1])),
_ => {
2024-12-31 06:29:49 +00:00
log::warn!(
"[KCmdline] Unable to parse kernel argument {}, skip for now",
arg
);
continue;
}
};
// Entry => Module "." ModuleOptionName | KernelOptionName
2023-09-04 03:04:42 +00:00
let entry_pattern: Vec<_> = entry.split('.').collect();
let (node, option) = match entry_pattern.len() {
1 => (None, entry_pattern[0]),
2 => (Some(entry_pattern[0]), entry_pattern[1]),
_ => {
2024-12-31 06:29:49 +00:00
log::warn!(
"[KCmdline] Unable to parse entry {} in argument {}, skip for now",
entry,
arg
);
continue;
}
};
if let Some(modname) = node {
let modarg = if let Some(v) = value {
2023-11-08 15:59:23 +00:00
ModuleArg::KeyVal(
CString::new(option.to_string()).unwrap(),
CString::new(v).unwrap(),
)
} else {
2023-11-08 15:59:23 +00:00
ModuleArg::Arg(CString::new(option).unwrap())
};
result
.module_args
.entry(modname.to_string())
.and_modify(|v| v.push(modarg.clone()))
.or_insert(vec![modarg.clone()]);
continue;
}
// KernelOptionName => /*literal string alternatives*/ | /*init environment*/
if let Some(value) = value {
// The option has a value.
match option {
"init" => {
if let Some(v) = &result.initproc.path {
panic!("Initproc assigned twice in the command line!");
}
result.initproc.path = Some(value.to_string());
}
_ => {
// If the option is not recognized, it is passed to the initproc.
// Pattern 'option=value' is treated as the init environment.
let envp_entry = CString::new(option.to_string() + "=" + value).unwrap();
result.initproc.envp.push(envp_entry);
}
}
} else {
// There is no value, the entry is only a option.
2023-09-04 03:04:42 +00:00
// If the option is not recognized, it is passed to the initproc.
// Pattern 'option' without value is treated as the init argument.
let argv_entry = CString::new(option.to_string()).unwrap();
result.initproc.argv.push(argv_entry);
}
}
result
}
}