diff --git a/docs/kernel/boot/cmdline.md b/docs/kernel/boot/cmdline.md new file mode 100644 index 00000000..16b42614 --- /dev/null +++ b/docs/kernel/boot/cmdline.md @@ -0,0 +1,109 @@ +# 内核启动命令行参数 + +:::{note} +本文作者: +- 龙进 +::: + +## 概述 + +  DragonOS内核启动命令行参数解析模块旨在提供类似Linux的内核启动命令行参数解析支持,以便更灵活地让内核执行不同的行为。该模块允许内核在启动时接收并解析命令行参数,根据参数的不同类型执行相应的回调函数或设置环境变量。 + +:::{note} +暂时不支持设置回调函数 +::: + +## 设计方案 + + +### 参数类型 + +内核启动命令行参数分为三种类型: + +- Arg类型 +- KV类型 +- EarlyKV类型 + +#### Arg类型 + +Arg类型的参数在命令行中只有名称,没有值。分为以下两种类型: + +- ArgNormal:默认值为`false`,如果命令行中包含这个参数,则会设置为`true`。 +- ArgInv:默认值为`true`,如果命令行中包含这个参数,则会设置为`false`。 + +#### KV类型 + +KV类型的参数在命令行中表现为`name=value`,`value`按照逗号分隔。内核模块可提供参数的默认值。 + +#### EarlyKV类型 + +EarlyKV类型的参数与KV类型类似,但它们在内存管理初始化之前被解析。 + +### Module标志 + +Module标志类似于`usbprobe.xxxx`。 + +### 参数声明 +提供宏来声明内核命令行参数。 +### procfs支持 + +:::{note} +TODO: 在`/proc/cmdline`下显示当前内核的启动命令行参数。 +::: + +## 声明内核启动命令行参数的宏 + +### Arg类型参数声明 +```rust +kernel_cmdline_param_arg!(varname, name, default_bool, inv); +``` +- `varname`:参数的变量名 +- `name`:参数的名称 +- `default_bool`:默认值 +- `inv`:是否反转 + +### KV类型参数声明 + +```rust +kernel_cmdline_param_kv!(varname, name, default_str); +``` + +- `varname`:参数的变量名 +- `name`:参数的名称 +- `default_str`:默认值 + +### 内存管理初始化之前的KV类型参数声明 + +```rust +kernel_cmdline_param_early_kv!(varname, name, default_str); +``` + +- `varname`:参数的变量名 +- `name`:参数的名称 +- `default_str`:默认值 + +## 示例 + +以下示例展示了如何声明和使用KV类型参数: +```rust +kernel_cmdline_param_kv!(ROOTFS_PATH_PARAM, root, ""); +if let Some(rootfs_dev_path) = ROOTFS_PATH_PARAM.value_str() { + ....... +} else { + ....... +}; +``` + +### 使用方式 + +1. 在内核代码中,使用`kernel_cmdline_param_kv!`宏声明所需的KV类型参数。 +2. 在内核初始化过程中,通过参数的`value_str()`或者`value_bool()`方法获取参数值。 +3. 根据参数值执行相应的操作。 + +通过以上步骤,开发者可以灵活地使用内核启动命令行参数来控制内核行为。 + + +## TODO + +- 支持在`/proc/cmdline`下显示当前内核的启动命令行参数。(需要在procfs重构后) +- 支持设置回调函数,调用回调函数来设置参数值 diff --git a/docs/kernel/boot/index.rst b/docs/kernel/boot/index.rst index 006564bf..25d6b66e 100644 --- a/docs/kernel/boot/index.rst +++ b/docs/kernel/boot/index.rst @@ -1,10 +1,9 @@ 引导加载 ==================================== - - DragonOS采用GRUB2作为其引导加载程序,支持Multiboot2协议引导。目前仅支持GRUB2.06版本。 .. toctree:: :maxdepth: 1 :caption: 目录 bootloader + cmdline diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 3156e114..18781dbd 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -41,6 +41,7 @@ fdt = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/fdt", rev hashbrown = "=0.13.2" ida = { path = "crates/ida" } intertrait = { path = "crates/intertrait" } +kcmdline_macros = { path = "crates/kcmdline_macros" } kdepends = { path = "crates/kdepends" } klog_types = { path = "crates/klog_types" } linkme = "=0.3.27" diff --git a/kernel/crates/kcmdline_macros/Cargo.toml b/kernel/crates/kcmdline_macros/Cargo.toml new file mode 100644 index 00000000..e39f16b6 --- /dev/null +++ b/kernel/crates/kcmdline_macros/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "kcmdline_macros" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/kernel/crates/kcmdline_macros/src/lib.rs b/kernel/crates/kcmdline_macros/src/lib.rs new file mode 100644 index 00000000..ff4fe32c --- /dev/null +++ b/kernel/crates/kcmdline_macros/src/lib.rs @@ -0,0 +1,74 @@ +#![no_std] +#![deny(clippy::all)] +#![allow(clippy::crate_in_macro_def)] + +/// 定义一个bool类型的参数 +/// +/// # 参数 +/// +/// - `$varname`: 参数的变量名 +/// - `$name`: 参数的名称 +/// - `$default_bool`: 默认值 +/// - `$inv`: 是否反转 +#[macro_export] +macro_rules! kernel_cmdline_param_arg { + ($varname:ident, $name:ident, $default_bool:expr, $inv:expr) => { + #[::linkme::distributed_slice(crate::init::cmdline::KCMDLINE_PARAM_ARG)] + static $varname: crate::init::cmdline::KernelCmdlineParameter = + crate::init::cmdline::KernelCmdlineParamBuilder::new( + stringify!($name), + crate::init::cmdline::KCmdlineParamType::Arg, + ) + .default_bool($default_bool) + .inv($inv) + .build() + .unwrap(); + }; +} + +/// 定义一个key-value类型的参数 +/// +/// # 参数 +/// - `$varname`: 参数的变量名 +/// - `$name`: 参数的名称 +/// - `$default_str`: 默认值 +#[macro_export] +macro_rules! kernel_cmdline_param_kv { + ($varname:ident, $name:ident, $default_str:expr) => { + #[::linkme::distributed_slice(crate::init::cmdline::KCMDLINE_PARAM_KV)] + static $varname: crate::init::cmdline::KernelCmdlineParameter = + crate::init::cmdline::KernelCmdlineParamBuilder::new( + stringify!($name), + crate::init::cmdline::KCmdlineParamType::KV, + ) + .default_str($default_str) + .build() + .unwrap(); + }; +} + +/// 定义一个内存管理初始化之前就要设置的key-value类型的参数 +/// +/// # 参数 +/// - `$varname`: 参数的变量名 +/// - `$name`: 参数的名称 +/// - `$default_str`: 默认值 +#[macro_export] +macro_rules! kernel_cmdline_param_early_kv { + ($varname:ident, $name:ident, $default_str:expr) => { + #[::linkme::distributed_slice(crate::init::cmdline::KCMDLINE_PARAM_EARLY_KV)] + static $varname: crate::init::cmdline::KernelCmdlineParameter = { + static ___KV: crate::init::cmdline::KernelCmdlineEarlyKV = { + const { assert!($default_str.len() < KernelCmdlineEarlyKV::VALUE_MAX_LEN) }; + crate::init::cmdline::KernelCmdlineParamBuilder::new( + stringify!($name), + crate::init::cmdline::KCmdlineParamType::EarlyKV, + ) + .default_str($default_str) + .build_early_kv() + .unwrap() + }; + crate::init::cmdline::KernelCmdlineParameter::EarlyKV(&___KV) + }; + }; +} diff --git a/kernel/src/driver/tty/tty_core.rs b/kernel/src/driver/tty/tty_core.rs index d6dc8305..93f0e9c4 100644 --- a/kernel/src/driver/tty/tty_core.rs +++ b/kernel/src/driver/tty/tty_core.rs @@ -103,7 +103,7 @@ impl TtyCore { self.line_discipline.clone() } - pub fn write_without_serial(&self, buf: &[u8], nr: usize) -> Result { + pub fn write_to_core(&self, buf: &[u8], nr: usize) -> Result { self.core .driver() .driver_funcs() diff --git a/kernel/src/filesystem/vfs/core.rs b/kernel/src/filesystem/vfs/core.rs index 41984a71..f52470f6 100644 --- a/kernel/src/filesystem/vfs/core.rs +++ b/kernel/src/filesystem/vfs/core.rs @@ -5,7 +5,7 @@ use log::{error, info}; use system_error::SystemError; use crate::{ - driver::base::block::manager::block_dev_manager, + driver::base::block::{gendisk::GenDisk, manager::block_dev_manager}, filesystem::{ devfs::devfs_init, fat::fs::FATFileSystem, @@ -28,6 +28,7 @@ use super::{ /// 当没有指定根文件系统时,尝试的根文件系统列表 const ROOTFS_TRY_LIST: [&str; 4] = ["/dev/sda1", "/dev/sda", "/dev/vda1", "/dev/vda"]; +kernel_cmdline_param_kv!(ROOTFS_PATH_PARAM, root, ""); /// @brief 原子地生成新的Inode号。 /// 请注意,所有的inode号都需要通过该函数来生成.全局的inode号,除了以下两个特殊的以外,都是唯一的 @@ -116,20 +117,26 @@ fn migrate_virtual_filesystem(new_fs: Arc) -> Result<(), SystemE return Ok(()); } +fn try_find_gendisk_as_rootfs(path: &str) -> Option> { + if let Some(gd) = block_dev_manager().lookup_gendisk_by_path(path) { + info!("Use {} as rootfs", path); + return Some(gd); + } + return None; +} + pub fn mount_root_fs() -> Result<(), SystemError> { info!("Try to mount root fs..."); block_dev_manager().print_gendisks(); - - let gendisk = ROOTFS_TRY_LIST - .iter() - .find_map(|&path| { - if let Some(gd) = block_dev_manager().lookup_gendisk_by_path(path) { - info!("Use {} as rootfs", path); - return Some(gd); - } - return None; - }) - .ok_or(SystemError::ENODEV)?; + let gendisk = if let Some(rootfs_dev_path) = ROOTFS_PATH_PARAM.value_str() { + try_find_gendisk_as_rootfs(rootfs_dev_path) + .unwrap_or_else(|| panic!("Failed to find rootfs device {}", rootfs_dev_path)) + } else { + ROOTFS_TRY_LIST + .iter() + .find_map(|&path| try_find_gendisk_as_rootfs(path)) + .ok_or(SystemError::ENODEV)? + }; let fatfs: Result, SystemError> = FATFileSystem::new(gendisk); if fatfs.is_err() { diff --git a/kernel/src/init/boot.rs b/kernel/src/init/boot.rs index 426ea1cc..b692e3fb 100644 --- a/kernel/src/init/boot.rs +++ b/kernel/src/init/boot.rs @@ -46,13 +46,20 @@ impl BootParams { /// 开机命令行参数字符串 pub fn boot_cmdline_str(&self) -> &str { - core::str::from_utf8(self.boot_cmdline()).unwrap() + core::str::from_utf8(&self.boot_cmdline()[..self.boot_cmdline_len()]).unwrap() } pub fn bootloader_name(&self) -> Option<&str> { self.bootloader_name.as_deref() } + pub fn boot_cmdline_len(&self) -> usize { + self.boot_command_line + .iter() + .position(|&x| x == 0) + .unwrap_or(self.boot_command_line.len()) + } + /// 追加开机命令行参数 /// /// 如果开机命令行参数已经满了,则不会追加。 @@ -149,13 +156,6 @@ pub fn boot_callbacks() -> &'static dyn BootCallbacks { } pub(super) fn boot_callback_except_early() { - boot_callbacks() - .init_kernel_cmdline() - .inspect_err(|e| { - log::error!("Failed to init kernel cmdline: {:?}", e); - }) - .ok(); - let mut boot_params = boot_params().write(); boot_params.bootloader_name = boot_callbacks() .init_bootloader_name() diff --git a/kernel/src/init/cmdline.rs b/kernel/src/init/cmdline.rs new file mode 100644 index 00000000..0d0ff040 --- /dev/null +++ b/kernel/src/init/cmdline.rs @@ -0,0 +1,476 @@ +use core::{ + str, + sync::atomic::{fence, Ordering}, +}; + +use alloc::{ffi::CString, vec::Vec}; + +use crate::libs::spinlock::SpinLock; + +use super::boot_params; + +#[::linkme::distributed_slice] +pub static KCMDLINE_PARAM_EARLY_KV: [KernelCmdlineParameter] = [..]; + +#[::linkme::distributed_slice] +pub static KCMDLINE_PARAM_KV: [KernelCmdlineParameter] = [..]; + +#[::linkme::distributed_slice] +pub static KCMDLINE_PARAM_ARG: [KernelCmdlineParameter] = [..]; + +static KERNEL_CMDLINE_PARAM_MANAGER: KernelCmdlineManager = KernelCmdlineManager::new(); + +#[inline(always)] +pub fn kenrel_cmdline_param_manager() -> &'static KernelCmdlineManager { + &KERNEL_CMDLINE_PARAM_MANAGER +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum KCmdlineParamType { + /// bool类型参数 + Arg, + /// key-value类型参数 + KV, + /// 内存管理初始化之前的KV参数 + EarlyKV, +} + +pub struct KernelCmdlineParamBuilder { + name: &'static str, + ty: KCmdlineParamType, + default_str: &'static str, + default_bool: bool, + inv: bool, +} + +impl KernelCmdlineParamBuilder { + pub const fn new(name: &'static str, ty: KCmdlineParamType) -> Self { + Self { + name, + ty, + default_str: "", + default_bool: false, + inv: false, + } + } + + pub const fn default_str(mut self, default_str: &'static str) -> Self { + self.default_str = default_str; + self + } + + pub const fn default_bool(mut self, default_bool: bool) -> Self { + self.default_bool = default_bool; + self + } + + pub const fn inv(mut self, inv: bool) -> Self { + self.inv = inv; + self + } + + pub const fn build_early_kv(self) -> Option { + if matches!(self.ty, KCmdlineParamType::EarlyKV) { + Some(KernelCmdlineEarlyKV { + name: self.name, + value: [0; KernelCmdlineEarlyKV::VALUE_MAX_LEN], + index: 0, + initialized: false, + default: self.default_str, + }) + } else { + None + } + } + + pub const fn build(self) -> Option { + match self.ty { + KCmdlineParamType::Arg => Some(KernelCmdlineParameter::Arg(KernelCmdlineArg { + name: self.name, + value: self.default_bool, + initialized: false, + inv: self.inv, + default: self.default_bool, + })), + KCmdlineParamType::KV => Some(KernelCmdlineParameter::KV(KernelCmdlineKV { + name: self.name, + value: None, + initialized: false, + default: self.default_str, + })), + _ => None, + } + } +} + +#[allow(dead_code)] +pub enum KernelCmdlineParameter { + Arg(KernelCmdlineArg), + KV(KernelCmdlineKV), + EarlyKV(&'static KernelCmdlineEarlyKV), +} + +impl KernelCmdlineParameter { + pub fn name(&self) -> &str { + match self { + KernelCmdlineParameter::Arg(v) => v.name, + KernelCmdlineParameter::KV(v) => v.name, + KernelCmdlineParameter::EarlyKV(v) => v.name, + } + } + + /// 获取bool类型参数的值 + pub fn value_bool(&self) -> Option { + match self { + KernelCmdlineParameter::Arg(v) => Some(v.value()), + _ => None, + } + } + + /// 获取key-value类型参数的值 + pub fn value_str(&self) -> Option<&str> { + match self { + KernelCmdlineParameter::Arg(_) => None, + KernelCmdlineParameter::KV(v) => v + .value + .as_ref() + .and_then(|v| str::from_utf8(v.as_bytes()).ok()), + KernelCmdlineParameter::EarlyKV(v) => v.value_str(), + } + } + + pub fn is_arg(&self) -> bool { + matches!(self, KernelCmdlineParameter::Arg(_)) + } + + pub fn is_kv(&self) -> bool { + matches!(self, KernelCmdlineParameter::KV(_)) + } + + pub fn is_early_kv(&self) -> bool { + matches!(self, KernelCmdlineParameter::EarlyKV(_)) + } + + /// 强行获取可变引用 + /// + /// # Safety + /// + /// 只能在内核初始化阶段pid0使用! + #[allow(clippy::mut_from_ref)] + unsafe fn force_mut(&self) -> &mut Self { + let p = self as *const Self as *mut Self; + p.as_mut().unwrap() + } +} + +#[derive(Debug)] +pub struct KernelCmdlineArg { + name: &'static str, + value: bool, + initialized: bool, + /// 是否反转 + inv: bool, + default: bool, +} + +impl KernelCmdlineArg { + pub fn value(&self) -> bool { + volatile_read!(self.value) + } +} + +pub struct KernelCmdlineKV { + name: &'static str, + value: Option, + initialized: bool, + default: &'static str, +} + +/// 在内存管理初始化之前的KV参数 +pub struct KernelCmdlineEarlyKV { + name: &'static str, + value: [u8; Self::VALUE_MAX_LEN], + index: usize, + initialized: bool, + default: &'static str, +} + +impl KernelCmdlineEarlyKV { + pub const VALUE_MAX_LEN: usize = 256; + + pub fn value(&self) -> &[u8] { + &self.value[..self.index] + } + + pub fn value_str(&self) -> Option<&str> { + core::str::from_utf8(&self.value[..self.index]).ok() + } + + /// 强行获取可变引用 + /// + /// # Safety + /// + /// 只能在内核初始化阶段pid0使用! + #[allow(clippy::mut_from_ref)] + unsafe fn force_mut(&self) -> &mut Self { + let p = self as *const Self as *mut Self; + p.as_mut().unwrap() + } +} + +pub struct KernelCmdlineManager { + inner: SpinLock, +} + +pub(super) struct InnerKernelCmdlineManager { + /// init进程的路径 + init_path: Option, + init_args: Vec, + init_envs: Vec, +} + +impl KernelCmdlineManager { + const fn new() -> Self { + Self { + inner: SpinLock::new(InnerKernelCmdlineManager { + init_path: None, + init_args: Vec::new(), + init_envs: Vec::new(), + }), + } + } + + pub(super) fn init_proc_path(&self) -> Option { + self.inner.lock().init_path.clone() + } + + pub(super) fn init_proc_args(&self) -> Vec { + self.inner.lock().init_args.clone() + } + + pub(super) fn init_proc_envs(&self) -> Vec { + self.inner.lock().init_envs.clone() + } + + /// 在内存管理初始化之前设置部分参数 + pub fn early_init(&self) { + let boot_params = boot_params().read(); + + for argument in self.split_args(boot_params.boot_cmdline_str()) { + let (node, option, value) = match self.split_arg(argument) { + Some(v) => v, + None => continue, + }; + // 查找参数 + if let Some(param) = self.find_param(node, option, KCmdlineParamType::EarlyKV) { + let param = unsafe { param.force_mut() }; + match param { + KernelCmdlineParameter::EarlyKV(p) => { + let p = unsafe { p.force_mut() }; + if let Some(value) = value { + let value = value.as_bytes(); + let len = value.len().min(KernelCmdlineEarlyKV::VALUE_MAX_LEN); + p.value[..len].copy_from_slice(&value[..len]); + p.index = len; + } + p.initialized = true; + } + _ => unreachable!(), + } + fence(Ordering::SeqCst); + } + } + + // 初始化默认值 + KCMDLINE_PARAM_EARLY_KV.iter().for_each(|x| { + let x = unsafe { x.force_mut() }; + if let KernelCmdlineParameter::EarlyKV(v) = x { + if !v.initialized { + let v = unsafe { v.force_mut() }; + let len = v.default.len().min(KernelCmdlineEarlyKV::VALUE_MAX_LEN); + v.value[..len].copy_from_slice(v.default.as_bytes()); + v.index = len; + v.initialized = true; + } + } + }); + } + + /// 在内存管理初始化之后设置命令行参数 + pub fn init(&self) { + let mut inner = self.inner.lock(); + let boot_params = boot_params().read(); + // `--`以后的参数都是init进程的参数 + let mut kernel_cmdline_end = false; + for argument in self.split_args(boot_params.boot_cmdline_str()) { + if kernel_cmdline_end { + if inner.init_path.is_none() { + panic!("cmdline: init proc path is not set while init proc args are set"); + } + if !argument.is_empty() { + inner.init_args.push(CString::new(argument).unwrap()); + } + continue; + } + + if argument == "--" { + kernel_cmdline_end = true; + continue; + } + + let (node, option, value) = match self.split_arg(argument) { + Some(v) => v, + None => continue, + }; + if option == "init" && value.is_some() { + if inner.init_path.is_some() { + panic!("cmdline: init proc path is set twice"); + } + inner.init_path = Some(CString::new(value.unwrap()).unwrap()); + continue; + } + // log::debug!( + // "cmdline: node: {:?}, option: {:?}, value: {:?}", + // node, + // option, + // value + // ); + if let Some(param) = self.find_param(node, option, KCmdlineParamType::KV) { + let param = unsafe { param.force_mut() }; + match param { + KernelCmdlineParameter::KV(p) => { + if p.value.is_some() { + log::warn!("cmdline: parameter {} is set twice", p.name); + continue; + } + p.value = Some(CString::new(value.unwrap()).unwrap()); + p.initialized = true; + } + _ => unreachable!(), + } + fence(Ordering::SeqCst); + } else if let Some(param) = self.find_param(node, option, KCmdlineParamType::Arg) { + let param = unsafe { param.force_mut() }; + match param { + KernelCmdlineParameter::Arg(p) => { + if p.initialized { + log::warn!("cmdline: parameter {} is set twice", p.name); + continue; + } + p.value = !p.inv; + p.initialized = true; + } + _ => unreachable!(), + } + fence(Ordering::SeqCst); + } else if node.is_none() { + if let Some(val) = value { + inner + .init_envs + .push(CString::new(format!("{}={}", option, val)).unwrap()); + } else if !option.is_empty() { + inner.init_args.push(CString::new(option).unwrap()); + } + } + } + fence(Ordering::SeqCst); + // 初始化默认值 + self.default_initialize(); + fence(Ordering::SeqCst); + } + + fn default_initialize(&self) { + KCMDLINE_PARAM_ARG.iter().for_each(|x| { + let x = unsafe { x.force_mut() }; + if let KernelCmdlineParameter::Arg(v) = x { + if !v.initialized { + v.value = v.default; + v.initialized = true; + } + } + fence(Ordering::SeqCst); + }); + + KCMDLINE_PARAM_KV.iter().for_each(|x| { + let x = unsafe { x.force_mut() }; + if let KernelCmdlineParameter::KV(v) = x { + if !v.initialized { + v.value = Some(CString::new(v.default).unwrap()); + v.initialized = true; + } + } + fence(Ordering::SeqCst); + }); + } + + fn find_param( + &self, + node: Option<&str>, + option: &str, + param_typ: KCmdlineParamType, + ) -> Option<&KernelCmdlineParameter> { + let list = match param_typ { + KCmdlineParamType::Arg => &KCMDLINE_PARAM_ARG, + KCmdlineParamType::KV => &KCMDLINE_PARAM_KV, + KCmdlineParamType::EarlyKV => &KCMDLINE_PARAM_EARLY_KV, + }; + + list.iter().find(|x| { + let name = x.name(); + if let Some(node) = node { + // 加1是因为有一个点号 + name.len() == (node.len() + option.len() + 1) + && name.starts_with(node) + && name[node.len() + 1..].starts_with(option) + } else { + name == option + } + }) + } + + fn split_arg<'a>(&self, arg: &'a str) -> Option<(Option<&'a str>, &'a str, Option<&'a str>)> { + let mut iter = arg.splitn(2, '='); + let key = iter.next().unwrap(); + let value = iter.next(); + let value = value.map(|v| v.trim()); + if value.is_some() && iter.next().is_some() { + log::warn!("cmdline: invalid argument: {}", arg); + return None; + } + + let mut iter = key.splitn(2, '.'); + let v1 = iter.next().map(|v| v.trim()); + let v2 = iter.next().map(|v| v.trim()); + let v3 = iter.next().map(|v| v.trim()); + let v = [v1, v2, v3]; + + let mut key_split_len = 0; + v.iter().for_each(|x| { + if x.is_some() { + key_split_len += 1 + } + }); + + let (node, option) = match key_split_len { + 1 => (None, v[0].unwrap()), + 2 => (Some(v[0].unwrap()), v[1].unwrap()), + _ => { + log::warn!("cmdline: invalid argument: {}", arg); + return None; + } + }; + + Some((node, option, value)) + } + + fn split_args<'a>(&self, cmdline: &'a str) -> impl Iterator { + // 是否在引号内 + let mut in_quote = false; + cmdline.split(move |c: char| { + if c == '"' { + in_quote = !in_quote; + } + !in_quote && c.is_whitespace() + }) + } +} diff --git a/kernel/src/init/init.rs b/kernel/src/init/init.rs index a1d8aee8..953f0974 100644 --- a/kernel/src/init/init.rs +++ b/kernel/src/init/init.rs @@ -31,7 +31,10 @@ use crate::{ }, }; -use super::boot::boot_callback_except_early; +use super::{ + boot::{boot_callback_except_early, boot_callbacks}, + cmdline::kenrel_cmdline_param_manager, +}; /// The entry point for the kernel /// @@ -52,9 +55,7 @@ pub fn start_kernel() -> ! { #[inline(never)] fn do_start_kernel() { init_before_mem_init(); - early_init_logging(); - early_setup_arch().expect("setup_arch failed"); unsafe { mm_init() }; if scm_reinit().is_ok() { @@ -62,8 +63,10 @@ fn do_start_kernel() { warn!("Failed to init textui: {:?}", e); } } - + // 初始化内核命令行参数 + kenrel_cmdline_param_manager().init(); boot_callback_except_early(); + init_intertrait(); vfs_init().expect("vfs init failed"); @@ -99,4 +102,16 @@ fn init_before_mem_init() { serial_early_init().expect("serial early init failed"); let video_ok = unsafe { VideoRefreshManager::video_init().is_ok() }; scm_init(video_ok); + + early_init_logging(); + + early_setup_arch().expect("setup_arch failed"); + + boot_callbacks() + .init_kernel_cmdline() + .inspect_err(|e| { + log::error!("Failed to init kernel cmdline: {:?}", e); + }) + .ok(); + kenrel_cmdline_param_manager().early_init(); } diff --git a/kernel/src/init/initial_kthread.rs b/kernel/src/init/initial_kthread.rs index 31e2547c..0d71c8d4 100644 --- a/kernel/src/init/initial_kthread.rs +++ b/kernel/src/init/initial_kthread.rs @@ -11,12 +11,17 @@ use crate::{ driver::{net::e1000e::e1000e::e1000e_init, virtio::virtio::virtio_probe}, filesystem::vfs::core::mount_root_fs, net::net_core::net_init, - process::{kthread::KernelThreadMechanism, stdio::stdio_init, ProcessFlags, ProcessManager}, + process::{ + exec::ProcInitInfo, kthread::KernelThreadMechanism, stdio::stdio_init, ProcessFlags, + ProcessManager, + }, smp::smp_init, syscall::Syscall, }; -use super::initcall::do_initcalls; +use super::{cmdline::kenrel_cmdline_param_manager, initcall::do_initcalls}; + +const INIT_PROC_TRYLIST: [&str; 3] = ["/bin/dragonreach", "/bin/init", "/bin/sh"]; pub fn initial_kernel_thread() -> i32 { kernel_init().unwrap_or_else(|err| { @@ -69,39 +74,79 @@ fn switch_to_user() -> ! { *current_pcb.sched_info().sched_policy.write_irqsave() = crate::sched::SchedPolicy::CFS; drop(current_pcb); - let mut trap_frame = TrapFrame::new(); - // 逐个尝试运行init进程 - if try_to_run_init_process("/bin/dragonreach", &mut trap_frame).is_err() - && try_to_run_init_process("/bin/init", &mut trap_frame).is_err() - && try_to_run_init_process("/bin/sh", &mut trap_frame).is_err() - { - panic!("Failed to run init process: No working init found."); - } + let mut proc_init_info = ProcInitInfo::new(""); + proc_init_info.envs.push(CString::new("PATH=/").unwrap()); + proc_init_info.args = kenrel_cmdline_param_manager().init_proc_args(); + proc_init_info.envs = kenrel_cmdline_param_manager().init_proc_envs(); + let mut trap_frame = TrapFrame::new(); + + if let Some(path) = kenrel_cmdline_param_manager().init_proc_path() { + log::info!("Boot with specified init process: {:?}", path); + + try_to_run_init_process( + path.as_c_str().to_str().unwrap(), + &mut proc_init_info, + &mut trap_frame, + ) + .unwrap_or_else(|e| { + panic!( + "Failed to run specified init process: {:?}, err: {:?}", + path, e + ) + }); + } else { + let mut ok = false; + for path in INIT_PROC_TRYLIST.iter() { + if try_to_run_init_process(path, &mut proc_init_info, &mut trap_frame).is_ok() { + ok = true; + break; + } + } + if !ok { + panic!("Failed to run init process: No working init found."); + } + } + drop(proc_init_info); // 需要确保执行到这里之后,上面所有的资源都已经释放(比如arc之类的) compiler_fence(Ordering::SeqCst); unsafe { arch_switch_to_user(trap_frame) }; } -fn try_to_run_init_process(path: &str, trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - if let Err(e) = run_init_process(path, trap_frame) { +fn try_to_run_init_process( + path: &str, + proc_init_info: &mut ProcInitInfo, + trap_frame: &mut TrapFrame, +) -> Result<(), SystemError> { + proc_init_info.proc_name = CString::new(path).unwrap(); + proc_init_info.args.insert(0, CString::new(path).unwrap()); + if let Err(e) = run_init_process(proc_init_info, trap_frame) { if e != SystemError::ENOENT { error!( "Failed to run init process: {path} exists but couldn't execute it (error {:?})", e ); } + + proc_init_info.args.remove(0); return Err(e); } Ok(()) } -fn run_init_process(path: &str, trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - let argv = vec![CString::new(path).unwrap()]; - let envp = vec![CString::new("PATH=/").unwrap()]; - +fn run_init_process( + proc_init_info: &ProcInitInfo, + trap_frame: &mut TrapFrame, +) -> Result<(), SystemError> { compiler_fence(Ordering::SeqCst); - Syscall::do_execve(path.to_string(), argv, envp, trap_frame)?; + let path = proc_init_info.proc_name.to_str().unwrap(); + + Syscall::do_execve( + path.to_string(), + proc_init_info.args.clone(), + proc_init_info.envs.clone(), + trap_frame, + )?; Ok(()) } diff --git a/kernel/src/init/mod.rs b/kernel/src/init/mod.rs index 15e02bca..ee4f9942 100644 --- a/kernel/src/init/mod.rs +++ b/kernel/src/init/mod.rs @@ -2,6 +2,7 @@ use crate::libs::rwlock::RwLock; use self::boot::BootParams; pub mod boot; +pub mod cmdline; #[allow(clippy::module_inception)] pub mod init; pub mod initcall; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 974f1fdb..aa67c0a7 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -81,7 +81,8 @@ extern crate smoltcp; extern crate intertrait; #[cfg(target_arch = "x86_64")] extern crate x86; - +#[macro_use] +extern crate kcmdline_macros; extern crate klog_types; extern crate uefi; extern crate uefi_raw; diff --git a/kernel/src/libs/lib_ui/textui.rs b/kernel/src/libs/lib_ui/textui.rs index 75fca7ce..16e52397 100644 --- a/kernel/src/libs/lib_ui/textui.rs +++ b/kernel/src/libs/lib_ui/textui.rs @@ -1045,9 +1045,8 @@ pub extern "C" fn rs_textui_putchar(character: u8, fr_color: u32, bk_color: u32) let port = current_vc.port(); let tty = port.port_data().internal_tty(); if let Some(tty) = tty { - send_to_default_serial8250_port(&[character]); return tty - .write_without_serial(buf.as_bytes(), buf.len()) + .write_to_core(buf.as_bytes(), buf.len()) .map(|_| 0) .unwrap_or_else(|e| e.to_posix_errno()); } diff --git a/tools/write_disk_image.sh b/tools/write_disk_image.sh index 5aa915f6..27b43110 100644 --- a/tools/write_disk_image.sh +++ b/tools/write_disk_image.sh @@ -126,9 +126,8 @@ cfg_content='set timeout=15 set default=0 insmod efi_gop menuentry "DragonOS" { - multiboot2 /boot/kernel.elf "KERNEL_ELF" + multiboot2 /boot/kernel.elf init=/bin/dragonreach }' - # 增加insmod efi_gop防止32位uefi启动报错 echo "echo '${cfg_content}' > ${boot_folder}/grub/grub.cfg" | sh fi