diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index 58a86ce4..03ddb7ec 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -12,14 +12,14 @@ jobs: name: Format check ${{ matrix.arch }} runs-on: ubuntu-latest continue-on-error: true - container: dragonos/dragonos-dev:v1.3 + container: dragonos/dragonos-dev:v1.4 strategy: matrix: arch: [x86_64, riscv64] steps: - - run: echo "Running in dragonos/dragonos-dev:v1.3" + - run: echo "Running in dragonos/dragonos-dev:v1.4" - uses: actions/checkout@v3 - name: Format check @@ -35,14 +35,14 @@ jobs: name: Kernel static test ${{ matrix.arch }} runs-on: ubuntu-latest continue-on-error: true - container: dragonos/dragonos-dev:v1.3 + container: dragonos/dragonos-dev:v1.4 strategy: matrix: arch: [x86_64, riscv64] steps: - - run: echo "Running in dragonos/dragonos-dev:v1.3" + - run: echo "Running in dragonos/dragonos-dev:v1.4" - uses: actions/checkout@v3 @@ -56,10 +56,10 @@ jobs: build-x86_64: runs-on: ubuntu-latest - container: dragonos/dragonos-dev:v1.3 + container: dragonos/dragonos-dev:v1.4 steps: - - run: echo "Running in dragonos/dragonos-dev:v1.3" + - run: echo "Running in dragonos/dragonos-dev:v1.4" - uses: actions/checkout@v3 - name: build the DragonOS @@ -78,10 +78,10 @@ jobs: build-riscv64: runs-on: ubuntu-latest - container: dragonos/dragonos-dev:v1.3 + container: dragonos/dragonos-dev:v1.4 steps: - - run: echo "Running in dragonos/dragonos-dev:v1.3" + - run: echo "Running in dragonos/dragonos-dev:v1.4" - uses: actions/checkout@v3 with: 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/crates/rust-slabmalloc/src/lib.rs b/kernel/crates/rust-slabmalloc/src/lib.rs index 819fc854..5b995fd9 100644 --- a/kernel/crates/rust-slabmalloc/src/lib.rs +++ b/kernel/crates/rust-slabmalloc/src/lib.rs @@ -65,7 +65,12 @@ pub enum AllocationError { /// Needs to adhere to safety requirements of a rust allocator (see GlobalAlloc et. al.). pub unsafe trait Allocator<'a> { fn allocate(&mut self, layout: Layout) -> Result, AllocationError>; - fn deallocate(&mut self, ptr: NonNull, layout: Layout) -> Result<(), AllocationError>; + unsafe fn deallocate( + &mut self, + ptr: NonNull, + layout: Layout, + slab_callback: &'static dyn CallBack, + ) -> Result<(), AllocationError>; /// Refill the allocator with a [`ObjectPage`]. /// @@ -77,3 +82,8 @@ pub unsafe trait Allocator<'a> { new_page: &'a mut ObjectPage<'a>, ) -> Result<(), AllocationError>; } + +/// 将slab_page归还Buddy的回调函数 +pub trait CallBack: Send + Sync { + unsafe fn free_slab_page(&self, _: *mut u8, _: usize) {} +} diff --git a/kernel/crates/rust-slabmalloc/src/pages.rs b/kernel/crates/rust-slabmalloc/src/pages.rs index 6d2f6516..1e92eb7e 100644 --- a/kernel/crates/rust-slabmalloc/src/pages.rs +++ b/kernel/crates/rust-slabmalloc/src/pages.rs @@ -255,6 +255,20 @@ pub trait AllocablePage { self.bitfield().clear_bit(idx); Ok(()) } + + /// 统计page中还可以分配多少个object + fn free_obj_count(&self) -> usize { + // 统计page中还可以分配多少个object + let mut free_obj_count = 0; + + // 遍历page中的bitfield(用来统计内存分配情况的u64数组) + for b in self.bitfield().iter() { + let bitval = b.load(Ordering::Relaxed); + free_obj_count += bitval.count_zeros() as usize; + } + + free_obj_count + } } /// Holds allocated data within a 4 KiB page. diff --git a/kernel/crates/rust-slabmalloc/src/sc.rs b/kernel/crates/rust-slabmalloc/src/sc.rs index fd34eada..a17f5770 100644 --- a/kernel/crates/rust-slabmalloc/src/sc.rs +++ b/kernel/crates/rust-slabmalloc/src/sc.rs @@ -59,21 +59,29 @@ pub struct SCAllocator<'a, P: AllocablePage> { pub(crate) slabs: PageList<'a, P>, /// List of full ObjectPages (everything allocated in these don't need to search them). pub(crate) full_slabs: PageList<'a, P>, + /// Free objects count + pub(crate) free_obj_count: usize, + /// Maximum free objects num for this `SCAllocator`. + pub(crate) free_limit: usize, } /// Creates an instance of a scallocator, we do this in a macro because we /// re-use the code in const and non-const functions macro_rules! new_sc_allocator { - ($size:expr) => { + ($size:expr) => {{ + let obj_per_page = cmin((P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD) / $size, 8 * 64); SCAllocator { size: $size, allocation_count: 0, - obj_per_page: cmin((P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD) / $size, 8 * 64), + obj_per_page, empty_slabs: PageList::new(), slabs: PageList::new(), full_slabs: PageList::new(), + // TODO: 优化free_limit的计算: https://bbs.dragonos.org.cn/t/topic/358 + free_limit: 2 * obj_per_page, + free_obj_count: 0, } - }; + }}; } impl<'a, P: AllocablePage> SCAllocator<'a, P> { @@ -241,6 +249,7 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> { *page.next() = Rawlink::none(); trace!("adding page to SCAllocator {:p}", page); self.insert_empty(page); + self.free_obj_count += self.obj_per_page; } /// Allocates a block of memory descriped by `layout`. @@ -294,6 +303,7 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> { self.size, ptr as usize ); + self.free_obj_count -= 1; } res @@ -304,7 +314,12 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> { /// May return an error in case an invalid `layout` is provided. /// The function may also move internal slab pages between lists partial -> empty /// or full -> partial lists. - pub fn deallocate(&self, ptr: NonNull, layout: Layout) -> Result<(), AllocationError> { + pub unsafe fn deallocate( + &mut self, + ptr: NonNull, + layout: Layout, + slab_callback: &'static dyn CallBack, + ) -> Result<(), AllocationError> { assert!(layout.size() <= self.size); assert!(self.size <= (P::SIZE - OBJECT_PAGE_METADATA_OVERHEAD)); trace!( @@ -324,6 +339,17 @@ impl<'a, P: AllocablePage> SCAllocator<'a, P> { let ret = slab_page.deallocate(ptr, new_layout); debug_assert!(ret.is_ok(), "Slab page deallocate won't fail at the moment"); + self.free_obj_count += 1; + let is_empty_after_dealloc = slab_page.is_empty(self.obj_per_page); + + // 如果slab_page是空白的,且空闲块数大于free_limit,将slab_page归还buddy + if self.free_obj_count >= self.free_limit && is_empty_after_dealloc { + self.slabs.remove_from_list(slab_page); + // 将slab_page归还buddy + slab_callback.free_slab_page(slab_page as *const P as *mut u8, P::SIZE); + } + self.check_page_assignments(); + ret } } diff --git a/kernel/crates/rust-slabmalloc/src/zone.rs b/kernel/crates/rust-slabmalloc/src/zone.rs index 118d45c0..3e6d7cad 100644 --- a/kernel/crates/rust-slabmalloc/src/zone.rs +++ b/kernel/crates/rust-slabmalloc/src/zone.rs @@ -3,7 +3,6 @@ //! The ZoneAllocator achieves this by having many `SCAllocator` use crate::*; -use core::sync::atomic::Ordering; /// Creates an instance of a zone, we do this in a macro because we /// re-use the code in const and non-const functions @@ -139,16 +138,8 @@ impl<'a> ZoneAllocator<'a> { // 遍历scallocator中的部分分配的page(partial_page) for slab_page in scallocator.slabs.iter_mut() { - // 统计page中还可以分配多少个object - let mut free_obj_count = 0; - // 遍历page中的bitfield(用来统计内存分配情况的u64数组) - for b in slab_page.bitfield().iter() { - let bitval = b.load(Ordering::Relaxed); - let free_count = bitval.count_zeros() as usize; - free_obj_count += free_count; - } // 剩余可分配object数乘上page中规定的每个object的大小,即空闲空间 - free += free_obj_count * scallocator.size(); + free += slab_page.free_obj_count() * scallocator.size(); } // 遍历scallocator中的empty_page,把空页空间也加上去 free += @@ -178,9 +169,15 @@ unsafe impl<'a> crate::Allocator<'a> for ZoneAllocator<'a> { /// # Arguments /// * `ptr` - Address of the memory location to free. /// * `layout` - Memory layout of the block pointed to by `ptr`. - fn deallocate(&mut self, ptr: NonNull, layout: Layout) -> Result<(), AllocationError> { + /// * `slab_callback` - The callback function to free slab_page in buddy. + unsafe fn deallocate( + &mut self, + ptr: NonNull, + layout: Layout, + slab_callback: &'static dyn CallBack, + ) -> Result<(), AllocationError> { match ZoneAllocator::get_slab(layout.size()) { - Slab::Base(idx) => self.small_slabs[idx].deallocate(ptr, layout), + Slab::Base(idx) => self.small_slabs[idx].deallocate(ptr, layout, slab_callback), Slab::Unsupported => Err(AllocationError::InvalidLayout), } } diff --git a/kernel/src/driver/serial/serial8250/serial8250_pio.rs b/kernel/src/driver/serial/serial8250/serial8250_pio.rs index 0c346369..f5cba86f 100644 --- a/kernel/src/driver/serial/serial8250/serial8250_pio.rs +++ b/kernel/src/driver/serial/serial8250/serial8250_pio.rs @@ -19,12 +19,14 @@ use crate::{ }, serial::{AtomicBaudRate, BaudRate, DivisorFraction, UartPort}, tty::{ + console::ConsoleSwitch, kthread::send_to_tty_refresh_thread, termios::WindowSize, tty_core::{TtyCore, TtyCoreData}, tty_driver::{TtyDriver, TtyDriverManager, TtyOperation}, - virtual_terminal::{vc_manager, VirtConsole}, + virtual_terminal::{vc_manager, virtual_console::VirtualConsoleData, VirtConsole}, }, + video::console::dummycon::dummy_console, }, exception::{ irqdata::IrqHandlerData, @@ -32,7 +34,7 @@ use crate::{ manage::irq_manager, IrqNumber, }, - libs::rwlock::RwLock, + libs::{rwlock::RwLock, spinlock::SpinLock}, }; use system_error::SystemError; @@ -265,9 +267,20 @@ impl UartPort for Serial8250PIOPort { } fn handle_irq(&self) -> Result<(), SystemError> { - if let Some(c) = self.read_one_byte() { - send_to_tty_refresh_thread(&[c]); + let mut buf = [0; 8]; + let mut index = 0; + + // Read up to the size of the buffer + while index < buf.len() { + if let Some(c) = self.read_one_byte() { + buf[index] = c; + index += 1; + } else { + break; // No more bytes to read + } } + + send_to_tty_refresh_thread(&buf[0..index]); Ok(()) } @@ -385,7 +398,18 @@ impl TtyOperation for Serial8250PIOTtyDriverInner { if tty.core().index() >= unsafe { PIO_PORTS.len() } { return Err(SystemError::ENODEV); } - let vc = VirtConsole::new(None); + + *tty.core().window_size_write() = WindowSize::DEFAULT; + let vc_data = Arc::new(SpinLock::new(VirtualConsoleData::new(usize::MAX))); + let mut vc_data_guard = vc_data.lock_irqsave(); + vc_data_guard.set_driver_funcs(Arc::downgrade(&dummy_console()) as Weak); + vc_data_guard.init( + Some(tty.core().window_size().row.into()), + Some(tty.core().window_size().col.into()), + true, + ); + drop(vc_data_guard); + let vc = VirtConsole::new(Some(vc_data)); let vc_index = vc_manager().alloc(vc.clone()).ok_or(SystemError::EBUSY)?; self.do_install(driver, tty, vc.clone()).inspect_err(|_| { vc_manager().free(vc_index); diff --git a/kernel/src/driver/tty/termios.rs b/kernel/src/driver/tty/termios.rs index b219d203..0a0ca4bb 100644 --- a/kernel/src/driver/tty/termios.rs +++ b/kernel/src/driver/tty/termios.rs @@ -2,7 +2,7 @@ use super::tty_ldisc::LineDisciplineType; /// ## 窗口大小 #[repr(C)] -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Default)] pub struct WindowSize { /// 行 pub row: u16, @@ -26,12 +26,6 @@ impl WindowSize { } } -impl Default for WindowSize { - fn default() -> Self { - Self::DEFAULT - } -} - #[derive(Debug, Clone, Copy)] pub struct Termios { pub input_mode: InputMode, diff --git a/kernel/src/driver/tty/tty_core.rs b/kernel/src/driver/tty/tty_core.rs index 376de925..93f0e9c4 100644 --- a/kernel/src/driver/tty/tty_core.rs +++ b/kernel/src/driver/tty/tty_core.rs @@ -31,7 +31,7 @@ use super::{ TtyLineDiscipline, }, tty_port::TtyPort, - virtual_terminal::{vc_manager, virtual_console::VirtualConsoleData}, + virtual_terminal::{vc_manager, virtual_console::VirtualConsoleData, DrawRegion}, }; #[derive(Debug)] @@ -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() @@ -489,6 +489,61 @@ impl TtyCoreData { pub fn add_epitem(&self, epitem: Arc) { self.epitems.lock().push_back(epitem) } + + pub fn eptiems(&self) -> &SpinLock>> { + &self.epitems + } + + pub fn do_write(&self, buf: &[u8], mut nr: usize) -> Result { + // 关闭中断 + if let Some(vc_data) = self.vc_data() { + let mut vc_data_guard = vc_data.lock_irqsave(); + let mut offset = 0; + + // 这个参数是用来扫描unicode字符的,但是这部分目前未完成,先写着 + let mut rescan = false; + let mut ch: u32 = 0; + + let mut draw = DrawRegion::default(); + + // 首先隐藏光标再写 + vc_data_guard.hide_cursor(); + + while nr != 0 { + if !rescan { + ch = buf[offset] as u32; + offset += 1; + nr -= 1; + } + + let (tc, rescan_last) = vc_data_guard.translate(&mut ch); + if tc.is_none() { + // 表示未转换完成 + continue; + } + + let tc = tc.unwrap(); + rescan = rescan_last; + + if vc_data_guard.is_control(tc, ch) { + vc_data_guard.flush(&mut draw); + vc_data_guard.do_control(ch); + continue; + } + + if !vc_data_guard.console_write_normal(tc, ch, &mut draw) { + continue; + } + } + + vc_data_guard.flush(&mut draw); + + // TODO: notify update + return Ok(offset); + } else { + return Ok(0); + } + } } impl TtyOperation for TtyCore { diff --git a/kernel/src/driver/tty/tty_port.rs b/kernel/src/driver/tty/tty_port.rs index b387343f..d0488132 100644 --- a/kernel/src/driver/tty/tty_port.rs +++ b/kernel/src/driver/tty/tty_port.rs @@ -4,7 +4,10 @@ use alloc::sync::{Arc, Weak}; use kdepends::thingbuf::mpsc; use system_error::SystemError; -use crate::libs::spinlock::{SpinLock, SpinLockGuard}; +use crate::{ + libs::spinlock::{SpinLock, SpinLockGuard}, + net::event_poll::EventPoll, +}; use super::tty_core::TtyCore; @@ -85,6 +88,8 @@ pub trait TtyPort: Sync + Send + Debug { return ld.receive_buf(tty, buf, None, count); } + EventPoll::wakeup_epoll(tty.core().eptiems(), None)?; + ret } } diff --git a/kernel/src/driver/tty/virtual_terminal/mod.rs b/kernel/src/driver/tty/virtual_terminal/mod.rs index 8336591a..31abbfb6 100644 --- a/kernel/src/driver/tty/virtual_terminal/mod.rs +++ b/kernel/src/driver/tty/virtual_terminal/mod.rs @@ -300,55 +300,6 @@ impl TtyConsoleDriverInner { Ok(Self { console }) } - fn do_write(&self, tty: &TtyCoreData, buf: &[u8], mut nr: usize) -> Result { - // 关闭中断 - let vc_data = tty.vc_data().unwrap(); - let mut vc_data_guard = vc_data.lock_irqsave(); - - let mut offset = 0; - - // 这个参数是用来扫描unicode字符的,但是这部分目前未完成,先写着 - let mut rescan = false; - let mut ch: u32 = 0; - - let mut draw = DrawRegion::default(); - - // 首先隐藏光标再写 - vc_data_guard.hide_cursor(); - - while nr != 0 { - if !rescan { - ch = buf[offset] as u32; - offset += 1; - nr -= 1; - } - - let (tc, rescan_last) = vc_data_guard.translate(&mut ch); - if tc.is_none() { - // 表示未转换完成 - continue; - } - - let tc = tc.unwrap(); - rescan = rescan_last; - - if vc_data_guard.is_control(tc, ch) { - vc_data_guard.flush(&mut draw); - vc_data_guard.do_control(ch); - continue; - } - - if !vc_data_guard.console_write_normal(tc, ch, &mut draw) { - continue; - } - } - - vc_data_guard.flush(&mut draw); - - // TODO: notify update - return Ok(offset); - } - fn do_install(&self, tty: Arc, vc: &Arc) -> Result<(), SystemError> { let tty_core = tty.core(); @@ -426,7 +377,7 @@ impl TtyOperation for TtyConsoleDriverInner { // loop {} // } send_to_default_serial8250_port(buf); - let ret = self.do_write(tty, buf, nr); + let ret = tty.do_write(buf, nr); self.flush_chars(tty); ret } diff --git a/kernel/src/driver/tty/virtual_terminal/virtual_console.rs b/kernel/src/driver/tty/virtual_terminal/virtual_console.rs index 3b5630ed..da7cdcd5 100644 --- a/kernel/src/driver/tty/virtual_terminal/virtual_console.rs +++ b/kernel/src/driver/tty/virtual_terminal/virtual_console.rs @@ -206,7 +206,7 @@ impl VirtualConsoleData { } } - pub(super) fn init(&mut self, rows: Option, cols: Option, clear: bool) { + pub fn init(&mut self, rows: Option, cols: Option, clear: bool) { if let Some(rows) = rows { self.rows = rows; } @@ -242,7 +242,7 @@ impl VirtualConsoleData { self.driver_funcs.as_ref().unwrap().upgrade().unwrap() } - pub(super) fn set_driver_funcs(&mut self, func: Weak) { + pub fn set_driver_funcs(&mut self, func: Weak) { self.driver_funcs = Some(func); } @@ -312,7 +312,7 @@ impl VirtualConsoleData { /// /// ### 返回值 /// ### (转换后的字符:i32,是否需要更多的数据才能进行转换:bool) - pub(super) fn translate(&mut self, c: &mut u32) -> (Option, bool) { + pub fn translate(&mut self, c: &mut u32) -> (Option, bool) { if self.vc_state != VirtualConsoleState::ESnormal { // 在控制字符状态下不需要翻译 return (Some(*c), false); @@ -440,7 +440,7 @@ impl VirtualConsoleData { const CTRL_ALWAYS: u32 = 0x0800f501; /// ## 用于判断tc(终端字符)在当前VC下是不是需要显示的控制字符 - pub(super) fn is_control(&self, tc: u32, c: u32) -> bool { + pub fn is_control(&self, tc: u32, c: u32) -> bool { // 当前vc状态机不在正常状态,即在接收特殊字符的状态,则是控制字符 if self.vc_state != VirtualConsoleState::ESnormal { return true; @@ -1257,7 +1257,7 @@ impl VirtualConsoleData { /// ## 处理终端控制字符 #[inline(never)] - pub(super) fn do_control(&mut self, ch: u32) { + pub fn do_control(&mut self, ch: u32) { // 首先检查是否处于 ANSI 控制字符串状态 if self.vc_state.is_ansi_control_string() && (8..=13).contains(&ch) { return; @@ -1534,12 +1534,7 @@ impl VirtualConsoleData { } #[inline(never)] - pub(super) fn console_write_normal( - &mut self, - mut tc: u32, - c: u32, - draw: &mut DrawRegion, - ) -> bool { + pub fn console_write_normal(&mut self, mut tc: u32, c: u32, draw: &mut DrawRegion) -> bool { let mut attr = self.attr; let himask = self.hi_font_mask; let charmask = if himask == 0 { 0xff } else { 0x1ff }; @@ -1753,7 +1748,7 @@ impl VirtualConsoleData { return (self.attr & 0x88) | ((self.attr & 0x70) >> 4) | ((self.attr & 0x07) << 4); } - pub(super) fn flush(&self, draw: &mut DrawRegion) { + pub fn flush(&self, draw: &mut DrawRegion) { if draw.x.is_none() { return; } diff --git a/kernel/src/filesystem/eventfd.rs b/kernel/src/filesystem/eventfd.rs index 5a90ff67..4d0d9a76 100644 --- a/kernel/src/filesystem/eventfd.rs +++ b/kernel/src/filesystem/eventfd.rs @@ -137,7 +137,7 @@ impl IndexNode for EventFdInode { let pollflag = EPollEventType::from_bits_truncate(self.poll(&data)? as u32); // 唤醒epoll中等待的进程 - EventPoll::wakeup_epoll(&self.epitems, pollflag)?; + EventPoll::wakeup_epoll(&self.epitems, Some(pollflag))?; return Ok(8); } @@ -184,7 +184,7 @@ impl IndexNode for EventFdInode { let pollflag = EPollEventType::from_bits_truncate(self.poll(&data)? as u32); // 唤醒epoll中等待的进程 - EventPoll::wakeup_epoll(&self.epitems, pollflag)?; + EventPoll::wakeup_epoll(&self.epitems, Some(pollflag))?; return Ok(8); } 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/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index cc479883..c4ab9079 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -399,6 +399,15 @@ impl IndexNode for MountFSInode { return self.inner_inode.ioctl(cmd, data, private_data); } + #[inline] + fn kernel_ioctl( + &self, + arg: Arc, + data: &FilePrivateData, + ) -> Result { + return self.inner_inode.kernel_ioctl(arg, data); + } + #[inline] fn list(&self) -> Result, SystemError> { return self.inner_inode.list(); 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 f5d6b028..a6670f16 100644 --- a/kernel/src/init/initial_kthread.rs +++ b/kernel/src/init/initial_kthread.rs @@ -14,12 +14,17 @@ use crate::{ }, 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| { @@ -73,39 +78,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/ipc/pipe.rs b/kernel/src/ipc/pipe.rs index 864bc750..05b8927c 100644 --- a/kernel/src/ipc/pipe.rs +++ b/kernel/src/ipc/pipe.rs @@ -267,7 +267,7 @@ impl IndexNode for LockedPipeInode { let pollflag = EPollEventType::from_bits_truncate(inode.poll(&data)? as u32); // 唤醒epoll中等待的进程 - EventPoll::wakeup_epoll(&inode.epitems, pollflag)?; + EventPoll::wakeup_epoll(&inode.epitems, Some(pollflag))?; //返回读取的字节数 return Ok(num); @@ -413,7 +413,7 @@ impl IndexNode for LockedPipeInode { let pollflag = EPollEventType::from_bits_truncate(inode.poll(&data)? as u32); // 唤醒epoll中等待的进程 - EventPoll::wakeup_epoll(&inode.epitems, pollflag)?; + EventPoll::wakeup_epoll(&inode.epitems, Some(pollflag))?; // 返回写入的字节数 return Ok(len); 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/kernel/src/mm/allocator/kernel_allocator.rs b/kernel/src/mm/allocator/kernel_allocator.rs index 6201fef3..9d71d907 100644 --- a/kernel/src/mm/allocator/kernel_allocator.rs +++ b/kernel/src/mm/allocator/kernel_allocator.rs @@ -51,7 +51,7 @@ impl KernelAllocator { return Ok(NonNull::from(slice)); } - unsafe fn free_in_buddy(&self, ptr: *mut u8, layout: Layout) { + pub(super) unsafe fn free_in_buddy(&self, ptr: *mut u8, layout: Layout) { // 由于buddy分配的页数量是2的幂,因此释放的时候也需要按照2的幂向上取整。 let count = (page_align_up(layout.size()) / MMArch::PAGE_SIZE).next_power_of_two(); let page_frame_count = PageFrameCount::new(count); diff --git a/kernel/src/mm/allocator/slab.rs b/kernel/src/mm/allocator/slab.rs index 710f9b7b..e564c325 100644 --- a/kernel/src/mm/allocator/slab.rs +++ b/kernel/src/mm/allocator/slab.rs @@ -4,12 +4,16 @@ use alloc::boxed::Box; use log::debug; use slabmalloc::*; +use crate::{arch::MMArch, mm::MemoryManagementArch, KERNEL_ALLOCATOR}; + // 全局slab分配器 pub(crate) static mut SLABALLOCATOR: Option = None; // slab初始化状态 pub(crate) static mut SLABINITSTATE: AtomicBool = AtomicBool::new(false); +static SLAB_CALLBACK: SlabCallback = SlabCallback; + /// slab分配器,实际为一堆小的allocator,可以在里面装4K的page /// 利用这些allocator可以为对象分配不同大小的空间 pub(crate) struct SlabAllocator { @@ -52,7 +56,7 @@ impl SlabAllocator { ) -> Result<(), AllocationError> { if let Some(nptr) = NonNull::new(ptr) { self.zone - .deallocate(nptr, layout) + .deallocate(nptr, layout, &SLAB_CALLBACK) .expect("Couldn't deallocate"); return Ok(()); } else { @@ -80,3 +84,13 @@ pub unsafe fn slab_usage() -> SlabUsage { SlabUsage::new(0, 0) } } + +/// 归还slab_page给buddy的回调 +pub struct SlabCallback; +impl CallBack for SlabCallback { + unsafe fn free_slab_page(&self, base_addr: *mut u8, size: usize) { + assert_eq!(base_addr as usize & (MMArch::PAGE_SIZE - 1), 0); // 确认地址4k对齐 + assert_eq!(size, MMArch::PAGE_SIZE); // 确认释放的slab_page大小 + KERNEL_ALLOCATOR.free_in_buddy(base_addr, Layout::from_size_align_unchecked(size, 1)); + } +} diff --git a/kernel/src/net/event_poll/mod.rs b/kernel/src/net/event_poll/mod.rs index af19ee1c..f6a9ad61 100644 --- a/kernel/src/net/event_poll/mod.rs +++ b/kernel/src/net/event_poll/mod.rs @@ -716,41 +716,50 @@ impl EventPoll { /// ### epoll的回调,支持epoll的文件有事件到来时直接调用该方法即可 pub fn wakeup_epoll( epitems: &SpinLock>>, - pollflags: EPollEventType, + pollflags: Option, ) -> Result<(), SystemError> { let mut epitems_guard = epitems.try_lock_irqsave()?; // 一次只取一个,因为一次也只有一个进程能拿到对应文件的🔓 if let Some(epitem) = epitems_guard.pop_front() { - let epoll = epitem.epoll().upgrade().unwrap(); - let mut epoll_guard = epoll.try_lock()?; - let binding = epitem.clone(); - let event_guard = binding.event().read(); - let ep_events = EPollEventType::from_bits_truncate(event_guard.events()); + let pollflags = pollflags.unwrap_or({ + if let Some(file) = epitem.file.upgrade() { + EPollEventType::from_bits_truncate(file.poll()? as u32) + } else { + EPollEventType::empty() + } + }); - // 检查事件合理性以及是否有感兴趣的事件 - if !(ep_events - .difference(EPollEventType::EP_PRIVATE_BITS) - .is_empty() - || pollflags.difference(ep_events).is_empty()) - { - // TODO: 未处理pm相关 + if let Some(epoll) = epitem.epoll().upgrade() { + let mut epoll_guard = epoll.try_lock()?; + let binding = epitem.clone(); + let event_guard = binding.event().read(); + let ep_events = EPollEventType::from_bits_truncate(event_guard.events()); - // 首先将就绪的epitem加入等待队列 - epoll_guard.ep_add_ready(epitem.clone()); + // 检查事件合理性以及是否有感兴趣的事件 + if !(ep_events + .difference(EPollEventType::EP_PRIVATE_BITS) + .is_empty() + || pollflags.difference(ep_events).is_empty()) + { + // TODO: 未处理pm相关 - if epoll_guard.ep_has_waiter() { - if ep_events.contains(EPollEventType::EPOLLEXCLUSIVE) - && !pollflags.contains(EPollEventType::POLLFREE) - { - // 避免惊群 - epoll_guard.ep_wake_one(); - } else { - epoll_guard.ep_wake_all(); + // 首先将就绪的epitem加入等待队列 + epoll_guard.ep_add_ready(epitem.clone()); + + if epoll_guard.ep_has_waiter() { + if ep_events.contains(EPollEventType::EPOLLEXCLUSIVE) + && !pollflags.contains(EPollEventType::POLLFREE) + { + // 避免惊群 + epoll_guard.ep_wake_one(); + } else { + epoll_guard.ep_wake_all(); + } } } - } - epitems_guard.push_back(epitem); + epitems_guard.push_back(epitem); + } } Ok(()) } diff --git a/tools/BUILD_CONTAINER_VERSION b/tools/BUILD_CONTAINER_VERSION index 26e5d195..64c411b8 100644 --- a/tools/BUILD_CONTAINER_VERSION +++ b/tools/BUILD_CONTAINER_VERSION @@ -1 +1 @@ -v1.3 \ No newline at end of file +v1.4 \ No newline at end of file diff --git a/tools/bootstrap.sh b/tools/bootstrap.sh index aee206b9..ddb5a608 100644 --- a/tools/bootstrap.sh +++ b/tools/bootstrap.sh @@ -232,6 +232,7 @@ rustInstall() { echo "正在安装DragonOS所需的rust组件...首次安装需要一些时间来更新索引,请耐心等待..." cargo install cargo-binutils + cargo install bpf-linker rustup toolchain install nightly-2023-08-15-x86_64-unknown-linux-gnu rustup toolchain install $RUST_VERSION-x86_64-unknown-linux-gnu rustup component add rust-src --toolchain $RUST_VERSION-x86_64-unknown-linux-gnu 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