mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 10:15:03 +00:00
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:
parent
c709f79fda
commit
f9fe30be89
109
docs/kernel/boot/cmdline.md
Normal file
109
docs/kernel/boot/cmdline.md
Normal file
@ -0,0 +1,109 @@
|
||||
# 内核启动命令行参数
|
||||
|
||||
:::{note}
|
||||
本文作者:
|
||||
- 龙进 <longjin@DragonOS.org>
|
||||
:::
|
||||
|
||||
## 概述
|
||||
|
||||
  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,10 +1,9 @@
|
||||
引导加载
|
||||
====================================
|
||||
|
||||
DragonOS采用GRUB2作为其引导加载程序,支持Multiboot2协议引导。目前仅支持GRUB2.06版本。
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: 目录
|
||||
|
||||
bootloader
|
||||
cmdline
|
||||
|
@ -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
kernel/crates/kcmdline_macros/Cargo.toml
Normal file
6
kernel/crates/kcmdline_macros/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "kcmdline_macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
74
kernel/crates/kcmdline_macros/src/lib.rs
Normal file
74
kernel/crates/kcmdline_macros/src/lib.rs
Normal 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)
|
||||
};
|
||||
};
|
||||
}
|
@ -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()
|
||||
|
@ -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() {
|
||||
|
@ -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
kernel/src/init/cmdline.rs
Normal file
476
kernel/src/init/cmdline.rs
Normal 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()
|
||||
})
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user