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 <longjin@DragonOS.org>
This commit is contained in:
LoGin 2024-10-13 23:39:55 +08:00 committed by GitHub
parent c709f79fda
commit f9fe30be89
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 781 additions and 49 deletions

109
docs/kernel/boot/cmdline.md Normal file
View File

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

View File

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

View File

@ -41,6 +41,7 @@ fdt = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/fdt", rev
hashbrown = "=0.13.2" hashbrown = "=0.13.2"
ida = { path = "crates/ida" } ida = { path = "crates/ida" }
intertrait = { path = "crates/intertrait" } intertrait = { path = "crates/intertrait" }
kcmdline_macros = { path = "crates/kcmdline_macros" }
kdepends = { path = "crates/kdepends" } kdepends = { path = "crates/kdepends" }
klog_types = { path = "crates/klog_types" } klog_types = { path = "crates/klog_types" }
linkme = "=0.3.27" linkme = "=0.3.27"

View File

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

View File

@ -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)
};
};
}

View File

@ -103,7 +103,7 @@ impl TtyCore {
self.line_discipline.clone() 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 self.core
.driver() .driver()
.driver_funcs() .driver_funcs()

View File

@ -5,7 +5,7 @@ use log::{error, info};
use system_error::SystemError; use system_error::SystemError;
use crate::{ use crate::{
driver::base::block::manager::block_dev_manager, driver::base::block::{gendisk::GenDisk, manager::block_dev_manager},
filesystem::{ filesystem::{
devfs::devfs_init, devfs::devfs_init,
fat::fs::FATFileSystem, fat::fs::FATFileSystem,
@ -28,6 +28,7 @@ use super::{
/// 当没有指定根文件系统时,尝试的根文件系统列表 /// 当没有指定根文件系统时,尝试的根文件系统列表
const ROOTFS_TRY_LIST: [&str; 4] = ["/dev/sda1", "/dev/sda", "/dev/vda1", "/dev/vda"]; const ROOTFS_TRY_LIST: [&str; 4] = ["/dev/sda1", "/dev/sda", "/dev/vda1", "/dev/vda"];
kernel_cmdline_param_kv!(ROOTFS_PATH_PARAM, root, "");
/// @brief 原子地生成新的Inode号。 /// @brief 原子地生成新的Inode号。
/// 请注意所有的inode号都需要通过该函数来生成.全局的inode号除了以下两个特殊的以外都是唯一的 /// 请注意所有的inode号都需要通过该函数来生成.全局的inode号除了以下两个特殊的以外都是唯一的
@ -116,20 +117,26 @@ fn migrate_virtual_filesystem(new_fs: Arc<dyn FileSystem>) -> Result<(), SystemE
return Ok(()); return Ok(());
} }
pub fn mount_root_fs() -> Result<(), SystemError> { fn try_find_gendisk_as_rootfs(path: &str) -> Option<Arc<GenDisk>> {
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) { if let Some(gd) = block_dev_manager().lookup_gendisk_by_path(path) {
info!("Use {} as rootfs", path); info!("Use {} as rootfs", path);
return Some(gd); return Some(gd);
} }
return None; return None;
}) }
.ok_or(SystemError::ENODEV)?;
pub fn mount_root_fs() -> Result<(), SystemError> {
info!("Try to mount root fs...");
block_dev_manager().print_gendisks();
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); let fatfs: Result<Arc<FATFileSystem>, SystemError> = FATFileSystem::new(gendisk);
if fatfs.is_err() { if fatfs.is_err() {

View File

@ -46,13 +46,20 @@ impl BootParams {
/// 开机命令行参数字符串 /// 开机命令行参数字符串
pub fn boot_cmdline_str(&self) -> &str { 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> { pub fn bootloader_name(&self) -> Option<&str> {
self.bootloader_name.as_deref() 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() { 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(); let mut boot_params = boot_params().write();
boot_params.bootloader_name = boot_callbacks() boot_params.bootloader_name = boot_callbacks()
.init_bootloader_name() .init_bootloader_name()

476
kernel/src/init/cmdline.rs Normal file
View File

@ -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()
})
}
}

View File

@ -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 /// The entry point for the kernel
/// ///
@ -52,9 +55,7 @@ pub fn start_kernel() -> ! {
#[inline(never)] #[inline(never)]
fn do_start_kernel() { fn do_start_kernel() {
init_before_mem_init(); init_before_mem_init();
early_init_logging();
early_setup_arch().expect("setup_arch failed");
unsafe { mm_init() }; unsafe { mm_init() };
if scm_reinit().is_ok() { if scm_reinit().is_ok() {
@ -62,8 +63,10 @@ fn do_start_kernel() {
warn!("Failed to init textui: {:?}", e); warn!("Failed to init textui: {:?}", e);
} }
} }
// 初始化内核命令行参数
kenrel_cmdline_param_manager().init();
boot_callback_except_early(); boot_callback_except_early();
init_intertrait(); init_intertrait();
vfs_init().expect("vfs init failed"); vfs_init().expect("vfs init failed");
@ -99,4 +102,16 @@ fn init_before_mem_init() {
serial_early_init().expect("serial early init failed"); serial_early_init().expect("serial early init failed");
let video_ok = unsafe { VideoRefreshManager::video_init().is_ok() }; let video_ok = unsafe { VideoRefreshManager::video_init().is_ok() };
scm_init(video_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();
} }

View File

@ -11,12 +11,17 @@ use crate::{
driver::{net::e1000e::e1000e::e1000e_init, virtio::virtio::virtio_probe}, driver::{net::e1000e::e1000e::e1000e_init, virtio::virtio::virtio_probe},
filesystem::vfs::core::mount_root_fs, filesystem::vfs::core::mount_root_fs,
net::net_core::net_init, 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, smp::smp_init,
syscall::Syscall, 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 { pub fn initial_kernel_thread() -> i32 {
kernel_init().unwrap_or_else(|err| { 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; *current_pcb.sched_info().sched_policy.write_irqsave() = crate::sched::SchedPolicy::CFS;
drop(current_pcb); 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(); let mut trap_frame = TrapFrame::new();
// 逐个尝试运行init进程
if try_to_run_init_process("/bin/dragonreach", &mut trap_frame).is_err() if let Some(path) = kenrel_cmdline_param_manager().init_proc_path() {
&& try_to_run_init_process("/bin/init", &mut trap_frame).is_err() log::info!("Boot with specified init process: {:?}", path);
&& try_to_run_init_process("/bin/sh", &mut trap_frame).is_err()
{ 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."); panic!("Failed to run init process: No working init found.");
} }
}
drop(proc_init_info);
// 需要确保执行到这里之后上面所有的资源都已经释放比如arc之类的 // 需要确保执行到这里之后上面所有的资源都已经释放比如arc之类的
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
unsafe { arch_switch_to_user(trap_frame) }; unsafe { arch_switch_to_user(trap_frame) };
} }
fn try_to_run_init_process(path: &str, trap_frame: &mut TrapFrame) -> Result<(), SystemError> { fn try_to_run_init_process(
if let Err(e) = run_init_process(path, trap_frame) { 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 { if e != SystemError::ENOENT {
error!( error!(
"Failed to run init process: {path} exists but couldn't execute it (error {:?})", "Failed to run init process: {path} exists but couldn't execute it (error {:?})",
e e
); );
} }
proc_init_info.args.remove(0);
return Err(e); return Err(e);
} }
Ok(()) Ok(())
} }
fn run_init_process(path: &str, trap_frame: &mut TrapFrame) -> Result<(), SystemError> { fn run_init_process(
let argv = vec![CString::new(path).unwrap()]; proc_init_info: &ProcInitInfo,
let envp = vec![CString::new("PATH=/").unwrap()]; trap_frame: &mut TrapFrame,
) -> Result<(), SystemError> {
compiler_fence(Ordering::SeqCst); 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(()) Ok(())
} }

View File

@ -2,6 +2,7 @@ use crate::libs::rwlock::RwLock;
use self::boot::BootParams; use self::boot::BootParams;
pub mod boot; pub mod boot;
pub mod cmdline;
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
pub mod init; pub mod init;
pub mod initcall; pub mod initcall;

View File

@ -81,7 +81,8 @@ extern crate smoltcp;
extern crate intertrait; extern crate intertrait;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
extern crate x86; extern crate x86;
#[macro_use]
extern crate kcmdline_macros;
extern crate klog_types; extern crate klog_types;
extern crate uefi; extern crate uefi;
extern crate uefi_raw; extern crate uefi_raw;

View File

@ -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 port = current_vc.port();
let tty = port.port_data().internal_tty(); let tty = port.port_data().internal_tty();
if let Some(tty) = tty { if let Some(tty) = tty {
send_to_default_serial8250_port(&[character]);
return tty return tty
.write_without_serial(buf.as_bytes(), buf.len()) .write_to_core(buf.as_bytes(), buf.len())
.map(|_| 0) .map(|_| 0)
.unwrap_or_else(|e| e.to_posix_errno()); .unwrap_or_else(|e| e.to_posix_errno());
} }

View File

@ -126,9 +126,8 @@ cfg_content='set timeout=15
set default=0 set default=0
insmod efi_gop insmod efi_gop
menuentry "DragonOS" { menuentry "DragonOS" {
multiboot2 /boot/kernel.elf "KERNEL_ELF" multiboot2 /boot/kernel.elf init=/bin/dragonreach
}' }'
# 增加insmod efi_gop防止32位uefi启动报错 # 增加insmod efi_gop防止32位uefi启动报错
echo "echo '${cfg_content}' > ${boot_folder}/grub/grub.cfg" | sh echo "echo '${cfg_content}' > ${boot_folder}/grub/grub.cfg" | sh
fi fi