Просмотр исходного кода

feat(boot): 内核启动命令行参数解析 (#969)

支持解析启动命令行参数,行为与Linux一致。具体见文档。

bbs链接:https://bbs.dragonos.org.cn/t/topic/362
issue: https://github.com/DragonOS-Community/DragonOS/issues/865

支持了三种参数:
- Arg (不带Value的参数)
- KV (正常的KV参数)
- EarlyKV (在内存管理初始化之前解析)

# TODO

- 支持在`/proc/cmdline`下面查看内核启动时的命令行参数。
- 支持回调函数,允许更加灵活的设置参数的值(目前用不到,就没写了)

Signed-off-by: longjin <[email protected]>
LoGin 5 месяцев назад
Родитель
Сommit
f9fe30be89

+ 109 - 0
docs/kernel/boot/cmdline.md

@@ -0,0 +1,109 @@
+# 内核启动命令行参数
+
+:::{note}
+本文作者: 
+- 龙进 <[email protected]>
+:::
+
+## 概述
+
+&emsp;&emsp;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重构后)
+- 支持设置回调函数,调用回调函数来设置参数值

+ 1 - 2
docs/kernel/boot/index.rst

@@ -1,10 +1,9 @@
 引导加载
 ====================================
-
-   DragonOS采用GRUB2作为其引导加载程序,支持Multiboot2协议引导。目前仅支持GRUB2.06版本。
    
 .. toctree::
    :maxdepth: 1
    :caption: 目录
 
    bootloader
+   cmdline

+ 1 - 0
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"

+ 6 - 0
kernel/crates/kcmdline_macros/Cargo.toml

@@ -0,0 +1,6 @@
+[package]
+name = "kcmdline_macros"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]

+ 74 - 0
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)
+        };
+    };
+}

+ 1 - 1
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<usize, SystemError> {
+    pub fn write_to_core(&self, buf: &[u8], nr: usize) -> Result<usize, SystemError> {
         self.core
             .driver()
             .driver_funcs()

+ 19 - 12
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<dyn FileSystem>) -> Result<(), SystemE
     return Ok(());
 }
 
+fn try_find_gendisk_as_rootfs(path: &str) -> Option<Arc<GenDisk>> {
+    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<Arc<FATFileSystem>, SystemError> = FATFileSystem::new(gendisk);
     if fatfs.is_err() {

+ 8 - 8
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()

+ 476 - 0
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<KernelCmdlineEarlyKV> {
+        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<KernelCmdlineParameter> {
+        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<bool> {
+        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<CString>,
+    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<InnerKernelCmdlineManager>,
+}
+
+pub(super) struct InnerKernelCmdlineManager {
+    /// init进程的路径
+    init_path: Option<CString>,
+    init_args: Vec<CString>,
+    init_envs: Vec<CString>,
+}
+
+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<CString> {
+        self.inner.lock().init_path.clone()
+    }
+
+    pub(super) fn init_proc_args(&self) -> Vec<CString> {
+        self.inner.lock().init_args.clone()
+    }
+
+    pub(super) fn init_proc_envs(&self) -> Vec<CString> {
+        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<Item = &'a str> {
+        // 是否在引号内
+        let mut in_quote = false;
+        cmdline.split(move |c: char| {
+            if c == '"' {
+                in_quote = !in_quote;
+            }
+            !in_quote && c.is_whitespace()
+        })
+    }
+}

+ 19 - 4
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();
 }

+ 61 - 16
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 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();
-    // 逐个尝试运行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.");
-    }
 
+    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(())
 }

+ 1 - 0
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;

+ 2 - 1
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;

+ 1 - 2
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());
         }

+ 1 - 2
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