mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-30 00:23:24 +00:00
Move kcmdline parsing out of OSTD
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
397ce9652f
commit
5ea366bced
@ -40,14 +40,10 @@ fn parse_kernel_commandline(mb1_info: &MultibootLegacyInfo) -> &str {
|
||||
if mb1_info.cmdline != 0 {
|
||||
// SAFETY: the command line is C-style zero-terminated string.
|
||||
unsafe {
|
||||
let cstr = paddr_to_vaddr(mb1_info.cmdline as usize) as *const u8;
|
||||
let mut len = 0;
|
||||
while cstr.add(len).read() != 0 {
|
||||
len += 1;
|
||||
}
|
||||
let cstr = paddr_to_vaddr(mb1_info.cmdline as usize) as *const i8;
|
||||
let cstr = core::ffi::CStr::from_ptr(cstr);
|
||||
|
||||
cmdline = core::str::from_utf8(core::slice::from_raw_parts(cstr, len))
|
||||
.expect("cmdline is not a utf-8 string");
|
||||
cmdline = cstr.to_str().unwrap();
|
||||
}
|
||||
}
|
||||
cmdline
|
||||
|
@ -1,185 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#![allow(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:
|
||||
//!
|
||||
//! <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,
|
||||
};
|
||||
|
||||
use crate::early_println;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct InitprocArgs {
|
||||
path: Option<String>,
|
||||
argv: Vec<CString>,
|
||||
envp: Vec<CString>,
|
||||
}
|
||||
|
||||
/// Kernel module arguments
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum ModuleArg {
|
||||
/// A string argument
|
||||
Arg(CString),
|
||||
/// A key-value argument
|
||||
KeyVal(CString, CString),
|
||||
}
|
||||
|
||||
/// The struct to store the parsed kernel command-line arguments.
|
||||
#[derive(Debug)]
|
||||
pub struct KCmdlineArg {
|
||||
initproc: InitprocArgs,
|
||||
module_args: BTreeMap<String, Vec<ModuleArg>>,
|
||||
}
|
||||
|
||||
// Define get APIs.
|
||||
impl KCmdlineArg {
|
||||
/// Gets the path of the initprocess.
|
||||
pub fn get_initproc_path(&self) -> Option<&str> {
|
||||
self.initproc.path.as_deref()
|
||||
}
|
||||
/// Gets the argument vector(argv) of the initprocess.
|
||||
pub fn get_initproc_argv(&self) -> &Vec<CString> {
|
||||
&self.initproc.argv
|
||||
}
|
||||
/// Gets the environment vector(envp) of the initprocess.
|
||||
pub fn get_initproc_envp(&self) -> &Vec<CString> {
|
||||
&self.initproc.envp
|
||||
}
|
||||
/// Gets the argument vector of a kernel module.
|
||||
pub fn get_module_args(&self, module: &str) -> Option<&Vec<ModuleArg>> {
|
||||
self.module_args.get(module)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 Backus–Naur 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 {
|
||||
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
|
||||
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])),
|
||||
_ => {
|
||||
early_println!(
|
||||
"[KCmdline] Unable to parse kernel argument {}, skip for now",
|
||||
arg
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
// Entry => Module "." ModuleOptionName | KernelOptionName
|
||||
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]),
|
||||
_ => {
|
||||
early_println!(
|
||||
"[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 {
|
||||
ModuleArg::KeyVal(
|
||||
CString::new(option.to_string()).unwrap(),
|
||||
CString::new(v).unwrap(),
|
||||
)
|
||||
} else {
|
||||
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.
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
@ -6,7 +6,6 @@
|
||||
//! 2. the routine booting into the actual kernel;
|
||||
//! 3. the routine booting the other processors in the SMP context.
|
||||
|
||||
pub mod kcmdline;
|
||||
pub mod memory_region;
|
||||
pub mod smp;
|
||||
|
||||
@ -15,7 +14,6 @@ use alloc::{
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use kcmdline::KCmdlineArg;
|
||||
use memory_region::{MemoryRegion, MemoryRegionArray};
|
||||
use spin::Once;
|
||||
|
||||
@ -24,7 +22,7 @@ pub struct BootInfo {
|
||||
/// The name of the bootloader.
|
||||
pub bootloader_name: String,
|
||||
/// The kernel command line arguments.
|
||||
pub kernel_cmdline: KCmdlineArg,
|
||||
pub kernel_cmdline: String,
|
||||
/// The initial ramfs raw bytes.
|
||||
pub initramfs: Option<&'static [u8]>,
|
||||
/// The framebuffer arguments.
|
||||
@ -100,7 +98,7 @@ pub(crate) fn init() {
|
||||
|
||||
INFO.call_once(|| BootInfo {
|
||||
bootloader_name: boot_time_info.bootloader_name.to_string(),
|
||||
kernel_cmdline: boot_time_info.kernel_cmdline.into(),
|
||||
kernel_cmdline: boot_time_info.kernel_cmdline.to_string(),
|
||||
initramfs: boot_time_info.initramfs,
|
||||
framebuffer_arg: boot_time_info.framebuffer_arg,
|
||||
memory_regions: boot_time_info.memory_regions.to_vec(),
|
||||
|
@ -12,7 +12,7 @@ use core::str::FromStr;
|
||||
use log::{LevelFilter, Metadata, Record};
|
||||
use spin::Once;
|
||||
|
||||
use crate::boot::{boot_info, kcmdline::ModuleArg};
|
||||
use crate::boot::BOOT_TIME_INFO;
|
||||
|
||||
static LOGGER: Logger = Logger::new();
|
||||
|
||||
@ -82,16 +82,15 @@ pub(crate) fn init() {
|
||||
}
|
||||
|
||||
fn get_log_level() -> Option<LevelFilter> {
|
||||
let module_args = boot_info().kernel_cmdline.get_module_args("ostd")?;
|
||||
let kcmdline = BOOT_TIME_INFO.get().unwrap().kernel_cmdline;
|
||||
|
||||
// Although OSTD is agnostic of the parsing of the kernel command line,
|
||||
// the logger assumes that it follows the Linux kernel command line format.
|
||||
// We search for the `ostd.log_level=ARGUMENT` pattern in string.
|
||||
let value = kcmdline
|
||||
.split(' ')
|
||||
.find(|arg| arg.starts_with("ostd.log_level="))
|
||||
.map(|arg| arg.split('=').last().unwrap_or_default())?;
|
||||
|
||||
let value = {
|
||||
let value = module_args.iter().find_map(|arg| match arg {
|
||||
ModuleArg::KeyVal(name, value) if name.as_bytes() == "log_level".as_bytes() => {
|
||||
Some(value)
|
||||
}
|
||||
_ => None,
|
||||
})?;
|
||||
value.as_c_str().to_str().ok()?
|
||||
};
|
||||
LevelFilter::from_str(value).ok()
|
||||
}
|
||||
|
Reference in New Issue
Block a user