mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-28 03:43:23 +00:00
Implement kernel command line with init configuring
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
69cbfc72f5
commit
807c8355c0
26
Cargo.lock
generated
26
Cargo.lock
generated
@ -531,6 +531,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"multiboot2",
|
"multiboot2",
|
||||||
"pod",
|
"pod",
|
||||||
|
"regex",
|
||||||
"spin 0.9.8",
|
"spin 0.9.8",
|
||||||
"trapframe",
|
"trapframe",
|
||||||
"volatile",
|
"volatile",
|
||||||
@ -979,6 +980,31 @@ dependencies = [
|
|||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
|
||||||
|
dependencies = [
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ringbuf"
|
name = "ringbuf"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -2,7 +2,7 @@ set timeout_style=menu
|
|||||||
set timeout=1
|
set timeout=1
|
||||||
|
|
||||||
menuentry 'jinux' {
|
menuentry 'jinux' {
|
||||||
multiboot2 /boot/jinux
|
multiboot2 /boot/jinux SHELL="/bin/sh" LOGNAME="root" HOME="/" USER="root" PATH="/bin" init=/usr/bin/busybox -- sh -l
|
||||||
module2 --nounzip /boot/ramdisk.cpio.gz
|
module2 --nounzip /boot/ramdisk.cpio.gz
|
||||||
boot
|
boot
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,13 @@ log = "0.4"
|
|||||||
lazy_static = { version = "1.0", features = ["spin_no_std"] }
|
lazy_static = { version = "1.0", features = ["spin_no_std"] }
|
||||||
trapframe = { git = "https://github.com/sdww0/trapframe-rs", rev = "e886763" }
|
trapframe = { git = "https://github.com/sdww0/trapframe-rs", rev = "e886763" }
|
||||||
inherit-methods-macro = { git = "https://github.com/jinzhao-dev/inherit-methods-macro", rev = "98f7e3e" }
|
inherit-methods-macro = { git = "https://github.com/jinzhao-dev/inherit-methods-macro", rev = "98f7e3e" }
|
||||||
multiboot2 = "0.16.0"
|
regex = { version = "1.9.1", default-features = false, features = ["unicode"] }
|
||||||
|
|
||||||
[target.x86_64-custom.dependencies]
|
[target.x86_64-custom.dependencies]
|
||||||
x86_64 = "0.14.2"
|
x86_64 = "0.14.2"
|
||||||
x86 = "0.52.0"
|
x86 = "0.52.0"
|
||||||
acpi = "4.1.1"
|
acpi = "4.1.1"
|
||||||
aml = "0.16.3"
|
aml = "0.16.3"
|
||||||
|
multiboot2 = { version = "0.16.0", features = [] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
use alloc::{string::{ToString, String}, vec::Vec};
|
use alloc::{
|
||||||
|
string::{String, ToString},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
use multiboot2::{BootInformation, BootInformationHeader, MemoryAreaType};
|
use multiboot2::{BootInformation, BootInformationHeader, MemoryAreaType};
|
||||||
|
|
||||||
use crate::boot::{
|
use crate::boot::{
|
||||||
|
kcmdline::KCmdlineArg,
|
||||||
memory_region::{MemoryRegion, MemoryRegionType},
|
memory_region::{MemoryRegion, MemoryRegionType},
|
||||||
kcmdline::KCmdlineArg, BootloaderAcpiArg, BootloaderFramebufferArg,
|
BootloaderAcpiArg, BootloaderFramebufferArg,
|
||||||
};
|
};
|
||||||
use core::{arch::global_asm, mem::swap};
|
use core::{arch::global_asm, mem::swap};
|
||||||
use spin::Once;
|
use spin::Once;
|
||||||
@ -35,9 +39,9 @@ fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
|
|||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.command_line_tag()
|
.command_line_tag()
|
||||||
.expect("Kernel commandline not found from the Multiboot2 header!")
|
.expect("Kernel command-line not found from the Multiboot2 header!")
|
||||||
.cmdline()
|
.cmdline()
|
||||||
.expect("UTF-8 error: failed to parse kernel commandline!")
|
.expect("UTF-8 error: failed to parse kernel command-line!")
|
||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,189 @@
|
|||||||
//! The module to parse kernel commandline arguments.
|
//! The module to parse kernel command-line arguments.
|
||||||
//!
|
//!
|
||||||
|
//! The format of the Jinux 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::string::String;
|
use alloc::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
ffi::CString,
|
||||||
|
string::{String, ToString},
|
||||||
|
vec,
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use log::debug;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
/// The struct to store parsed kernel commandline arguments.
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct InitprocArg {
|
||||||
|
// Since environment arguments can precede the init path argument, we
|
||||||
|
// have no choice but to wrap the path in `Option` and check it later.
|
||||||
|
path: Option<String>,
|
||||||
|
argv: Vec<CString>,
|
||||||
|
envp: Vec<CString>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The struct to store the parsed kernel command-line arguments.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct KCmdlineArg {
|
pub struct KCmdlineArg {
|
||||||
initproc: Option<String>,
|
initproc: Option<InitprocArg>,
|
||||||
|
module_args: BTreeMap<String, Vec<CString>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define get APIs.
|
// Define get APIs.
|
||||||
impl KCmdlineArg {
|
impl KCmdlineArg {
|
||||||
pub fn get_initproc(&self) -> Option<&str> {
|
/// Get the path of the initprocess.
|
||||||
self.initproc.as_deref()
|
pub fn get_initproc_path(&self) -> Option<&str> {
|
||||||
|
self.initproc
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|i| i.path.as_ref())
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
}
|
||||||
|
/// Get the argument vector(argv) of the initprocess.
|
||||||
|
pub fn get_initproc_argv(&self) -> Option<&Vec<CString>> {
|
||||||
|
self.initproc.as_ref().map(|i| &i.argv)
|
||||||
|
}
|
||||||
|
/// Get the environment vector(envp) of the initprocess.
|
||||||
|
pub fn get_initproc_envp(&self) -> Option<&Vec<CString>> {
|
||||||
|
self.initproc.as_ref().map(|i| &i.argv)
|
||||||
|
}
|
||||||
|
/// Get the argument vector of a kernel module.
|
||||||
|
pub fn get_module_args(&self, module: &str) -> Option<&Vec<CString>> {
|
||||||
|
self.module_args.get(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the way to parse a string to `KCmdlineArg`.
|
// Define the way to parse a string to `KCmdlineArg`.
|
||||||
impl From<&str> for KCmdlineArg {
|
impl From<&str> for KCmdlineArg {
|
||||||
fn from(cmdline: &str) -> Self {
|
fn from(cmdline: &str) -> Self {
|
||||||
KCmdlineArg { initproc: None }
|
// What we construct.
|
||||||
|
let mut result = KCmdlineArg {
|
||||||
|
initproc: None,
|
||||||
|
module_args: BTreeMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Split the command line string by spaces but preserve
|
||||||
|
// ones that are protected by double quotes(`"`).
|
||||||
|
let re = Regex::new(r#"((\S*"[^"]*"\S*)+|\S+)"#).unwrap();
|
||||||
|
// 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 re.find_iter(cmdline).map(|m| m.as_str()) {
|
||||||
|
if arg == "" || arg == " " {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Cmdline => KernelArg "--" InitArg
|
||||||
|
// KernelArg => Arg "\s+" KernelArg | %empty
|
||||||
|
// InitArg => Arg "\s+" InitArg | %empty
|
||||||
|
if kcmdline_end {
|
||||||
|
if let Some(&mut ref mut i) = result.initproc.as_mut() {
|
||||||
|
i.argv.push(CString::new(arg).unwrap());
|
||||||
|
} else {
|
||||||
|
panic!("Initproc arguments provided but no initproc path specified!");
|
||||||
|
}
|
||||||
|
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])),
|
||||||
|
_ => {
|
||||||
|
panic!("Unable to parse argument {}", arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 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]),
|
||||||
|
_ => {
|
||||||
|
panic!("Unable to parse entry {} in argument {}", entry, arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(modname) = node {
|
||||||
|
let modarg = if let Some(v) = value {
|
||||||
|
CString::new(option.to_string() + "=" + v).unwrap()
|
||||||
|
} else {
|
||||||
|
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(&mut ref mut i) = result.initproc.as_mut() {
|
||||||
|
if let Some(v) = &i.path {
|
||||||
|
panic!("Initproc assigned twice in the command line!");
|
||||||
|
}
|
||||||
|
i.path = Some(value.to_string());
|
||||||
|
} else {
|
||||||
|
result.initproc = Some(InitprocArg {
|
||||||
|
path: Some(value.to_string()),
|
||||||
|
argv: Vec::new(),
|
||||||
|
envp: Vec::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// 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();
|
||||||
|
if let Some(&mut ref mut i) = result.initproc.as_mut() {
|
||||||
|
i.envp.push(envp_entry);
|
||||||
|
} else {
|
||||||
|
result.initproc = Some(InitprocArg {
|
||||||
|
path: None,
|
||||||
|
argv: Vec::new(),
|
||||||
|
envp: vec![envp_entry],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// There is no value, the entry is only a option.
|
||||||
|
match 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();
|
||||||
|
if let Some(&mut ref mut i) = result.initproc.as_mut() {
|
||||||
|
i.argv.push(argv_entry);
|
||||||
|
} else {
|
||||||
|
result.initproc = Some(InitprocArg {
|
||||||
|
path: None,
|
||||||
|
argv: vec![argv_entry],
|
||||||
|
envp: Vec::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("{:?}", result);
|
||||||
|
|
||||||
|
if let Some(&ref i) = result.initproc.as_ref() {
|
||||||
|
if i.path == None {
|
||||||
|
panic!("Initproc arguments provided but no initproc! Maybe have bad option.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! The architecture-independent boot module, which provides a universal interface
|
//! The architecture-independent boot module, which provides a universal interface
|
||||||
//! from the bootloader to the rest of the framework.
|
//! from the bootloader to the rest of the framework.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::arch::boot::init_boot_args;
|
use crate::arch::boot::init_boot_args;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user