diff --git a/docs/conf.py b/docs/conf.py index 235cd9ca..5a1a940e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,7 +17,7 @@ import os # -- Project information ----------------------------------------------------- project = 'DragonOS' -copyright = '2022-2024, DragonOS Community' +copyright = '2022-2025, DragonOS Community' author = 'longjin' github_org = 'DragonOS-Community' github_repo = 'DragonOS' @@ -31,7 +31,12 @@ release = 'dev' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['myst_parser', 'sphinx_multiversion'] +extensions = [ + 'myst_parser', + 'sphinx_multiversion', + 'sphinxcontrib.mermaid', + 'sphinx.ext.extlinks', +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/index.rst b/docs/index.rst index a0d9c869..aebc2d04 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -33,6 +33,7 @@ kernel/container/index kernel/libs/index kernel/trace/index + kernel/syscall/index @@ -42,11 +43,6 @@ userland/appdev/index -.. toctree:: - :maxdepth: 1 - :caption: 系统调用api文档 - - syscall_api/index .. toctree:: :maxdepth: 1 diff --git a/docs/kernel/filesystem/overview.md b/docs/kernel/filesystem/overview.md index 1238f5a8..624a07fc 100644 --- a/docs/kernel/filesystem/overview.md +++ b/docs/kernel/filesystem/overview.md @@ -67,7 +67,7 @@ Syscall: │ sys_open, sys_read, sys_write, sys_close, │ - `sys_fchmod`:修改文件权限(未实现) - 其他系统调用接口(未实现) -  关于接口的具体含义,可以参考 [DragonOS系统调用接口](../../syscall_api/index.rst)。 +  关于接口的具体含义,可以参考Linux的相关文档。 ## 虚拟文件系统(VFS) diff --git a/docs/syscall_api/index.rst b/docs/kernel/syscall/index.rst similarity index 67% rename from docs/syscall_api/index.rst rename to docs/kernel/syscall/index.rst index 1145e005..41d7beee 100644 --- a/docs/syscall_api/index.rst +++ b/docs/kernel/syscall/index.rst @@ -1,10 +1,10 @@ -.. _syscall_api: +.. _syscall: -系统调用API +系统调用 ==================================== .. toctree:: :maxdepth: 1 :caption: 目录 - intro + syscall_table diff --git a/docs/kernel/syscall/syscall_table.rst b/docs/kernel/syscall/syscall_table.rst new file mode 100644 index 00000000..29399917 --- /dev/null +++ b/docs/kernel/syscall/syscall_table.rst @@ -0,0 +1,120 @@ +系统调用表实现方案 +==================== + +.. note:: + Author: longjin + + Date: 2025/05/13 + +概述 +---- + +.. mermaid:: + :align: center + :caption: 系统调用表架构 + + classDiagram + class Syscall { + <> + +num_args() usize + +handle(args, from_user) Result + +entry_format(args) Vec + } + + class SyscallHandle { + +nr: usize + +inner_handle: &dyn Syscall + } + + class SyscallTable { + -entries: [Option<&SyscallHandle>; 512] + +get(nr) Option<&dyn Syscall> + } + + Syscall <|.. SysXXXXXXHandle + SyscallHandle "1" *-- "1" Syscall + SyscallTable "1" *-- "512" SyscallHandle + +相比于将原本集中在一个大match中的系统调用分发,本方案采用基于trait和系统调用表的实现。主要优势包括: + +- 降低栈内存使用:避免单个大函数占用过多栈空间 +- 支持参数打印:通过统一的参数格式化接口 +- 更好的扩展性:新增系统调用无需修改分发逻辑 + +核心设计 +-------- + +Syscall Trait +~~~~~~~~~~~~~ + +所有系统调用处理函数都需要实现 `Syscall` trait: + +.. code-block:: rust + + pub trait Syscall: Send + Sync + 'static { + fn num_args(&self) -> usize; + fn handle(&self, args: &[usize], from_user: bool) -> Result; + fn entry_format(&self, args: &[usize]) -> Vec; + } + +- `num_args()`: 返回该系统调用需要的参数数量 +- `handle()`: 实际执行系统调用处理 +- `entry_format()`: 格式化参数用于调试打印 + +SyscallHandle +~~~~~~~~~~~~~ + +`SyscallHandle` 结构体将系统调用号与处理函数关联: + +.. code-block:: rust + + pub struct SyscallHandle { + pub nr: usize, // 系统调用号 + pub inner_handle: &'static dyn Syscall, // 处理函数 + pub name: &'static str, + } + +SyscallTable +~~~~~~~~~~~~ + +`SyscallTable` 管理所有系统调用: + +- 固定大小512项 +- 编译时初始化 +- 通过系统调用号快速查找处理函数 + +使用方式 +-------- + +实现系统调用 +~~~~~~~~~~~~ + +1. 定义实现``Syscall`` trait的结构体 +2. 实现``handle()``和``entry_format()``方法 +3. 使用``declare_syscall!``宏注册 + +参考实现:`sys_write.rs `_ + +.. _sys_write: + https://github.com/DragonOS-Community/DragonOS/blob/master/kernel/src/filesystem/vfs/syscall/sys_write.rs + +注册系统调用 +~~~~~~~~~~~~ + +使用``declare_syscall!``宏注册系统调用: + +.. code-block:: rust + + syscall_table_macros::declare_syscall!(SYS_WRITE, SysWriteHandle); + +参数说明: + +1. 系统调用名称(用于生成符号) +2. 实现``Syscall`` trait的结构体 + +初始化流程 +---------- + +1. 内核启动时调用``syscall_table_init()`` +2. 从链接器符号``_syscall_table``加载所有注册的系统调用 +3. 填充系统调用表 diff --git a/docs/requirements.txt b/docs/requirements.txt index ce664001..6ba70944 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,5 @@ sphinx==5.0.2 myst-parser==0.18.0 sphinx-rtd-theme +sphinxcontrib-mermaid==1.0.0 git+https://git.mirrors.dragonos.org.cn/DragonOS-Community/sphinx-multiversion.git@5858b75#egg=sphinx-multiversion diff --git a/docs/syscall_api/intro.md b/docs/syscall_api/intro.md deleted file mode 100644 index 941a1f85..00000000 --- a/docs/syscall_api/intro.md +++ /dev/null @@ -1 +0,0 @@ -# 简介 \ No newline at end of file diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 992df850..f9ef3930 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -531,6 +531,7 @@ dependencies = [ "slabmalloc", "smoltcp", "static-keys", + "syscall_table_macros", "system_error", "uefi", "uefi-raw", @@ -1665,6 +1666,10 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syscall_table_macros" +version = "0.1.0" + [[package]] name = "system_error" version = "0.1.0" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 0abfb04c..7fe70acc 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -60,6 +60,7 @@ smoltcp = { version = "=0.11.0", default-features = false, features = [ "proto-ipv4", "proto-ipv6", ] } +syscall_table_macros = { path = "crates/syscall_table_macros" } system_error = { path = "crates/system_error" } uefi = { version = "=0.26.0", features = ["alloc"] } uefi-raw = "=0.5.0" diff --git a/kernel/crates/syscall_table_macros/Cargo.toml b/kernel/crates/syscall_table_macros/Cargo.toml new file mode 100644 index 00000000..709d3861 --- /dev/null +++ b/kernel/crates/syscall_table_macros/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "syscall_table_macros" +version = "0.1.0" +edition = "2021" + +[dependencies] + diff --git a/kernel/crates/syscall_table_macros/src/lib.rs b/kernel/crates/syscall_table_macros/src/lib.rs new file mode 100644 index 00000000..c5c42b50 --- /dev/null +++ b/kernel/crates/syscall_table_macros/src/lib.rs @@ -0,0 +1,19 @@ +#![no_std] +#![deny(clippy::all)] + +#[macro_export] +#[allow(clippy::crate_in_macro_def)] +macro_rules! declare_syscall { + ($nr:ident, $inner_handle:ident) => { + paste::paste! { + #[allow(non_upper_case_globals)] + #[link_section = ".syscall_table"] + #[used] + pub static []: crate::syscall::table::SyscallHandle = crate::syscall::table::SyscallHandle { + nr: $nr, + inner_handle: &$inner_handle, + name: stringify!($nr), + }; + } + }; +} diff --git a/kernel/src/arch/loongarch64/link.ld b/kernel/src/arch/loongarch64/link.ld index 42b51cf3..be30520b 100644 --- a/kernel/src/arch/loongarch64/link.ld +++ b/kernel/src/arch/loongarch64/link.ld @@ -74,8 +74,18 @@ SECTIONS *(.tracepoint.*) _etracepoint = .; } - . = ALIGN(32768); + . = ALIGN(4096); + syscall_table_start_pa = .; + .syscall_table (syscall_table_start_pa): + { + _syscall_table = .; + *(.syscall_table) + *(.syscall_table.*) + _esyscall_table = .; + } + + . = ALIGN(32768); init_proc_union_start_pa = .; .data.init_proc_union (init_proc_union_start_pa): { *(.data.init_proc_union) } diff --git a/kernel/src/arch/riscv64/link.ld b/kernel/src/arch/riscv64/link.ld index 1a726a65..6b7de40e 100644 --- a/kernel/src/arch/riscv64/link.ld +++ b/kernel/src/arch/riscv64/link.ld @@ -76,8 +76,19 @@ SECTIONS *(.tracepoint.*) _etracepoint = .; } - . = ALIGN(32768); + . = ALIGN(4096); + + syscall_table_start_pa = .; + .syscall_table (syscall_table_start_pa): AT(syscall_table_start_pa - KERNEL_VMA) + { + _syscall_table = .; + *(.syscall_table) + *(.syscall_table.*) + _esyscall_table = .; + } + + . = ALIGN(32768); init_proc_union_start_pa = .; .data.init_proc_union (init_proc_union_start_pa): AT(init_proc_union_start_pa - KERNEL_VMA) { *(.data.init_proc_union) } diff --git a/kernel/src/arch/x86_64/link.lds b/kernel/src/arch/x86_64/link.lds index 04e05880..79c4d889 100644 --- a/kernel/src/arch/x86_64/link.lds +++ b/kernel/src/arch/x86_64/link.lds @@ -40,7 +40,7 @@ SECTIONS _etext = .; __etext = .; } - . = ALIGN(32768); + . = ALIGN(4096); data_start_pa = .; .data (data_start_pa): AT(data_start_pa - KERNEL_VMA) { @@ -51,7 +51,7 @@ SECTIONS _edata = .; } - . = ALIGN(32768); + . = ALIGN(4096); rodata_start_pa = .; .rodata (rodata_start_pa): AT(rodata_start_pa - KERNEL_VMA) @@ -65,7 +65,7 @@ SECTIONS _erodata = .; } - . = ALIGN(32768); + . = ALIGN(4096); trace_point_start_pa = .; .tracepoint (trace_point_start_pa): AT(trace_point_start_pa - KERNEL_VMA) @@ -75,8 +75,18 @@ SECTIONS *(.tracepoint.*) _etracepoint = .; } - . = ALIGN(32768); + . = ALIGN(4096); + syscall_table_start_pa = .; + .syscall_table (syscall_table_start_pa): AT(syscall_table_start_pa - KERNEL_VMA) + { + _syscall_table = .; + *(.syscall_table) + *(.syscall_table.*) + _esyscall_table = .; + } + + . = ALIGN(32768); init_proc_union_start_pa = .; .data.init_proc_union (init_proc_union_start_pa): AT(init_proc_union_start_pa - KERNEL_VMA) { *(.data.init_proc_union) } diff --git a/kernel/src/filesystem/mbr.rs b/kernel/src/filesystem/mbr.rs index 9cf6eedd..f44b801b 100644 --- a/kernel/src/filesystem/mbr.rs +++ b/kernel/src/filesystem/mbr.rs @@ -4,7 +4,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::debug; use system_error::SystemError; use crate::{ @@ -106,7 +105,7 @@ impl MbrDiskPartionTable { table.dpte[i].starting_lba = cursor.read_u32()?; table.dpte[i].total_sectors = cursor.read_u32()?; - debug!("dpte[{i}] = {:?}", table.dpte[i]); + // debug!("dpte[{i}] = {:?}", table.dpte[i]); } table.bs_trailsig = cursor.read_u16()?; // debug!("bs_trailsig = {}", unsafe { diff --git a/kernel/src/filesystem/vfs/iov.rs b/kernel/src/filesystem/vfs/iov.rs new file mode 100644 index 00000000..5943e495 --- /dev/null +++ b/kernel/src/filesystem/vfs/iov.rs @@ -0,0 +1,149 @@ +use alloc::vec::Vec; +use system_error::SystemError; + +use crate::syscall::user_access::{UserBufferReader, UserBufferWriter}; +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct IoVec { + /// 缓冲区的起始地址 + pub iov_base: *mut u8, + /// 缓冲区的长度 + pub iov_len: usize, +} + +/// 用于存储多个来自用户空间的IoVec +/// +/// 由于目前内核中的文件系统还不支持分散读写,所以暂时只支持将用户空间的IoVec聚合成一个缓冲区,然后进行操作。 +/// TODO:支持分散读写 +#[derive(Debug)] +pub struct IoVecs(Vec); + +impl IoVecs { + /// 获取IoVecs中所有缓冲区的总长度 + #[inline(never)] + pub fn total_len(&self) -> usize { + self.0.iter().map(|x| x.iov_len).sum() + } + + /// Constructs `IoVecs` from an array of `IoVec` in userspace. + /// + /// # Arguments + /// + /// * `iov` - Pointer to the array of `IoVec` in userspace + /// * `iovcnt` - Number of `IoVec` elements in the array + /// * `readv` - Whether this is for the `readv` syscall (currently unused) + /// + /// # Returns + /// + /// Returns `Ok(IoVecs)` on success, or `Err(SystemError)` if any error occurs. + /// + /// # Safety + /// + /// This function is unsafe because it operates on raw pointers from userspace. + /// The caller must ensure: + /// - The pointer `iov` is valid and points to at least `iovcnt` valid `IoVec` structures + /// - The userspace memory is not modified during this operation + #[inline(never)] + pub unsafe fn from_user( + iov: *const IoVec, + iovcnt: usize, + _readv: bool, + ) -> Result { + let iovs_reader = UserBufferReader::new(iov, iovcnt * core::mem::size_of::(), true)?; + + // 将用户空间的IoVec转换为引用(注意:这里的引用是静态的,因为用户空间的IoVec不会被释放) + let iovs = iovs_reader.buffer::(0)?; + + let mut slices: Vec = Vec::with_capacity(iovs.len()); + + for iov in iovs.iter() { + if iov.iov_len == 0 { + continue; + } + + let _ = UserBufferWriter::new(iov.iov_base, iov.iov_len, true)?; + slices.push(*iov); + } + + return Ok(Self(slices)); + } + + /// Aggregates data from all IoVecs into a single buffer. + /// + /// This function reads data from each IoVec in sequence and combines them into + /// a single contiguous buffer. + /// + /// # Returns + /// + /// Returns a [`Vec`] containing all the data from the IoVecs. + /// + /// # Examples + /// + /// ```rust + /// let iovecs = IoVecs::from_user(/* ... */)?; + /// let buffer = iovecs.gather(); + /// ``` + pub fn gather(&self) -> Vec { + let mut buf = Vec::new(); + for slice in self.0.iter() { + let buf_reader = UserBufferReader::new(slice.iov_base, slice.iov_len, true).unwrap(); + let slice = buf_reader.buffer::(0).unwrap(); + buf.extend_from_slice(slice); + } + return buf; + } + + /// Scatters the given data into the IoVecs. + /// + /// This function writes data sequentially to each IoVec, splitting the input data + /// across multiple buffers as needed. If the input data is smaller than the total + /// capacity of the IoVecs, only the required amount of data will be written. + /// If the input data is larger than the total capacity, the excess data will be ignored. + /// + /// # Arguments + /// + /// * `data` - The data to be scattered across the IoVecs + /// + /// # Examples + /// + /// ```rust + /// let iovecs = IoVecs::from_user(/* ... */)?; + /// iovecs.scatter(&[1, 2, 3, 4, 5]); + /// ``` + pub fn scatter(&self, data: &[u8]) { + let mut data: &[u8] = data; + for slice in self.0.iter() { + let len = core::cmp::min(slice.iov_len, data.len()); + if len == 0 { + continue; + } + + let mut buf_writer = + UserBufferWriter::new(slice.iov_base, slice.iov_len, true).unwrap(); + let slice = buf_writer.buffer::(0).unwrap(); + + slice[..len].copy_from_slice(&data[..len]); + data = &data[len..]; + } + } + + /// Creates a buffer with capacity equal to the total length of all IoVecs. + /// + /// # Arguments + /// + /// * `set_len` - If true, sets the length of the returned Vec to the total length of all IoVecs. + /// If false, the returned Vec will have length 0 but capacity equal to the total length. + /// + /// # Returns + /// + /// A new [`Vec`] with capacity (and potentially length) equal to the total length of all IoVecs. + pub fn new_buf(&self, set_len: bool) -> Vec { + let total_len = self.total_len(); + let mut buf: Vec = Vec::with_capacity(total_len); + + if set_len { + buf.resize(total_len, 0); + } + return buf; + } +} diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 55df722d..ffa639ec 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -1,5 +1,6 @@ pub mod fcntl; pub mod file; +pub mod iov; pub mod mount; pub mod open; pub mod stat; diff --git a/kernel/src/filesystem/vfs/syscall.rs b/kernel/src/filesystem/vfs/syscall/mod.rs similarity index 90% rename from kernel/src/filesystem/vfs/syscall.rs rename to kernel/src/filesystem/vfs/syscall/mod.rs index 2ec58b7f..b5c7c717 100644 --- a/kernel/src/filesystem/vfs/syscall.rs +++ b/kernel/src/filesystem/vfs/syscall/mod.rs @@ -12,7 +12,7 @@ use crate::{ driver::base::{block::SeekFrom, device::device_number::DeviceNumber}, filesystem::vfs::{file::FileDescriptorVec, vcore as Vcore}, libs::rwlock::RwLockWriteGuard, - mm::{verify_area, VirtAddr}, + mm::VirtAddr, process::ProcessManager, syscall::{ user_access::{self, check_and_clone_cstr, UserBufferWriter}, @@ -35,6 +35,11 @@ use super::{ VFS_MAX_FOLLOW_SYMLINK_TIMES, }; +mod sys_read; +mod sys_readv; +mod sys_write; +mod sys_writev; + pub const SEEK_SET: u32 = 0; pub const SEEK_CUR: u32 = 1; pub const SEEK_END: u32 = 2; @@ -497,48 +502,6 @@ impl Syscall { return r; } - /// @brief 根据文件描述符,读取文件数据。尝试读取的数据长度与buf的长度相同。 - /// - /// @param fd 文件描述符编号 - /// @param buf 输出缓冲区 - /// - /// @return Ok(usize) 成功读取的数据的字节数 - /// @return Err(SystemError) 读取失败,返回posix错误码 - pub fn read(fd: i32, buf: &mut [u8]) -> Result { - let binding = ProcessManager::current_pcb().fd_table(); - let fd_table_guard = binding.read(); - - let file = fd_table_guard.get_file_by_fd(fd); - if file.is_none() { - return Err(SystemError::EBADF); - } - // drop guard 以避免无法调度的问题 - drop(fd_table_guard); - let file = file.unwrap(); - - return file.read(buf.len(), buf); - } - - /// @brief 根据文件描述符,向文件写入数据。尝试写入的数据长度与buf的长度相同。 - /// - /// @param fd 文件描述符编号 - /// @param buf 输入缓冲区 - /// - /// @return Ok(usize) 成功写入的数据的字节数 - /// @return Err(SystemError) 写入失败,返回posix错误码 - pub fn write(fd: i32, buf: &[u8]) -> Result { - let binding = ProcessManager::current_pcb().fd_table(); - let fd_table_guard = binding.read(); - - let file = fd_table_guard - .get_file_by_fd(fd) - .ok_or(SystemError::EBADF)?; - - // drop guard 以避免无法调度的问题 - drop(fd_table_guard); - return file.write(buf.len(), buf); - } - /// @brief 调整文件操作指针的位置 /// /// @param fd 文件描述符编号 @@ -1421,28 +1384,6 @@ impl Syscall { return Ok(0); } - pub fn writev(fd: i32, iov: usize, count: usize) -> Result { - // IoVecs会进行用户态检验 - let iovecs = unsafe { IoVecs::from_user(iov as *const IoVec, count, false) }?; - - let data = iovecs.gather(); - - Self::write(fd, &data) - } - - pub fn readv(fd: i32, iov: usize, count: usize) -> Result { - // IoVecs会进行用户态检验 - let mut iovecs = unsafe { IoVecs::from_user(iov as *const IoVec, count, true) }?; - - let mut data = vec![0; iovecs.0.iter().map(|x| x.len()).sum()]; - - let len = Self::read(fd, &mut data)?; - - iovecs.scatter(&data[..len]); - - return Ok(len); - } - pub fn readlink_at( dirfd: i32, path: *const u8, @@ -1675,104 +1616,3 @@ impl Syscall { do_utimes(&pathname, times) } } - -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct IoVec { - /// 缓冲区的起始地址 - pub iov_base: *mut u8, - /// 缓冲区的长度 - pub iov_len: usize, -} - -/// 用于存储多个来自用户空间的IoVec -/// -/// 由于目前内核中的文件系统还不支持分散读写,所以暂时只支持将用户空间的IoVec聚合成一个缓冲区,然后进行操作。 -/// TODO:支持分散读写 -#[derive(Debug)] -pub struct IoVecs(Vec<&'static mut [u8]>); - -impl IoVecs { - /// 从用户空间的IoVec中构造IoVecs - /// - /// @param iov 用户空间的IoVec - /// @param iovcnt 用户空间的IoVec的数量 - /// @param readv 是否为readv系统调用 - /// - /// @return 构造成功返回IoVecs,否则返回错误码 - pub unsafe fn from_user( - iov: *const IoVec, - iovcnt: usize, - _readv: bool, - ) -> Result { - // 检查iov指针所在空间是否合法 - verify_area( - VirtAddr::new(iov as usize), - iovcnt * core::mem::size_of::(), - ) - .map_err(|_| SystemError::EFAULT)?; - - // 将用户空间的IoVec转换为引用(注意:这里的引用是静态的,因为用户空间的IoVec不会被释放) - let iovs: &[IoVec] = core::slice::from_raw_parts(iov, iovcnt); - - let mut slices: Vec<&mut [u8]> = Vec::with_capacity(iovs.len()); - - for iov in iovs.iter() { - if iov.iov_len == 0 { - continue; - } - - verify_area( - VirtAddr::new(iov.iov_base as usize), - iovcnt * core::mem::size_of::(), - ) - .map_err(|_| SystemError::EFAULT)?; - - slices.push(core::slice::from_raw_parts_mut(iov.iov_base, iov.iov_len)); - } - - return Ok(Self(slices)); - } - - /// @brief 将IoVecs中的数据聚合到一个缓冲区中 - /// - /// @return 返回聚合后的缓冲区 - pub fn gather(&self) -> Vec { - let mut buf = Vec::new(); - for slice in self.0.iter() { - buf.extend_from_slice(slice); - } - return buf; - } - - /// @brief 将给定的数据分散写入到IoVecs中 - pub fn scatter(&mut self, data: &[u8]) { - let mut data: &[u8] = data; - for slice in self.0.iter_mut() { - let len = core::cmp::min(slice.len(), data.len()); - if len == 0 { - continue; - } - - slice[..len].copy_from_slice(&data[..len]); - data = &data[len..]; - } - } - - /// @brief 创建与IoVecs等长的缓冲区 - /// - /// @param set_len 是否设置返回的Vec的len。 - /// 如果为true,则返回的Vec的len为所有IoVec的长度之和; - /// 否则返回的Vec的len为0,capacity为所有IoVec的长度之和. - /// - /// @return 返回创建的缓冲区 - pub fn new_buf(&self, set_len: bool) -> Vec { - let total_len: usize = self.0.iter().map(|slice| slice.len()).sum(); - let mut buf: Vec = Vec::with_capacity(total_len); - - if set_len { - buf.resize(total_len, 0); - } - return buf; - } -} diff --git a/kernel/src/filesystem/vfs/syscall/sys_read.rs b/kernel/src/filesystem/vfs/syscall/sys_read.rs new file mode 100644 index 00000000..be9f43d0 --- /dev/null +++ b/kernel/src/filesystem/vfs/syscall/sys_read.rs @@ -0,0 +1,105 @@ +use system_error::SystemError; + +use crate::arch::syscall::nr::SYS_READ; +use crate::process::ProcessManager; +use crate::syscall::table::FormattedSyscallParam; +use crate::syscall::table::Syscall; +use crate::syscall::user_access::UserBufferWriter; + +use alloc::string::ToString; +use alloc::vec::Vec; + +/// System call handler for the `read` syscall +/// +/// This handler implements the `Syscall` trait to provide functionality for reading data from a file descriptor. +pub struct SysReadHandle; + +impl Syscall for SysReadHandle { + /// Returns the number of arguments expected by the `read` syscall + fn num_args(&self) -> usize { + 3 + } + + /// Handles the `read` system call + /// + /// Reads data from the specified file descriptor into a user buffer. + /// + /// # Arguments + /// * `args` - Array containing: + /// - args[0]: File descriptor (i32) + /// - args[1]: Pointer to user buffer (*mut u8) + /// - args[2]: Length of data to read (usize) + /// * `from_user` - Indicates if the call originates from user space + /// + /// # Returns + /// * `Ok(usize)` - Number of bytes successfully read + /// * `Err(SystemError)` - Error code if operation fails + fn handle(&self, args: &[usize], from_user: bool) -> Result { + let fd = Self::fd(args); + let buf_vaddr = Self::buf(args); + let len = Self::len(args); + + let mut user_buffer_writer = UserBufferWriter::new(buf_vaddr, len, from_user)?; + + let user_buf = user_buffer_writer.buffer(0)?; + do_read(fd, user_buf) + } + + /// Formats the syscall parameters for display/debug purposes + /// + /// # Arguments + /// * `args` - The raw syscall arguments + /// + /// # Returns + /// Vector of formatted parameters with descriptive names + fn entry_format(&self, args: &[usize]) -> Vec { + vec![ + FormattedSyscallParam::new("fd", Self::fd(args).to_string()), + FormattedSyscallParam::new("buf", format!("{:#x}", Self::buf(args) as usize)), + FormattedSyscallParam::new("len", Self::len(args).to_string()), + ] + } +} + +impl SysReadHandle { + /// Extracts the file descriptor from syscall arguments + fn fd(args: &[usize]) -> i32 { + args[0] as i32 + } + + /// Extracts the buffer pointer from syscall arguments + fn buf(args: &[usize]) -> *mut u8 { + args[1] as *mut u8 + } + + /// Extracts the buffer length from syscall arguments + fn len(args: &[usize]) -> usize { + args[2] + } +} + +syscall_table_macros::declare_syscall!(SYS_READ, SysReadHandle); + +/// Internal implementation of the read operation +/// +/// # Arguments +/// * `fd` - File descriptor to read from +/// * `buf` - Buffer to store read data +/// +/// # Returns +/// * `Ok(usize)` - Number of bytes successfully read +/// * `Err(SystemError)` - Error code if operation fails +pub(super) fn do_read(fd: i32, buf: &mut [u8]) -> Result { + let binding = ProcessManager::current_pcb().fd_table(); + let fd_table_guard = binding.read(); + + let file = fd_table_guard.get_file_by_fd(fd); + if file.is_none() { + return Err(SystemError::EBADF); + } + // drop guard 以避免无法调度的问题 + drop(fd_table_guard); + let file = file.unwrap(); + + return file.read(buf.len(), buf); +} diff --git a/kernel/src/filesystem/vfs/syscall/sys_readv.rs b/kernel/src/filesystem/vfs/syscall/sys_readv.rs new file mode 100644 index 00000000..70133d74 --- /dev/null +++ b/kernel/src/filesystem/vfs/syscall/sys_readv.rs @@ -0,0 +1,65 @@ +use system_error::SystemError; + +use crate::arch::syscall::nr::SYS_READV; +use crate::filesystem::vfs::iov::IoVec; +use crate::filesystem::vfs::iov::IoVecs; +use crate::syscall::table::FormattedSyscallParam; +use crate::syscall::table::Syscall; + +use alloc::string::ToString; +use alloc::vec::Vec; + +use super::sys_read::do_read; + +/// System call handler for `readv` operation +/// +/// The `readv` system call reads data into multiple buffers from a file descriptor. +/// It is equivalent to multiple `read` calls but is more efficient. +pub struct SysReadVHandle; + +impl Syscall for SysReadVHandle { + fn num_args(&self) -> usize { + 3 + } + + fn handle(&self, args: &[usize], _from_user: bool) -> Result { + let fd = Self::fd(args); + let iov = Self::iov(args); + let count = Self::count(args); + + // IoVecs会进行用户态检验 + let iovecs = unsafe { IoVecs::from_user(iov, count, true) }?; + + let mut data = vec![0; iovecs.total_len()]; + + let len = do_read(fd, &mut data)?; + + iovecs.scatter(&data[..len]); + + return Ok(len); + } + + fn entry_format(&self, args: &[usize]) -> Vec { + vec![ + FormattedSyscallParam::new("fd", Self::fd(args).to_string()), + FormattedSyscallParam::new("iov", format!("{:#x}", Self::iov(args) as usize)), + FormattedSyscallParam::new("count", Self::count(args).to_string()), + ] + } +} + +impl SysReadVHandle { + fn fd(args: &[usize]) -> i32 { + args[0] as i32 + } + + fn iov(args: &[usize]) -> *const IoVec { + args[1] as *const IoVec + } + + fn count(args: &[usize]) -> usize { + args[2] + } +} + +syscall_table_macros::declare_syscall!(SYS_READV, SysReadVHandle); diff --git a/kernel/src/filesystem/vfs/syscall/sys_write.rs b/kernel/src/filesystem/vfs/syscall/sys_write.rs new file mode 100644 index 00000000..246bc41e --- /dev/null +++ b/kernel/src/filesystem/vfs/syscall/sys_write.rs @@ -0,0 +1,104 @@ +use system_error::SystemError; + +use crate::arch::syscall::nr::SYS_WRITE; +use crate::process::ProcessManager; +use crate::syscall::table::FormattedSyscallParam; +use crate::syscall::table::Syscall; +use crate::syscall::user_access::UserBufferReader; + +use alloc::string::ToString; +use alloc::vec::Vec; + +/// System call handler for the `write` syscall +/// +/// This handler implements the `Syscall` trait to provide functionality for writing data to a file descriptor. +pub struct SysWriteHandle; + +impl Syscall for SysWriteHandle { + /// Returns the number of arguments expected by the `write` syscall + fn num_args(&self) -> usize { + 3 + } + + /// Handles the `write` system call + /// + /// Writes data from a user buffer to the specified file descriptor. + /// + /// # Arguments + /// * `args` - Array containing: + /// - args[0]: File descriptor (i32) + /// - args[1]: Pointer to user buffer (*const u8) + /// - args[2]: Length of data to write (usize) + /// * `from_user` - Indicates if the call originates from user space + /// + /// # Returns + /// * `Ok(usize)` - Number of bytes successfully written + /// * `Err(SystemError)` - Error code if operation fails + fn handle(&self, args: &[usize], from_user: bool) -> Result { + let fd = Self::fd(args); + let buf_vaddr = Self::buf(args); + let len = Self::len(args); + + let user_buffer_reader = UserBufferReader::new(buf_vaddr, len, from_user)?; + + let user_buf = user_buffer_reader.read_from_user(0)?; + + do_write(fd, user_buf) + } + + /// Formats the syscall parameters for display/debug purposes + /// + /// # Arguments + /// * `args` - The raw syscall arguments + /// + /// # Returns + /// Vector of formatted parameters with descriptive names + fn entry_format(&self, args: &[usize]) -> Vec { + vec![ + FormattedSyscallParam::new("fd", Self::fd(args).to_string()), + FormattedSyscallParam::new("buf", format!("{:#x}", Self::buf(args) as usize)), + FormattedSyscallParam::new("len", Self::len(args).to_string()), + ] + } +} + +impl SysWriteHandle { + /// Extracts the file descriptor from syscall arguments + fn fd(args: &[usize]) -> i32 { + args[0] as i32 + } + + /// Extracts the buffer pointer from syscall arguments + fn buf(args: &[usize]) -> *const u8 { + args[1] as *const u8 + } + + /// Extracts the buffer length from syscall arguments + fn len(args: &[usize]) -> usize { + args[2] + } +} + +syscall_table_macros::declare_syscall!(SYS_WRITE, SysWriteHandle); + +/// Internal implementation of the write operation +/// +/// # Arguments +/// * `fd` - File descriptor to write to +/// * `buf` - Buffer containing data to write +/// +/// # Returns +/// * `Ok(usize)` - Number of bytes successfully written +/// * `Err(SystemError)` - Error code if operation fails +pub(super) fn do_write(fd: i32, buf: &[u8]) -> Result { + let binding = ProcessManager::current_pcb().fd_table(); + let fd_table_guard = binding.read(); + + let file = fd_table_guard + .get_file_by_fd(fd) + .ok_or(SystemError::EBADF)?; + + // drop guard 以避免无法调度的问题 + drop(fd_table_guard); + return file.write(buf.len(), buf); +} diff --git a/kernel/src/filesystem/vfs/syscall/sys_writev.rs b/kernel/src/filesystem/vfs/syscall/sys_writev.rs new file mode 100644 index 00000000..e3390604 --- /dev/null +++ b/kernel/src/filesystem/vfs/syscall/sys_writev.rs @@ -0,0 +1,85 @@ +use system_error::SystemError; + +use crate::arch::syscall::nr::SYS_WRITEV; +use crate::filesystem::vfs::iov::IoVec; +use crate::filesystem::vfs::iov::IoVecs; +use crate::syscall::table::FormattedSyscallParam; +use crate::syscall::table::Syscall; + +use alloc::string::ToString; +use alloc::vec::Vec; + +use super::sys_write::do_write; + +/// System call handler for `writev` operation +/// +/// The `writev` system call writes data from multiple buffers to a file descriptor. +/// It is equivalent to multiple `write` calls but is more efficient. +pub struct SysWriteVHandle; + +impl Syscall for SysWriteVHandle { + /// Returns the number of arguments required by the `writev` system call + fn num_args(&self) -> usize { + 3 + } + + /// Handles the `writev` system call + /// + /// # Arguments + /// * `args` - System call arguments containing: + /// * `fd`: File descriptor to write to + /// * `iov`: Pointer to array of I/O vectors + /// * `count`: Number of elements in the I/O vector array + /// * `_from_user` - Flag indicating if the call originated from user space + /// + /// # Returns + /// * `Ok(usize)` - Number of bytes written + /// * `Err(SystemError)` - Error that occurred during operation + /// + /// # Safety + /// The caller must ensure the `iov` pointer is valid and points to properly initialized memory. + fn handle(&self, args: &[usize], _from_user: bool) -> Result { + let fd = Self::fd(args); + let iov = Self::iov(args); + let count = Self::count(args); + + // IoVecs会进行用户态检验 + let iovecs = unsafe { IoVecs::from_user(iov, count, false) }?; + let data = iovecs.gather(); + do_write(fd, &data) + } + + /// Formats the system call parameters for display/debug purposes + /// + /// # Arguments + /// * `args` - System call arguments to format + /// + /// # Returns + /// Vector of formatted parameters with their names and values + fn entry_format(&self, args: &[usize]) -> Vec { + vec![ + FormattedSyscallParam::new("fd", Self::fd(args).to_string()), + FormattedSyscallParam::new("iov", format!("{:#x}", Self::iov(args) as usize)), + FormattedSyscallParam::new("count", Self::count(args).to_string()), + ] + } +} + +impl SysWriteVHandle { + /// Extracts the file descriptor from system call arguments + fn fd(args: &[usize]) -> i32 { + args[0] as i32 + } + + /// Extracts the I/O vector pointer from system call arguments + fn iov(args: &[usize]) -> *const IoVec { + args[1] as *const IoVec + } + + /// Extracts the I/O vector count from system call arguments + fn count(args: &[usize]) -> usize { + args[2] + } +} + +syscall_table_macros::declare_syscall!(SYS_WRITEV, SysWriteVHandle); diff --git a/kernel/src/init/init.rs b/kernel/src/init/init.rs index 43ecdd79..8907c88e 100644 --- a/kernel/src/init/init.rs +++ b/kernel/src/init/init.rs @@ -23,7 +23,7 @@ use crate::{ process::{kthread::kthread_init, process_init, ProcessManager}, sched::SchedArch, smp::{early_smp_init, SMPArch}, - syscall::Syscall, + syscall::{syscall_init, Syscall}, time::{ clocksource::clocksource_boot_finish, timekeeping::timekeeping_init, timer::timer_init, }, @@ -69,6 +69,8 @@ fn do_start_kernel() { init_intertrait(); + syscall_init().expect("syscall init failed"); + vfs_init().expect("vfs init failed"); driver_init().expect("driver init failed"); diff --git a/kernel/src/net/syscall.rs b/kernel/src/net/syscall.rs index 91aff6eb..7f841313 100644 --- a/kernel/src/net/syscall.rs +++ b/kernel/src/net/syscall.rs @@ -8,7 +8,7 @@ use system_error::SystemError; use crate::{ filesystem::vfs::{ file::{File, FileMode}, - syscall::{IoVec, IoVecs}, + iov::{IoVec, IoVecs}, FileType, }, libs::spinlock::SpinLockGuard, @@ -295,7 +295,7 @@ impl Syscall { /// @return 成功返回接收的字节数,失败返回错误码 pub fn recvmsg(fd: usize, msg: &mut MsgHdr, _flags: u32) -> Result { // 检查每个缓冲区地址是否合法,生成iovecs - let mut iovs = unsafe { IoVecs::from_user(msg.msg_iov, msg.msg_iovlen, true)? }; + let iovs = unsafe { IoVecs::from_user(msg.msg_iov, msg.msg_iovlen, true)? }; let socket: Arc = ProcessManager::current_pcb() .get_socket(fd as i32) diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index b8ed05f6..2cc41f8f 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -23,6 +23,7 @@ use crate::{ use log::{info, warn}; use num_traits::FromPrimitive; use system_error::SystemError; +use table::{syscall_table, syscall_table_init}; use crate::{ arch::{interrupt::TrapFrame, MMArch}, @@ -49,6 +50,7 @@ use self::{ }; pub mod misc; +pub mod table; pub mod user_access; // 与linux不一致的调用,在linux基础上累加 @@ -99,6 +101,18 @@ impl Syscall { args: &[usize], frame: &mut TrapFrame, ) -> Result { + // 首先尝试从syscall_table获取处理函数 + if let Some(handler) = syscall_table().get(syscall_num) { + // 使用以下代码可以打印系统调用号和参数,方便调试 + // log::debug!( + // "Syscall {} called with args {}", + // handler.name, + // handler.args_string(args) + // ); + return handler.inner_handle.handle(args, frame.is_from_user()); + } + + // 如果找不到,fallback到原有逻辑 let r = match syscall_num { SYS_PUT_STRING => { Self::put_string(args[0] as *const u8, args[1] as u32, args[2] as u32) @@ -155,28 +169,6 @@ impl Syscall { let fd = args[0]; Self::close(fd) } - SYS_READ => { - let fd = args[0] as i32; - let buf_vaddr = args[1]; - let len = args[2]; - let from_user = frame.is_from_user(); - let mut user_buffer_writer = - UserBufferWriter::new(buf_vaddr as *mut u8, len, from_user)?; - - let user_buf = user_buffer_writer.buffer(0)?; - Self::read(fd, user_buf) - } - SYS_WRITE => { - let fd = args[0] as i32; - let buf_vaddr = args[1]; - let len = args[2]; - let from_user = frame.is_from_user(); - let user_buffer_reader = - UserBufferReader::new(buf_vaddr as *const u8, len, from_user)?; - - let user_buf = user_buffer_reader.read_from_user(0)?; - Self::write(fd, user_buf) - } SYS_LSEEK => { let fd = args[0] as i32; @@ -781,9 +773,6 @@ impl Syscall { return ret; } - SYS_READV => Self::readv(args[0] as i32, args[1], args[2]), - SYS_WRITEV => Self::writev(args[0] as i32, args[1], args[2]), - SYS_SET_TID_ADDRESS => Self::set_tid_address(args[0]), #[cfg(target_arch = "x86_64")] @@ -1267,3 +1256,9 @@ impl Syscall { return Ok(s.len()); } } + +#[inline(never)] +pub fn syscall_init() -> Result<(), SystemError> { + syscall_table_init()?; + Ok(()) +} diff --git a/kernel/src/syscall/table.rs b/kernel/src/syscall/table.rs new file mode 100644 index 00000000..65d165cd --- /dev/null +++ b/kernel/src/syscall/table.rs @@ -0,0 +1,129 @@ +#![allow(unused)] + +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec::Vec; +use core::cell::OnceCell; +use core::fmt::Display; + +use crate::libs::once::Once; +use crate::syscall::SystemError; + +/// 定义Syscall trait +pub trait Syscall: Send + Sync + 'static { + /// 系统调用参数数量 + fn num_args(&self) -> usize; + fn handle(&self, args: &[usize], from_user: bool) -> Result; + + /// Formats the system call parameters for display/debug purposes + /// + /// # Arguments + /// * `args` - System call arguments to format + /// + /// # Returns + /// Vector of formatted parameters with their names and values + fn entry_format(&self, args: &[usize]) -> Vec; +} + +pub struct FormattedSyscallParam { + pub name: &'static str, + pub value: String, +} + +impl FormattedSyscallParam { + pub fn new(name: &'static str, value: String) -> Self { + Self { name, value } + } +} + +impl Display for FormattedSyscallParam { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}: {}", self.name, self.value) + } +} + +/// 系统调用处理句柄 +#[repr(C)] +pub struct SyscallHandle { + pub nr: usize, + pub inner_handle: &'static dyn Syscall, + pub name: &'static str, +} + +impl SyscallHandle { + #[inline(never)] + pub fn args_string(&self, args: &[usize]) -> String { + let args_slice = self.inner_handle.entry_format(args); + args_slice + .iter() + .map(|p| format!("{}", p)) + .collect::>() + .join(", ") + } +} + +/// 系统调用表类型 +#[repr(C)] +pub struct SyscallTable { + entries: [Option<&'static SyscallHandle>; Self::ENTRIES], +} + +impl SyscallTable { + pub const ENTRIES: usize = 512; + /// 获取系统调用处理函数 + pub fn get(&self, nr: usize) -> Option<&'static SyscallHandle> { + *self.entries.get(nr)? + } +} + +// 声明外部链接的syscall_table符号 +extern "C" { + fn _syscall_table(); + fn _esyscall_table(); +} + +/// 全局系统调用表实例 +#[used] +#[link_section = ".data"] +static mut SYS_CALL_TABLE: SyscallTable = SyscallTable { + entries: [None; SyscallTable::ENTRIES], +}; + +#[inline] +pub(super) fn syscall_table() -> &'static SyscallTable { + unsafe { &SYS_CALL_TABLE } +} + +/// 初始化系统调用表 +#[inline(never)] +pub(super) fn syscall_table_init() -> Result<(), SystemError> { + static INIT: Once = Once::new(); + INIT.call_once(|| { + log::debug!("Initializing syscall table..."); + + // 初始化系统调用表 + unsafe { + let start = _syscall_table as usize; + let end = _esyscall_table as usize; + let size = end - start; + let count = size / core::mem::size_of::(); + + if size % core::mem::size_of::() != 0 { + panic!("Invalid syscall table size: {}", size); + } + + let handles = + core::slice::from_raw_parts(_syscall_table as *const SyscallHandle, count); + for handle in handles { + if handle.nr < SyscallTable::ENTRIES { + SYS_CALL_TABLE.entries[handle.nr] = Some(handle); + } else { + panic!("Invalid syscall number: {}", handle.nr); + } + } + + log::debug!("Syscall table (count: {count}) initialized successfully.") + } + }); + Ok(()) +} diff --git a/user/apps/riscv_init/Makefile b/user/apps/riscv_init/Makefile index 87f86453..8df0859b 100644 --- a/user/apps/riscv_init/Makefile +++ b/user/apps/riscv_init/Makefile @@ -8,14 +8,12 @@ CC=$(CROSS_COMPILE)gcc .PHONY: all -all: main.c -# $(CC) -static -o init main.c +all: $(riscv_rust_init) $(MAKE) -C riscv_rust_init ARCH=$(ARCH) install .PHONY: install clean install: all $(MAKE) -C riscv_rust_init ARCH=$(ARCH) install -# mv init $(DADK_CURRENT_BUILD_DIR)/init clean: diff --git a/user/apps/riscv_init/main.c b/user/apps/riscv_init/main.c deleted file mode 100644 index aab66227..00000000 --- a/user/apps/riscv_init/main.c +++ /dev/null @@ -1,9 +0,0 @@ -#include - -int main() { - while(1){ - printf("\033[43;37mHello, World!\033[0m\n"); - sleep(1); - } - return 0; -} \ No newline at end of file diff --git a/user/dadk/config/riscv_init-0.1.0.toml b/user/dadk/config/riscv_init-0.1.0.toml index 385c655a..636099bf 100644 --- a/user/dadk/config/riscv_init-0.1.0.toml +++ b/user/dadk/config/riscv_init-0.1.0.toml @@ -29,7 +29,7 @@ build-command = "make install" # 安装相关信息 [install] # (可选)安装到DragonOS的路径 -in-dragonos-path = "/bin" +in-dragonos-path = "/" # 清除相关信息 [clean] # (可选)清除命令