diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index f9113a128..88ad306ab 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -11,14 +11,14 @@ jobs: name: Format check ${{ matrix.arch }} runs-on: ubuntu-latest continue-on-error: true - container: dragonos/dragonos-dev:v1.7 + container: dragonos/dragonos-dev:v1.8 strategy: matrix: arch: [x86_64, riscv64] steps: - - run: echo "Running in dragonos/dragonos-dev:v1.7" + - run: echo "Running in dragonos/dragonos-dev:v1.8" - 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.7 + container: dragonos/dragonos-dev:v1.8 strategy: matrix: arch: [x86_64, riscv64] steps: - - run: echo "Running in dragonos/dragonos-dev:v1.7" + - run: echo "Running in dragonos/dragonos-dev:v1.8" - uses: actions/checkout@v3 @@ -55,10 +55,10 @@ jobs: build-x86_64: runs-on: ubuntu-latest - container: dragonos/dragonos-dev:v1.7 + container: dragonos/dragonos-dev:v1.8 steps: - - run: echo "Running in dragonos/dragonos-dev:v1.7" + - run: echo "Running in dragonos/dragonos-dev:v1.8" - uses: actions/checkout@v3 - name: build the DragonOS @@ -77,10 +77,10 @@ jobs: build-riscv64: runs-on: ubuntu-latest - container: dragonos/dragonos-dev:v1.7 + container: dragonos/dragonos-dev:v1.8 steps: - - run: echo "Running in dragonos/dragonos-dev:v1.7" + - run: echo "Running in dragonos/dragonos-dev:v1.8" - uses: actions/checkout@v3 with: diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 414df828b..617ed5d36 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -76,6 +76,7 @@ unwinding = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/unwi "panic", "personality" ]} +defer = "0.2.1" # target为x86_64时,使用下面的依赖 [target.'cfg(target_arch = "x86_64")'.dependencies] @@ -106,4 +107,4 @@ debug = true # Controls whether the compiler passes `-g` # The release profile, used for `cargo build --release` [profile.release] -debug = false +debug = true diff --git a/kernel/crates/rbpf/src/interpreter.rs b/kernel/crates/rbpf/src/interpreter.rs index cb4bddf3c..68b9878e8 100644 --- a/kernel/crates/rbpf/src/interpreter.rs +++ b/kernel/crates/rbpf/src/interpreter.rs @@ -660,9 +660,9 @@ pub fn execute_program( // Save the callee saved registers pre_stack.save_registers(®[6..=9]); // Save the return address - pre_stack.save_return_address(insn_ptr as u16); + pre_stack.save_return_address(insn_ptr as u64); // save the stack pointer - pre_stack.save_sp(reg[10] as u16); + pre_stack.save_sp(reg[10]); let mut stack = StackFrame::new(); log::trace!("BPF TO BPF CALL: new pc: {} + {} = {}",insn_ptr ,insn.imm,insn_ptr + insn.imm as usize); reg[10] = stack.as_ptr() as u64 + stack.len() as u64; @@ -695,7 +695,7 @@ pub fn execute_program( // Restore the return address insn_ptr = stack.get_return_address() as usize; // Restore the stack pointer - reg[10] = stack.get_sp() as u64; + reg[10] = stack.get_sp(); log::trace!("EXIT: new pc: {}", insn_ptr); } } diff --git a/kernel/crates/rbpf/src/stack.rs b/kernel/crates/rbpf/src/stack.rs index be1732638..c15968566 100644 --- a/kernel/crates/rbpf/src/stack.rs +++ b/kernel/crates/rbpf/src/stack.rs @@ -1,9 +1,9 @@ use crate::{ebpf::STACK_SIZE, vec, Vec}; pub struct StackFrame { - return_address: u16, + return_address: u64, saved_registers: [u64; 4], - sp: u16, + sp: u64, frame: Vec, } @@ -54,22 +54,22 @@ impl StackFrame { } /// Save the return address - pub fn save_return_address(&mut self, address: u16) { + pub fn save_return_address(&mut self, address: u64) { self.return_address = address; } /// Get the return address - pub fn get_return_address(&self) -> u16 { + pub fn get_return_address(&self) -> u64 { self.return_address } /// Save the stack pointer - pub fn save_sp(&mut self, sp: u16) { + pub fn save_sp(&mut self, sp: u64) { self.sp = sp; } /// Get the stack pointer - pub fn get_sp(&self) -> u16 { + pub fn get_sp(&self) -> u64 { self.sp } } diff --git a/kernel/crates/system_error/Cargo.toml b/kernel/crates/system_error/Cargo.toml index d166286a9..d143c3452 100644 --- a/kernel/crates/system_error/Cargo.toml +++ b/kernel/crates/system_error/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] num-traits = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/num-traits.git", rev="1597c1c", default-features = false } -num-derive = "0.3" \ No newline at end of file +num-derive = "0.3" diff --git a/kernel/crates/system_error/src/lib.rs b/kernel/crates/system_error/src/lib.rs index 441b4b13e..a1cd1dd7a 100644 --- a/kernel/crates/system_error/src/lib.rs +++ b/kernel/crates/system_error/src/lib.rs @@ -277,31 +277,51 @@ pub enum SystemError { // === 以下错误码不应该被用户态程序使用 === ERESTARTSYS = 512, + ERESTARTNOINTR = 513, + /// restart if no handler + ERESTARTNOHAND = 514, + + /// 没有对应的ioctlcmd + ENOIOCTLCMD = 515, + /// restart by calling sys restart syscall + ERESTART_RESTARTBLOCK = 516, + + // === TODO: 这几个KVM的错误码不要放在这里 === + // VMX on 虚拟化开启指令出错 - EVMXONFailed = 513, + EVMXONFailed = 1513, // VMX off 虚拟化关闭指令出错 - EVMXOFFFailed = 514, + EVMXOFFFailed = 1514, // VMX VMWRITE 写入虚拟化VMCS内存出错 - EVMWRITEFailed = 515, - EVMREADFailed = 516, - EVMPRTLDFailed = 517, - EVMLAUNCHFailed = 518, - KVM_HVA_ERR_BAD = 519, - /// 没有对应的ioctlcmd - ENOIOCTLCMD = 520, + EVMWRITEFailed = 1515, + EVMREADFailed = 1516, + EVMPRTLDFailed = 1517, + EVMLAUNCHFailed = 1518, + KVM_HVA_ERR_BAD = 1519, + + MAXERRNO = 4095, } impl SystemError { - /// @brief 把posix错误码转换为系统错误枚举类型。 + /// 判断一个值是否是有效的posix错误码。 + pub fn is_valid_posix_errno(val: T) -> bool + where + T: PartialOrd + From, + { + let max_errno = T::from(-(Self::MAXERRNO as i32)); + val < T::from(0) && val >= max_errno + } + + /// 尝试把posix错误码转换为系统错误枚举类型。 pub fn from_posix_errno(errno: i32) -> Option { // posix 错误码是小于0的 - if errno >= 0 { + if !Self::is_valid_posix_errno(errno) { return None; } return ::from_i32(-errno); } - /// @brief 把系统错误枚举类型转换为负数posix错误码。 + /// 把系统错误枚举类型转换为负数posix错误码。 pub fn to_posix_errno(&self) -> i32 { return -::to_i32(self).unwrap(); } diff --git a/kernel/src/arch/riscv64/ipc/signal.rs b/kernel/src/arch/riscv64/ipc/signal.rs index f902e6965..befb23e29 100644 --- a/kernel/src/arch/riscv64/ipc/signal.rs +++ b/kernel/src/arch/riscv64/ipc/signal.rs @@ -1,8 +1,9 @@ use log::error; use crate::{ - arch::{sched::sched, CurrentIrqArch}, + arch::{interrupt::TrapFrame, sched::sched, CurrentIrqArch}, exception::InterruptArch, + ipc::signal_types::SignalArch, process::ProcessManager, }; @@ -339,3 +340,18 @@ fn sig_continue(sig: Signal) { fn sig_ignore(_sig: Signal) { return; } + +pub struct RiscV64SignalArch; + +impl SignalArch for RiscV64SignalArch { + // TODO: 为RISCV64实现信号处理 + // 注意,rv64现在在中断/系统调用返回用户态时,没有进入 irqentry_exit() 函数, + // 到时候实现信号处理时,需要修改中断/系统调用返回用户态的代码,进入 irqentry_exit() 函数 + unsafe fn do_signal_or_restart(_frame: &mut TrapFrame) { + todo!() + } + + fn sys_rt_sigreturn(_trap_frame: &mut TrapFrame) -> u64 { + todo!() + } +} diff --git a/kernel/src/arch/riscv64/mod.rs b/kernel/src/arch/riscv64/mod.rs index a34b99a7f..fc4ca2491 100644 --- a/kernel/src/arch/riscv64/mod.rs +++ b/kernel/src/arch/riscv64/mod.rs @@ -27,6 +27,8 @@ pub use self::time::RiscV64TimeArch as CurrentTimeArch; pub use self::elf::RiscV64ElfArch as CurrentElfArch; +pub use self::ipc::signal::RiscV64SignalArch as CurrentSignalArch; + pub use crate::arch::smp::RiscV64SMPArch as CurrentSMPArch; pub use crate::arch::sched::RiscV64SchedArch as CurrentSchedArch; diff --git a/kernel/src/arch/x86_64/asm/entry.S b/kernel/src/arch/x86_64/asm/entry.S index 355d9938f..8df6566cf 100644 --- a/kernel/src/arch/x86_64/asm/entry.S +++ b/kernel/src/arch/x86_64/asm/entry.S @@ -64,9 +64,9 @@ ENTRY(ret_from_intr) // 进入信号处理流程 cli - // 将原本要返回的栈帧的栈指针传入do_signal的第一个参数 + // 将原本要返回的栈帧的栈指针传入irqentry_exit的第一个参数 movq %rsp, %rdi - callq do_signal + callq irqentry_exit cli __entry_ret_from_intr_before_gs_check_2: @@ -375,10 +375,10 @@ ENTRY(syscall_64) sti callq *%rdx //调用服务程序 - // 将原本要返回的栈帧的栈指针传入do_signal的第一个参数 + // 将原本要返回的栈帧的栈指针传入 irqentry_exit 的第一个参数 movq %rsp, %rdi - callq do_signal + callq irqentry_exit cli diff --git a/kernel/src/arch/x86_64/interrupt/mod.rs b/kernel/src/arch/x86_64/interrupt/mod.rs index e83566ca7..8198a0633 100644 --- a/kernel/src/arch/x86_64/interrupt/mod.rs +++ b/kernel/src/arch/x86_64/interrupt/mod.rs @@ -125,6 +125,8 @@ pub struct TrapFrame { pub es: ::core::ffi::c_ulong, pub rax: ::core::ffi::c_ulong, pub func: ::core::ffi::c_ulong, + /// - 该字段在异常发生时,保存的是错误码 + /// - 在系统调用时,由系统调用入口函数将其设置为系统调用号 pub errcode: ::core::ffi::c_ulong, pub rip: ::core::ffi::c_ulong, pub cs: ::core::ffi::c_ulong, @@ -182,6 +184,31 @@ impl TrapFrame { pub fn set_pc(&mut self, pc: usize) { self.rip = pc as u64; } + + /// 获取系统调用号 + /// + /// # Safety + /// 该函数只能在系统调用上下文中调用, + /// 在其他上下文中,该函数返回值未定义 + pub unsafe fn syscall_nr(&self) -> Option { + if self.errcode == u64::MAX { + return None; + } + Some(self.errcode as usize) + } + + /// 获取系统调用错误码 + /// + /// # Safety + /// 该函数只能在系统调用上下文中调用, + /// 在其他上下文中,该函数返回值未定义 + /// + /// # Returns + /// 返回一个 `Option`,表示系统调用的错误码。 + pub unsafe fn syscall_error(&self) -> Option { + let val = self.rax as i32; + SystemError::from_posix_errno(val) + } } impl ProbeArgs for TrapFrame { diff --git a/kernel/src/arch/x86_64/ipc/signal.rs b/kernel/src/arch/x86_64/ipc/signal.rs index 5bc2db64a..25e9eed62 100644 --- a/kernel/src/arch/x86_64/ipc/signal.rs +++ b/kernel/src/arch/x86_64/ipc/signal.rs @@ -1,5 +1,6 @@ use core::{ffi::c_void, intrinsics::unlikely, mem::size_of}; +use defer::defer; use log::error; use system_error::SystemError; @@ -8,11 +9,12 @@ use crate::{ fpu::FpState, interrupt::TrapFrame, process::table::{USER_CS, USER_DS}, + syscall::nr::SYS_RESTART_SYSCALL, CurrentIrqArch, MMArch, }, exception::InterruptArch, ipc::{ - signal::set_current_sig_blocked, + signal::{restore_saved_sigmask, set_current_blocked}, signal_types::{SaHandlerType, SigInfo, Sigaction, SigactionType, SignalArch}, }, mm::MemoryManagementArch, @@ -405,99 +407,147 @@ pub struct SigStack { pub fpstate: FpState, } -#[no_mangle] -unsafe extern "C" fn do_signal(frame: &mut TrapFrame) { - X86_64SignalArch::do_signal(frame); - return; -} +unsafe fn do_signal(frame: &mut TrapFrame, got_signal: &mut bool) { + let pcb = ProcessManager::current_pcb(); -pub struct X86_64SignalArch; + let siginfo = pcb.try_siginfo_irqsave(5); -impl SignalArch for X86_64SignalArch { - unsafe fn do_signal(frame: &mut TrapFrame) { - let pcb = ProcessManager::current_pcb(); + if unlikely(siginfo.is_none()) { + return; + } - let siginfo = pcb.try_siginfo_irqsave(5); + let siginfo_read_guard = siginfo.unwrap(); - if unlikely(siginfo.is_none()) { - return; - } + // 检查sigpending是否为0 + if siginfo_read_guard.sig_pending().signal().bits() == 0 || !frame.is_from_user() { + // 若没有正在等待处理的信号,或者将要返回到的是内核态,则返回 + return; + } - let siginfo_read_guard = siginfo.unwrap(); + let mut sig_number: Signal; + let mut info: Option; + let mut sigaction: Option; + let sig_block: SigSet = *siginfo_read_guard.sig_blocked(); + drop(siginfo_read_guard); - // 检查sigpending是否为0 - if siginfo_read_guard.sig_pending().signal().bits() == 0 || !frame.is_from_user() { - // 若没有正在等待处理的信号,或者将要返回到的是内核态,则返回 - return; - } + let sig_guard = pcb.try_sig_struct_irqsave(5); + if unlikely(sig_guard.is_none()) { + return; + } + let siginfo_mut = pcb.try_siginfo_mut(5); + if unlikely(siginfo_mut.is_none()) { + return; + } - let mut sig_number: Signal; - let mut info: Option; - let mut sigaction: Sigaction; - let sig_block: SigSet = *siginfo_read_guard.sig_block(); - drop(siginfo_read_guard); + let sig_guard = sig_guard.unwrap(); + let mut siginfo_mut_guard = siginfo_mut.unwrap(); + loop { + (sig_number, info) = siginfo_mut_guard.dequeue_signal(&sig_block, &pcb); - let sig_guard = pcb.try_sig_struct_irqsave(5); - if unlikely(sig_guard.is_none()) { + // 如果信号非法,则直接返回 + if sig_number == Signal::INVALID { return; } - let siginfo_mut = pcb.try_siginfo_mut(5); - if unlikely(siginfo_mut.is_none()) { - return; + let sa = sig_guard.handlers[sig_number as usize - 1]; + + match sa.action() { + SigactionType::SaHandler(action_type) => match action_type { + SaHandlerType::Error => { + error!("Trying to handle a Sigerror on Process:{:?}", pcb.pid()); + return; + } + SaHandlerType::Default => { + sigaction = Some(sa); + } + SaHandlerType::Ignore => continue, + SaHandlerType::Customized(_) => { + sigaction = Some(sa); + } + }, + SigactionType::SaSigaction(_) => todo!(), } - let sig_guard = sig_guard.unwrap(); - let mut siginfo_mut_guard = siginfo_mut.unwrap(); - loop { - (sig_number, info) = siginfo_mut_guard.dequeue_signal(&sig_block); - // 如果信号非法,则直接返回 - if sig_number == Signal::INVALID { - return; - } + if sigaction.is_some() { + break; + } + } - sigaction = sig_guard.handlers[sig_number as usize - 1]; + let oldset = *siginfo_mut_guard.sig_blocked(); + //避免死锁 + drop(siginfo_mut_guard); + drop(sig_guard); + drop(pcb); + // 做完上面的检查后,开中断 + CurrentIrqArch::interrupt_enable(); - match sigaction.action() { - SigactionType::SaHandler(action_type) => match action_type { - SaHandlerType::Error => { - error!("Trying to handle a Sigerror on Process:{:?}", pcb.pid()); - return; - } - SaHandlerType::Default => { - sigaction = Sigaction::default(); - break; - } - SaHandlerType::Ignore => continue, - SaHandlerType::Customized(_) => { - break; - } - }, - SigactionType::SaSigaction(_) => todo!(), - } - // 如果当前动作是忽略这个信号,就继续循环。 + if sigaction.is_none() { + return; + } + *got_signal = true; + + let mut sigaction = sigaction.unwrap(); + + // 注意!由于handle_signal里面可能会退出进程, + // 因此这里需要检查清楚:上面所有的锁、arc指针都被释放了。否则会产生资源泄露的问题! + let res: Result = + handle_signal(sig_number, &mut sigaction, &info.unwrap(), &oldset, frame); + if res.is_err() { + error!( + "Error occurred when handling signal: {}, pid={:?}, errcode={:?}", + sig_number as i32, + ProcessManager::current_pcb().pid(), + res.as_ref().unwrap_err() + ); + } +} + +fn try_restart_syscall(frame: &mut TrapFrame) { + defer!({ + // 如果没有信号需要传递,我们只需恢复保存的信号掩码 + restore_saved_sigmask(); + }); + + if unsafe { frame.syscall_nr() }.is_none() { + return; + } + + let syscall_err = unsafe { frame.syscall_error() }; + if syscall_err.is_none() { + return; + } + let syscall_err = syscall_err.unwrap(); + + let mut restart = false; + match syscall_err { + SystemError::ERESTARTSYS | SystemError::ERESTARTNOHAND | SystemError::ERESTARTNOINTR => { + frame.rax = frame.errcode; + frame.rip -= 2; + restart = true; } + SystemError::ERESTART_RESTARTBLOCK => { + frame.rax = SYS_RESTART_SYSCALL as u64; + frame.rip -= 2; + restart = true; + } + _ => {} + } + log::debug!("try restart syscall: {:?}", restart); +} + +pub struct X86_64SignalArch; - let oldset = *siginfo_mut_guard.sig_block(); - //避免死锁 - drop(siginfo_mut_guard); - drop(sig_guard); - drop(pcb); - - // 做完上面的检查后,开中断 - CurrentIrqArch::interrupt_enable(); - - // 注意!由于handle_signal里面可能会退出进程, - // 因此这里需要检查清楚:上面所有的锁、arc指针都被释放了。否则会产生资源泄露的问题! - let res: Result = - handle_signal(sig_number, &mut sigaction, &info.unwrap(), &oldset, frame); - if res.is_err() { - error!( - "Error occurred when handling signal: {}, pid={:?}, errcode={:?}", - sig_number as i32, - ProcessManager::current_pcb().pid(), - res.as_ref().unwrap_err() - ); +impl SignalArch for X86_64SignalArch { + /// 处理信号,并尝试重启系统调用 + /// + /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/arch/x86/kernel/signal.c#865 + unsafe fn do_signal_or_restart(frame: &mut TrapFrame) { + let mut got_signal = false; + do_signal(frame, &mut got_signal); + + if got_signal { + return; } + try_restart_syscall(frame); } fn sys_rt_sigreturn(trap_frame: &mut TrapFrame) -> u64 { @@ -511,7 +561,7 @@ impl SignalArch for X86_64SignalArch { return trap_frame.rax; } let mut sigmask: SigSet = unsafe { (*frame).context.oldmask }; - set_current_sig_blocked(&mut sigmask); + set_current_blocked(&mut sigmask); // 从用户栈恢复sigcontext if !unsafe { &mut (*frame).context }.restore_sigcontext(trap_frame) { error!("unable to restore sigcontext"); @@ -533,6 +583,8 @@ impl SignalArch for X86_64SignalArch { /// @param regs 之前的系统调用将要返回的时候,要弹出的栈帧的拷贝 /// /// @return Result<0,SystemError> 若Error, 则返回错误码,否则返回Ok(0) +/// +/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/arch/x86/kernel/signal.c#787 fn handle_signal( sig: Signal, sigaction: &mut Sigaction, @@ -540,8 +592,28 @@ fn handle_signal( oldset: &SigSet, frame: &mut TrapFrame, ) -> Result { - // TODO 这里要补充一段逻辑,好像是为了保证引入线程之后的地址空间不会出问题。详见https://code.dragonos.org.cn/xref/linux-6.1.9/arch/mips/kernel/signal.c#830 - + if unsafe { frame.syscall_nr() }.is_some() { + if let Some(syscall_err) = unsafe { frame.syscall_error() } { + match syscall_err { + SystemError::ERESTARTNOHAND | SystemError::ERESTART_RESTARTBLOCK => { + frame.rax = SystemError::EINTR.to_posix_errno() as i64 as u64; + } + SystemError::ERESTARTSYS => { + if !sigaction.flags().contains(SigFlags::SA_RESTART) { + frame.rax = SystemError::EINTR.to_posix_errno() as i64 as u64; + } else { + frame.rax = frame.errcode; + frame.rip -= 2; + } + } + SystemError::ERESTARTNOINTR => { + frame.rax = frame.errcode; + frame.rip -= 2; + } + _ => {} + } + } + } // 设置栈帧 return setup_frame(sig, sigaction, info, oldset, frame); } diff --git a/kernel/src/arch/x86_64/mm/fault.rs b/kernel/src/arch/x86_64/mm/fault.rs index e38df1f2e..e55216242 100644 --- a/kernel/src/arch/x86_64/mm/fault.rs +++ b/kernel/src/arch/x86_64/mm/fault.rs @@ -267,9 +267,10 @@ impl X86_64MMArch { }); } else { log::error!( - "No mapped vma, error_code: {:#b}, address: {:#x}", + "No mapped vma, error_code: {:#b}, address: {:#x}, flags: {:?}", error_code, address.data(), + flags ); let pid = ProcessManager::current_pid(); let mut info = diff --git a/kernel/src/arch/x86_64/syscall/mod.rs b/kernel/src/arch/x86_64/syscall/mod.rs index 788517b0e..cc4756992 100644 --- a/kernel/src/arch/x86_64/syscall/mod.rs +++ b/kernel/src/arch/x86_64/syscall/mod.rs @@ -65,6 +65,8 @@ macro_rules! syscall_return { #[no_mangle] pub extern "sysv64" fn syscall_handler(frame: &mut TrapFrame) { + // 系统调用进入时,把系统调用号存入errcode字段,以便在syscall_handler退出后,仍能获取到系统调用号 + frame.errcode = frame.rax; let syscall_num = frame.rax as usize; // 防止sys_sched由于超时无法退出导致的死锁 if syscall_num == SYS_SCHED { diff --git a/kernel/src/bpf/helper/consts.rs b/kernel/src/bpf/helper/consts.rs index 69bce7d61..0c3685454 100644 --- a/kernel/src/bpf/helper/consts.rs +++ b/kernel/src/bpf/helper/consts.rs @@ -1,6 +1,7 @@ pub const HELPER_MAP_LOOKUP_ELEM: u32 = 1; pub const HELPER_MAP_UPDATE_ELEM: u32 = 2; pub const HELPER_MAP_DELETE_ELEM: u32 = 3; +pub const HELPER_KTIME_GET_NS: u32 = 5; pub const HELPER_MAP_FOR_EACH_ELEM: u32 = 164; pub const HELPER_MAP_LOOKUP_PERCPU_ELEM: u32 = 195; pub const HELPER_PERF_EVENT_OUTPUT: u32 = 25; diff --git a/kernel/src/bpf/helper/mod.rs b/kernel/src/bpf/helper/mod.rs index 2f951793f..2c876b25f 100644 --- a/kernel/src/bpf/helper/mod.rs +++ b/kernel/src/bpf/helper/mod.rs @@ -6,6 +6,7 @@ use crate::bpf::map::{BpfCallBackFn, BpfMap}; use crate::include::bindings::linux_bpf::BPF_F_CURRENT_CPU; use crate::libs::lazy_init::Lazy; use crate::smp::core::smp_get_processor_id; +use crate::time::Instant; use alloc::{collections::BTreeMap, sync::Arc}; use core::ffi::c_void; use system_error::SystemError; @@ -300,6 +301,10 @@ pub fn map_peek_elem(map: &Arc, value: &mut [u8]) -> Result<()> { value } +pub fn bpf_ktime_get_ns() -> u64 { + (Instant::now().total_micros() * 1000) as u64 +} + pub static BPF_HELPER_FUN_SET: Lazy> = Lazy::new(); /// Initialize the helper functions. @@ -311,6 +316,7 @@ pub fn init_helper_functions() { map.insert(HELPER_MAP_LOOKUP_ELEM, define_func!(raw_map_lookup_elem)); map.insert(HELPER_MAP_UPDATE_ELEM, define_func!(raw_map_update_elem)); map.insert(HELPER_MAP_DELETE_ELEM, define_func!(raw_map_delete_elem)); + map.insert(HELPER_KTIME_GET_NS, define_func!(bpf_ktime_get_ns)); map.insert( HELPER_MAP_FOR_EACH_ELEM, define_func!(raw_map_for_each_elem), diff --git a/kernel/src/bpf/mod.rs b/kernel/src/bpf/mod.rs index 8e84f2055..592ad7313 100644 --- a/kernel/src/bpf/mod.rs +++ b/kernel/src/bpf/mod.rs @@ -33,7 +33,7 @@ pub fn bpf(cmd: bpf_cmd, attr: &bpf_attr) -> Result { // Program related commands bpf_cmd::BPF_PROG_LOAD => prog::bpf_prog_load(attr), // Object creation commands - bpf_cmd::BPF_BTF_LOAD => { + bpf_cmd::BPF_BTF_LOAD | bpf_cmd::BPF_LINK_CREATE | bpf_cmd::BPF_OBJ_GET_INFO_BY_FD => { error!("bpf cmd {:?} not implemented", cmd); return Err(SystemError::ENOSYS); } diff --git a/kernel/src/driver/base/device/mod.rs b/kernel/src/driver/base/device/mod.rs index b1b76a396..bf42935ea 100644 --- a/kernel/src/driver/base/device/mod.rs +++ b/kernel/src/driver/base/device/mod.rs @@ -310,6 +310,7 @@ pub enum DeviceType { PlatformDev, Char, Pci, + Other, } /// @brief: 设备标识符类型 diff --git a/kernel/src/driver/base/init.rs b/kernel/src/driver/base/init.rs index 93a32d817..b38565142 100644 --- a/kernel/src/driver/base/init.rs +++ b/kernel/src/driver/base/init.rs @@ -21,7 +21,6 @@ pub fn driver_init() -> Result<(), SystemError> { platform_bus_init()?; serio_bus_init()?; CpuDeviceManager::init()?; - // 至此,已完成设备驱动模型的初始化 return Ok(()); } diff --git a/kernel/src/driver/tty/tty_device.rs b/kernel/src/driver/tty/tty_device.rs index 0185db9f1..7b78ef4e2 100644 --- a/kernel/src/driver/tty/tty_device.rs +++ b/kernel/src/driver/tty/tty_device.rs @@ -263,7 +263,7 @@ impl IndexNode for TtyDevice { break; } - if pcb.sig_info_irqsave().sig_pending().has_pending() { + if pcb.has_pending_signal_fast() { return Err(SystemError::ERESTARTSYS); } } diff --git a/kernel/src/driver/tty/tty_job_control.rs b/kernel/src/driver/tty/tty_job_control.rs index 90b7b493a..8ca1f0beb 100644 --- a/kernel/src/driver/tty/tty_job_control.rs +++ b/kernel/src/driver/tty/tty_job_control.rs @@ -4,7 +4,7 @@ use system_error::SystemError; use crate::{ arch::ipc::signal::{SigSet, Signal}, mm::VirtAddr, - process::{Pid, ProcessManager}, + process::{Pid, ProcessFlags, ProcessManager}, syscall::{ user_access::{UserBufferReader, UserBufferWriter}, Syscall, @@ -51,9 +51,9 @@ impl TtyJobCtrlManager { if tty_pgid.is_some() && tty_pgid.unwrap() != pgid { if pcb .sig_info_irqsave() - .sig_block() + .sig_blocked() .contains(SigSet::from_bits_truncate(1 << sig as u64)) - || pcb.sig_struct_irqsave().handlers[sig as usize].is_ignore() + || pcb.sig_struct_irqsave().handlers[sig as usize - 1].is_ignore() { // 忽略该信号 if sig == Signal::SIGTTIN { @@ -62,6 +62,10 @@ impl TtyJobCtrlManager { } else { // 暂时使用kill而不是killpg Syscall::kill(pgid, sig as i32)?; + ProcessManager::current_pcb() + .flags() + .insert(ProcessFlags::HAS_PENDING_SIGNAL); + return Err(SystemError::ERESTARTSYS); } } diff --git a/kernel/src/driver/tty/tty_ldisc/ntty.rs b/kernel/src/driver/tty/tty_ldisc/ntty.rs index df684e9d9..535e18d3a 100644 --- a/kernel/src/driver/tty/tty_ldisc/ntty.rs +++ b/kernel/src/driver/tty/tty_ldisc/ntty.rs @@ -21,7 +21,7 @@ use crate::{ }, mm::VirtAddr, net::event_poll::EPollEventType, - process::ProcessManager, + process::{ProcessFlags, ProcessManager}, syscall::{user_access::UserBufferWriter, Syscall}, }; @@ -1680,11 +1680,11 @@ impl TtyLineDiscipline for NTtyLinediscipline { break; } - if ProcessManager::current_pcb() - .sig_info_irqsave() - .sig_pending() - .has_pending() - { + if ProcessManager::current_pcb().has_pending_signal_fast() { + ProcessManager::current_pcb() + .flags() + .insert(ProcessFlags::HAS_PENDING_SIGNAL); + ret = Err(SystemError::ERESTARTSYS); break; } @@ -1763,7 +1763,11 @@ impl TtyLineDiscipline for NTtyLinediscipline { // drop(ldata); let mut offset = 0; loop { - if pcb.sig_info_irqsave().sig_pending().has_pending() { + if pcb.has_pending_signal_fast() { + ProcessManager::current_pcb() + .flags() + .insert(ProcessFlags::HAS_PENDING_SIGNAL); + return Err(SystemError::ERESTARTSYS); } if core.flags().contains(TtyFlag::HUPPED) { diff --git a/kernel/src/exception/entry.rs b/kernel/src/exception/entry.rs new file mode 100644 index 000000000..e54def7db --- /dev/null +++ b/kernel/src/exception/entry.rs @@ -0,0 +1,46 @@ +use crate::{ + arch::{interrupt::TrapFrame, CurrentSignalArch}, + ipc::signal_types::SignalArch, + process::{ProcessFlags, ProcessManager}, +}; + +#[no_mangle] +unsafe extern "C" fn irqentry_exit(frame: &mut TrapFrame) { + if frame.is_from_user() { + irqentry_exit_to_user_mode(frame); + } +} + +/// 退出到用户态之前,在这个函数内做最后的处理 +/// +/// # Safety +/// +/// 由于这个函数内可能会直接退出进程,因此,在进入函数之前, +/// 必须保证所有的栈上的Arc/Box指针等,都已经被释放。否则,可能会导致内存泄漏。 +unsafe fn irqentry_exit_to_user_mode(frame: &mut TrapFrame) { + exit_to_user_mode_prepare(frame); +} + +/// # Safety +/// +/// 由于这个函数内可能会直接退出进程,因此,在进入函数之前, +/// 必须保证所有的栈上的Arc/Box指针等,都已经被释放。否则,可能会导致内存泄漏。 +unsafe fn exit_to_user_mode_prepare(frame: &mut TrapFrame) { + let process_flags_work = *ProcessManager::current_pcb().flags(); + if !process_flags_work.exit_to_user_mode_work().is_empty() { + exit_to_user_mode_loop(frame, process_flags_work); + } +} + +/// # Safety +/// +/// 由于这个函数内可能会直接退出进程,因此,在进入函数之前, +/// 必须保证所有的栈上的Arc/Box指针等,都已经被释放。否则,可能会导致内存泄漏。 +unsafe fn exit_to_user_mode_loop(frame: &mut TrapFrame, mut process_flags_work: ProcessFlags) { + while !process_flags_work.exit_to_user_mode_work().is_empty() { + if process_flags_work.contains(ProcessFlags::HAS_PENDING_SIGNAL) { + unsafe { CurrentSignalArch::do_signal_or_restart(frame) }; + } + process_flags_work = *ProcessManager::current_pcb().flags(); + } +} diff --git a/kernel/src/exception/mod.rs b/kernel/src/exception/mod.rs index 8eb14dd6b..a12698b12 100644 --- a/kernel/src/exception/mod.rs +++ b/kernel/src/exception/mod.rs @@ -7,6 +7,7 @@ use crate::arch::CurrentIrqArch; pub mod debug; pub mod dummychip; pub mod ebreak; +pub mod entry; pub mod handle; pub mod init; pub mod ipi; diff --git a/kernel/src/filesystem/eventfd.rs b/kernel/src/filesystem/eventfd.rs index 5431e35ed..0805bd250 100644 --- a/kernel/src/filesystem/eventfd.rs +++ b/kernel/src/filesystem/eventfd.rs @@ -4,7 +4,7 @@ use crate::filesystem::vfs::{FilePrivateData, FileSystem, FileType, IndexNode, M use crate::libs::spinlock::{SpinLock, SpinLockGuard}; use crate::libs::wait_queue::WaitQueue; use crate::net::event_poll::{EPollEventType, EPollItem, EventPoll, KernelIoctlData}; -use crate::process::ProcessManager; +use crate::process::{ProcessFlags, ProcessManager}; use crate::sched::SchedMode; use crate::syscall::Syscall; use alloc::collections::LinkedList; @@ -147,6 +147,10 @@ impl IndexNode for EventFdInode { let r = wq_wait_event_interruptible!(self.wait_queue, self.readable(), {}); if r.is_err() { + ProcessManager::current_pcb() + .flags() + .insert(ProcessFlags::HAS_PENDING_SIGNAL); + return Err(SystemError::ERESTARTSYS); } diff --git a/kernel/src/filesystem/procfs/mod.rs b/kernel/src/filesystem/procfs/mod.rs index 0defc3e0e..3c1310326 100644 --- a/kernel/src/filesystem/procfs/mod.rs +++ b/kernel/src/filesystem/procfs/mod.rs @@ -394,7 +394,31 @@ impl ProcFS { } else { panic!("create ksmg error"); } - + // 这个文件是用来欺骗Aya框架识别内核版本 + /* On Ubuntu LINUX_VERSION_CODE doesn't correspond to info.release, + * but Ubuntu provides /proc/version_signature file, as described at + * https://ubuntu.com/kernel, with an example contents below, which we + * can use to get a proper LINUX_VERSION_CODE. + * + * Ubuntu 5.4.0-12.15-generic 5.4.8 + * + * In the above, 5.4.8 is what kernel is actually expecting, while + * uname() call will return 5.4.0 in info.release. + */ + let binding = inode.create("version_signature", FileType::File, ModeType::S_IRUGO); + if let Ok(version_signature) = binding { + let version_signature = version_signature + .as_any_ref() + .downcast_ref::() + .unwrap(); + version_signature.0.lock().fdata.ftype = ProcFileType::Default; + version_signature.0.lock().data = "DragonOS 6.0.0-generic 6.0.0\n" + .to_string() + .as_bytes() + .to_vec(); + } else { + panic!("create version_signature error"); + } return result; } @@ -466,6 +490,7 @@ impl IndexNode for LockedProcFSInode { let file_size = match inode.fdata.ftype { ProcFileType::ProcStatus => inode.open_status(&mut private_data)?, ProcFileType::ProcMeminfo => inode.open_meminfo(&mut private_data)?, + ProcFileType::Default => inode.data.len() as i64, _ => { todo!() } diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 3762474ea..33640bc73 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -311,14 +311,7 @@ impl File { let len = self .inode - .read_at(offset, len, buf, self.private_data.lock()) - .map_err(|e| { - if e == SystemError::ERESTARTSYS { - SystemError::EINTR - } else { - e - } - })?; + .read_at(offset, len, buf, self.private_data.lock())?; if update_offset { self.offset @@ -343,24 +336,11 @@ impl File { // 如果文件指针已经超过了文件大小,则需要扩展文件大小 if offset > self.inode.metadata()?.size as usize { - self.inode.resize(offset).map_err(|e| { - if e == SystemError::ERESTARTSYS { - SystemError::EINTR - } else { - e - } - })?; + self.inode.resize(offset)?; } let len = self .inode - .write_at(offset, len, buf, self.private_data.lock()) - .map_err(|e| { - if e == SystemError::ERESTARTSYS { - SystemError::EINTR - } else { - e - } - })?; + .write_at(offset, len, buf, self.private_data.lock())?; if update_offset { self.offset diff --git a/kernel/src/ipc/pipe.rs b/kernel/src/ipc/pipe.rs index 288a36628..32d574943 100644 --- a/kernel/src/ipc/pipe.rs +++ b/kernel/src/ipc/pipe.rs @@ -11,7 +11,7 @@ use crate::{ wait_queue::WaitQueue, }, net::event_poll::{EPollEventType, EPollItem, EventPoll}, - process::{ProcessManager, ProcessState}, + process::{ProcessFlags, ProcessManager, ProcessState}, sched::SchedMode, time::PosixTimeSpec, }; @@ -232,6 +232,9 @@ impl IndexNode for LockedPipeInode { drop(inode); let r = wq_wait_event_interruptible!(self.read_wait_queue, self.readable(), {}); if r.is_err() { + ProcessManager::current_pcb() + .flags() + .insert(ProcessFlags::HAS_PENDING_SIGNAL); return Err(SystemError::ERESTARTSYS); } diff --git a/kernel/src/ipc/signal.rs b/kernel/src/ipc/signal.rs index fe91bd68d..8c85182d0 100644 --- a/kernel/src/ipc/signal.rs +++ b/kernel/src/ipc/signal.rs @@ -8,7 +8,9 @@ use crate::{ arch::ipc::signal::{SigCode, SigFlags, SigSet, Signal}, ipc::signal_types::SigactionType, libs::spinlock::SpinLockGuard, - process::{pid::PidType, Pid, ProcessControlBlock, ProcessFlags, ProcessManager}, + process::{ + pid::PidType, Pid, ProcessControlBlock, ProcessFlags, ProcessManager, ProcessSignalInfo, + }, }; use super::signal_types::{ @@ -25,7 +27,7 @@ impl Signal { return false; } - if !pcb.has_pending_signal() { + if !pcb.has_pending_signal_fast() { return false; } @@ -112,7 +114,7 @@ impl Signal { } if !self.prepare_sianal(pcb.clone(), force_send) { - return Err(SystemError::EINVAL); + return Ok(0); } // debug!("force send={}", force_send); let pcb_info = pcb.sig_info_irqsave(); @@ -213,13 +215,18 @@ impl Signal { } } - /// @brief 本函数用于检测指定的进程是否想要接收SIG这个信号。 + /// 本函数用于检测指定的进程是否想要接收SIG这个信号。 + /// /// 当我们对于进程组中的所有进程都运行了这个检查之后,我们将可以找到组内愿意接收信号的进程。 /// 这么做是为了防止我们把信号发送给了一个正在或已经退出的进程,或者是不响应该信号的进程。 #[inline] fn wants_signal(&self, pcb: Arc) -> bool { // 如果改进程屏蔽了这个signal,则不能接收 - if pcb.sig_info_irqsave().sig_block().contains((*self).into()) { + if pcb + .sig_info_irqsave() + .sig_blocked() + .contains((*self).into()) + { return false; } @@ -291,7 +298,7 @@ impl Signal { // 一个被阻塞了的信号肯定是要被处理的 if pcb .sig_info_irqsave() - .sig_block() + .sig_blocked() .contains(self.into_sigset()) { return true; @@ -316,6 +323,7 @@ fn signal_wake_up(pcb: Arc, _guard: SpinLockGuard, _guard: SpinLockGuard bool { + sigset.bits() & (!blocked.bits()) != 0 +} + +impl ProcessControlBlock { + /// 重新计算线程的flag中的TIF_SIGPENDING位 + /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/signal.c?r=&mo=4806&fi=182#182 + pub fn recalc_sigpending(&self, siginfo_guard: Option<&ProcessSignalInfo>) { + if !self.recalc_sigpending_tsk(siginfo_guard) { + self.flags().remove(ProcessFlags::HAS_PENDING_SIGNAL); + } + } + + fn recalc_sigpending_tsk(&self, siginfo_guard: Option<&ProcessSignalInfo>) -> bool { + let mut _siginfo_tmp_guard = None; + let siginfo = if let Some(siginfo_guard) = siginfo_guard { + siginfo_guard + } else { + _siginfo_tmp_guard = Some(self.sig_info_irqsave()); + _siginfo_tmp_guard.as_ref().unwrap() + }; + return siginfo.do_recalc_sigpending_tsk(self); + } } -/// @brief 刷新指定进程的sighand的sigaction,将满足条件的sigaction恢复为Default -/// 除非某个信号被设置为ignore且force_default为false,否则都不会将其恢复 +impl ProcessSignalInfo { + fn do_recalc_sigpending_tsk(&self, pcb: &ProcessControlBlock) -> bool { + if has_pending_signals(&self.sig_pending().signal(), self.sig_blocked()) + || has_pending_signals(&self.sig_shared_pending().signal(), self.sig_blocked()) + { + pcb.flags().insert(ProcessFlags::HAS_PENDING_SIGNAL); + return true; + } + /* + * We must never clear the flag in another thread, or in current + * when it's possible the current syscall is returning -ERESTART*. + * So we don't clear it here, and only callers who know they should do. + */ + return false; + } +} +/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/sched/signal.h?fi=restore_saved_sigmask#547 +pub fn restore_saved_sigmask() { + if ProcessManager::current_pcb() + .flags() + .test_and_clear(ProcessFlags::RESTORE_SIG_MASK) + { + let saved = *ProcessManager::current_pcb() + .sig_info_irqsave() + .saved_sigmask(); + __set_current_blocked(&saved); + } +} + +/// 刷新指定进程的sighand的sigaction,将满足条件的sigaction恢复为默认状态。 +/// 除非某个信号被设置为忽略且 `force_default` 为 `false`,否则都不会将其恢复。 +/// +/// # 参数 /// -/// @param pcb 要被刷新的pcb -/// @param force_default 是否强制将sigaction恢复成默认状态 +/// - `pcb`: 要被刷新的pcb。 +/// - `force_default`: 是否强制将sigaction恢复成默认状态。 pub fn flush_signal_handlers(pcb: Arc, force_default: bool) { compiler_fence(core::sync::atomic::Ordering::SeqCst); // debug!("hand=0x{:018x}", hand as *const sighand_struct as usize); @@ -441,31 +500,144 @@ pub(super) fn do_sigaction( return Ok(()); } -/// 设置当前进程的屏蔽信号 (sig_block),待引入 [sigprocmask](https://man7.org/linux/man-pages/man2/sigprocmask.2.html) 系统调用后要删除这个散装函数 -/// -/// ## 参数 -/// -/// - `new_set` 新的屏蔽信号bitmap的值 -pub fn set_current_sig_blocked(new_set: &mut SigSet) { - let to_remove: SigSet = - >::into(Signal::SIGKILL) | Signal::SIGSTOP.into(); - new_set.remove(to_remove); - //TODO 把这个散装函数用 sigsetops 替换掉 - let pcb = ProcessManager::current_pcb(); +/// https://code.dragonos.org.cn/xref/linux-6.6.21/include/uapi/asm-generic/signal-defs.h#72 +/// 对应SIG_BLOCK,SIG_UNBLOCK,SIG_SETMASK +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SigHow { + Block = 0, + Unblock = 1, + SetMask = 2, +} + +impl TryFrom for SigHow { + type Error = SystemError; + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(SigHow::Block), + 1 => Ok(SigHow::Unblock), + 2 => Ok(SigHow::SetMask), + _ => Err(SystemError::EINVAL), + } + } +} + +fn __set_task_blocked(pcb: &Arc, new_set: &SigSet) { + //todo 还有一个对线程组是否为空的判断,进程组、线程组实现之后,需要更改这里。 + if pcb.has_pending_signal() { + let mut newblocked = *new_set; + let guard = pcb.sig_info_irqsave(); + newblocked.remove(*guard.sig_blocked()); + drop(guard); + + // 从主线程开始去遍历 + if let Some(group_leader) = pcb.threads_read_irqsave().group_leader() { + retarget_shared_pending(group_leader, newblocked); + } + } + *pcb.sig_info_mut().sig_block_mut() = *new_set; + pcb.recalc_sigpending(None); +} +fn __set_current_blocked(new_set: &SigSet) { + let pcb = ProcessManager::current_pcb(); /* 如果当前pcb的sig_blocked和新的相等,那么就不用改变它。 请注意,一个进程的sig_blocked字段不能被其他进程修改! */ - if pcb.sig_info_irqsave().sig_block().eq(new_set) { + if pcb.sig_info_irqsave().sig_blocked().eq(new_set) { return; } + let guard: SpinLockGuard<'_, SignalStruct> = pcb.sig_struct_irqsave(); - let guard = pcb.sig_struct_irqsave(); - // todo: 当一个进程有多个线程后,在这里需要设置每个线程的block字段,并且 retarget_shared_pending(虽然我还没搞明白linux这部分是干啥的) + __set_task_blocked(&pcb, new_set); - // 设置当前进程的sig blocked - *pcb.sig_info_mut().sig_block_mut() = *new_set; - recalc_sigpending(); drop(guard); } + +fn retarget_shared_pending(pcb: Arc, which: SigSet) { + let retarget = pcb.sig_info_irqsave().sig_shared_pending().signal(); + retarget.intersects(which); + if retarget.is_empty() { + return; + } + + // 对于线程组中的每一个线程都要执行的函数 + let thread_handling_function = |pcb: Arc, retarget: &SigSet| { + if retarget.is_empty() { + return; + } + + if pcb.flags().contains(ProcessFlags::EXITING) { + return; + } + + let blocked = pcb.sig_info_irqsave().sig_shared_pending().signal(); + if retarget.difference(blocked).is_empty() { + return; + } + + retarget.intersects(blocked); + if !pcb.has_pending_signal() { + let guard = pcb.sig_struct_irqsave(); + signal_wake_up(pcb.clone(), guard, false); + } + // 之前的对retarget的判断移动到最前面,因为对于当前线程的线程的处理已经结束,对于后面的线程在一开始判断retarget为空即可结束处理 + + // debug!("handle done"); + }; + + // 暴力遍历每一个线程,找到相同的tgid + let tgid = pcb.tgid(); + for &pid in pcb.children_read_irqsave().iter() { + if let Some(child) = ProcessManager::find(pid) { + if child.tgid() == tgid { + thread_handling_function(child, &retarget); + } + } + } + // debug!("retarget_shared_pending done!"); +} + +/// 设置当前进程的屏蔽信号 (sig_block) +/// +/// ## 参数 +/// +/// - `new_set` 新的屏蔽信号bitmap的值 +pub fn set_current_blocked(new_set: &mut SigSet) { + let to_remove: SigSet = + >::into(Signal::SIGKILL) | Signal::SIGSTOP.into(); + new_set.remove(to_remove); + __set_current_blocked(new_set); +} + +/// 设置当前进程的屏蔽信号 (sig_block) +/// +/// ## 参数 +/// +/// - `how` 设置方式 +/// - `new_set` 新的屏蔽信号bitmap的值 +pub fn set_sigprocmask(how: SigHow, set: SigSet) -> Result { + let pcb: Arc = ProcessManager::current_pcb(); + let guard = pcb.sig_info_irqsave(); + let oset = *guard.sig_blocked(); + + let mut res_set = oset; + drop(guard); + + match how { + SigHow::Block => { + // debug!("SIG_BLOCK\tGoing to insert is: {}", set.bits()); + res_set.insert(set); + } + SigHow::Unblock => { + res_set.remove(set); + } + SigHow::SetMask => { + // debug!("SIG_SETMASK\tGoing to set is: {}", set.bits()); + res_set = set; + } + } + + __set_current_blocked(&res_set); + Ok(oset) +} diff --git a/kernel/src/ipc/signal_types.rs b/kernel/src/ipc/signal_types.rs index d8d7b1677..befeda025 100644 --- a/kernel/src/ipc/signal_types.rs +++ b/kernel/src/ipc/signal_types.rs @@ -75,9 +75,15 @@ pub struct InnerSignalStruct { impl SignalStruct { #[inline(never)] pub fn new() -> Self { - Self { + let mut r = Self { inner: Box::::default(), - } + }; + let sig_ign = Sigaction::default(); + r.inner.handlers[Signal::SIGCHLD as usize - 1] = sig_ign; + r.inner.handlers[Signal::SIGURG as usize - 1] = sig_ign; + r.inner.handlers[Signal::SIGWINCH as usize - 1] = sig_ign; + + r } } @@ -447,8 +453,6 @@ impl SigPending { None }; - // 当一个进程具有多个线程之后,在这里需要重新计算线程的flag中的TIF_SIGPENDING位 - // recalc_sigpending(); return (sig, info); } /// @brief 从sigpending中删除mask中被置位的信号。也就是说,比如mask的第1位被置为1,那么就从sigqueue中删除所有signum为2的信号的信息。 @@ -539,10 +543,12 @@ impl SigQueue { pub trait SignalArch { /// 信号处理函数 /// + /// 处理信号或重启系统调用 + /// /// ## 参数 /// /// - `frame` 中断栈帧 - unsafe fn do_signal(frame: &mut TrapFrame); + unsafe fn do_signal_or_restart(frame: &mut TrapFrame); fn sys_rt_sigreturn(trap_frame: &mut TrapFrame) -> u64; } diff --git a/kernel/src/ipc/syscall.rs b/kernel/src/ipc/syscall.rs index 2d0b9b6b2..15c2cf5ba 100644 --- a/kernel/src/ipc/syscall.rs +++ b/kernel/src/ipc/syscall.rs @@ -35,6 +35,7 @@ use crate::{ use super::{ pipe::{LockedPipeInode, PipeFsPrivateData}, shm::{ShmCtlCmd, ShmFlags, ShmId, ShmKey}, + signal::{set_sigprocmask, SigHow}, signal_types::{ SaHandlerType, SigInfo, SigType, Sigaction, SigactionType, UserSigaction, USER_SIG_DFL, USER_SIG_ERR, USER_SIG_IGN, @@ -504,4 +505,72 @@ impl Syscall { ShmCtlCmd::Default => Err(SystemError::EINVAL), } } + + /// # SYS_SIGPROCMASK系统调用函数,用于设置或查询当前进程的信号屏蔽字 + /// + /// ## 参数 + /// + /// - `how`: 指示如何修改信号屏蔽字 + /// - `nset`: 新的信号屏蔽字 + /// - `oset`: 旧的信号屏蔽字的指针,由于可以是NULL,所以用Option包装 + /// - `sigsetsize`: 信号集的大小 + /// + /// ## 返回值 + /// + /// 成功:0 + /// 失败:错误码 + /// + /// ## 说明 + /// 根据 https://man7.org/linux/man-pages/man2/sigprocmask.2.html ,传进来的oldset和newset都是指针类型,这里选择传入usize然后转换为u64的指针类型 + pub fn rt_sigprocmask( + how: i32, + newset: usize, + oldset: usize, + sigsetsize: usize, + ) -> Result { + // 对应oset传进来一个NULL的情况 + let oset = if oldset == 0 { None } else { Some(oldset) }; + let nset = if newset == 0 { None } else { Some(newset) }; + + if sigsetsize != size_of::() { + return Err(SystemError::EFAULT); + } + + let sighow = SigHow::try_from(how)?; + + let mut new_set = SigSet::default(); + if let Some(nset) = nset { + let reader = UserBufferReader::new( + VirtAddr::new(nset).as_ptr::(), + core::mem::size_of::(), + true, + )?; + + let nset = reader.read_one_from_user::(0)?; + new_set = SigSet::from_bits_truncate(*nset); + // debug!("Get Newset: {}", &new_set.bits()); + let to_remove: SigSet = + >::into(Signal::SIGKILL) | Signal::SIGSTOP.into(); + new_set.remove(to_remove); + } + + let oldset_to_return = set_sigprocmask(sighow, new_set)?; + if let Some(oldset) = oset { + // debug!("Get Oldset to return: {}", &oldset_to_return.bits()); + let mut writer = UserBufferWriter::new( + VirtAddr::new(oldset).as_ptr::(), + core::mem::size_of::(), + true, + )?; + writer.copy_one_to_user::(&oldset_to_return.bits(), 0)?; + } + + Ok(0) + } + + pub fn restart_syscall() -> Result { + // todo: https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/signal.c#2998 + unimplemented!("restart_syscall with restart block"); + // Err(SystemError::ENOSYS) + } } diff --git a/kernel/src/misc/events/kprobe/device.rs b/kernel/src/misc/events/kprobe/device.rs new file mode 100644 index 000000000..dcb6d66c4 --- /dev/null +++ b/kernel/src/misc/events/kprobe/device.rs @@ -0,0 +1,189 @@ +use crate::driver::base::class::Class; +use crate::driver::base::device::bus::Bus; +use crate::driver::base::device::driver::Driver; +use crate::driver::base::device::{Device, DeviceCommonData, DeviceType, IdTable}; +use crate::driver::base::kobject::{ + KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState, +}; +use crate::driver::base::kset::KSet; +use crate::filesystem::kernfs::KernFSInode; +use crate::filesystem::sysfs::{Attribute, SysFSOpsSupport}; +use crate::filesystem::vfs::syscall::ModeType; +use crate::libs::rwlock::{RwLockReadGuard, RwLockWriteGuard}; +use crate::libs::spinlock::{SpinLock, SpinLockGuard}; +use alloc::string::{String, ToString}; +use alloc::sync::{Arc, Weak}; +use core::fmt::Debug; +use system_error::SystemError; + +#[derive(Debug)] +#[cast_to([sync] Device)] +pub struct KprobeDevice { + inner: SpinLock, + kobj_state: LockedKObjectState, + name: String, +} + +#[derive(Debug)] +struct InnerKprobeDevice { + kobject_common: KObjectCommonData, + device_common: DeviceCommonData, +} + +impl KprobeDevice { + pub fn new(parent: Option>) -> Arc { + let bus_device = Self { + inner: SpinLock::new(InnerKprobeDevice { + kobject_common: KObjectCommonData::default(), + device_common: DeviceCommonData::default(), + }), + kobj_state: LockedKObjectState::new(None), + name: "kprobe".to_string(), + }; + bus_device.set_parent(parent); + return Arc::new(bus_device); + } + + fn inner(&self) -> SpinLockGuard { + self.inner.lock() + } +} + +impl KObject for KprobeDevice { + fn as_any_ref(&self) -> &dyn core::any::Any { + self + } + + fn set_inode(&self, inode: Option>) { + self.inner().kobject_common.kern_inode = inode; + } + + fn inode(&self) -> Option> { + self.inner().kobject_common.kern_inode.clone() + } + + fn parent(&self) -> Option> { + self.inner().kobject_common.parent.clone() + } + + fn set_parent(&self, parent: Option>) { + self.inner().kobject_common.parent = parent; + } + + fn kset(&self) -> Option> { + self.inner().kobject_common.kset.clone() + } + + fn set_kset(&self, kset: Option>) { + self.inner().kobject_common.kset = kset; + } + + fn kobj_type(&self) -> Option<&'static dyn KObjType> { + self.inner().kobject_common.kobj_type + } + + fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { + self.inner().kobject_common.kobj_type = ktype; + } + + fn name(&self) -> String { + self.name.clone() + } + + fn set_name(&self, _name: String) {} + + fn kobj_state(&self) -> RwLockReadGuard { + self.kobj_state.read() + } + + fn kobj_state_mut(&self) -> RwLockWriteGuard { + self.kobj_state.write() + } + + fn set_kobj_state(&self, state: KObjectState) { + *self.kobj_state.write() = state; + } +} + +impl Device for KprobeDevice { + #[inline] + #[allow(dead_code)] + fn dev_type(&self) -> DeviceType { + return DeviceType::Other; + } + + #[inline] + fn id_table(&self) -> IdTable { + IdTable::new("kprobe".to_string(), None) + } + + fn bus(&self) -> Option> { + self.inner().device_common.bus.clone() + } + + fn set_bus(&self, bus: Option>) { + self.inner().device_common.bus = bus; + } + + fn set_class(&self, class: Option>) { + self.inner().device_common.class = class; + } + + fn driver(&self) -> Option> { + self.inner().device_common.driver.clone()?.upgrade() + } + + fn set_driver(&self, driver: Option>) { + self.inner().device_common.driver = driver; + } + + #[inline] + fn is_dead(&self) -> bool { + false + } + + fn can_match(&self) -> bool { + todo!() + } + + fn set_can_match(&self, _can_match: bool) { + todo!() + } + + fn state_synced(&self) -> bool { + todo!() + } + + fn dev_parent(&self) -> Option> { + self.inner().device_common.get_parent_weak_or_clear() + } + + fn set_dev_parent(&self, dev_parent: Option>) { + self.inner().device_common.parent = dev_parent; + } +} + +#[derive(Debug)] +pub struct KprobeAttr; + +impl Attribute for KprobeAttr { + fn name(&self) -> &str { + "type" + } + + fn mode(&self) -> ModeType { + ModeType::S_IRUGO + } + + fn support(&self) -> SysFSOpsSupport { + SysFSOpsSupport::ATTR_SHOW + } + fn show(&self, _kobj: Arc, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Err(SystemError::EINVAL); + } + // perf_type_id::PERF_TYPE_MAX + buf[0] = b'6'; + Ok(1) + } +} diff --git a/kernel/src/misc/events/kprobe/mod.rs b/kernel/src/misc/events/kprobe/mod.rs new file mode 100644 index 000000000..90bf874cf --- /dev/null +++ b/kernel/src/misc/events/kprobe/mod.rs @@ -0,0 +1,31 @@ +use crate::driver::base::device::bus::Bus; +use crate::driver::base::device::{device_manager, device_register, sys_devices_kset, Device}; +use crate::driver::base::kobject::KObject; +use crate::init::initcall::INITCALL_DEVICE; +use crate::misc::events::get_event_source_bus; +use crate::misc::events::kprobe::device::{KprobeAttr, KprobeDevice}; +use alloc::sync::Arc; +use system_error::SystemError; +use unified_init::macros::unified_init; + +pub mod device; +static mut KPROBE_DEVICE: Option> = None; + +#[unified_init(INITCALL_DEVICE)] +pub fn kprobe_subsys_init() -> Result<(), SystemError> { + let kprobe_device = KprobeDevice::new(Some(Arc::downgrade( + &(sys_devices_kset() as Arc), + ))); + + let event_source_bus = get_event_source_bus().ok_or(SystemError::EINVAL)?; + kprobe_device.set_bus(Some(Arc::downgrade(&(event_source_bus as Arc)))); + + // 注册到/sys/devices下 + device_register(kprobe_device.clone())?; + unsafe { + KPROBE_DEVICE = Some(kprobe_device.clone()); + } + + device_manager().create_file(&(kprobe_device as Arc), &KprobeAttr)?; + Ok(()) +} diff --git a/kernel/src/misc/events/mod.rs b/kernel/src/misc/events/mod.rs new file mode 100644 index 000000000..8590e32ed --- /dev/null +++ b/kernel/src/misc/events/mod.rs @@ -0,0 +1,28 @@ +use crate::driver::base::device::bus::{bus_register, Bus}; +use crate::init::initcall::INITCALL_SUBSYS; +use crate::misc::events::subsys::EventSourceBus; +use alloc::sync::Arc; +use system_error::SystemError; +use unified_init::macros::unified_init; + +mod kprobe; +mod subsys; + +static mut EVENT_SOURCE_BUS: Option> = None; + +fn get_event_source_bus() -> Option> { + unsafe { EVENT_SOURCE_BUS.clone() } +} + +#[unified_init(INITCALL_SUBSYS)] +pub fn init_event_source_bus() -> Result<(), SystemError> { + let event_source_bus = EventSourceBus::new(); + let r = bus_register(event_source_bus.clone() as Arc); + if r.is_err() { + unsafe { EVENT_SOURCE_BUS = None }; + return r; + } + unsafe { EVENT_SOURCE_BUS = Some(event_source_bus.clone()) }; + // kprobe::kprobe_subsys_init()?; + Ok(()) +} diff --git a/kernel/src/misc/events/subsys.rs b/kernel/src/misc/events/subsys.rs new file mode 100644 index 000000000..d581f3456 --- /dev/null +++ b/kernel/src/misc/events/subsys.rs @@ -0,0 +1,52 @@ +use crate::driver::base::device::bus::Bus; +use crate::driver::base::device::Device; +use crate::driver::base::subsys::SubSysPrivate; +use alloc::string::{String, ToString}; +use alloc::sync::{Arc, Weak}; +use system_error::SystemError; + +#[derive(Debug)] +pub struct EventSourceBus { + private: SubSysPrivate, +} + +impl EventSourceBus { + pub fn new() -> Arc { + let w: Weak = Weak::new(); + let private = SubSysPrivate::new("event_source".to_string(), Some(w), None, &[]); + let bus = Arc::new(Self { private }); + bus.subsystem() + .set_bus(Some(Arc::downgrade(&(bus.clone() as Arc)))); + return bus; + } +} + +impl Bus for EventSourceBus { + fn name(&self) -> String { + "event_source".to_string() + } + + fn dev_name(&self) -> String { + self.name() + } + + fn root_device(&self) -> Option> { + None + } + + fn remove(&self, _device: &Arc) -> Result<(), SystemError> { + todo!() + } + + fn shutdown(&self, _device: &Arc) { + todo!() + } + + fn resume(&self, _device: &Arc) -> Result<(), SystemError> { + todo!() + } + + fn subsystem(&self) -> &SubSysPrivate { + &self.private + } +} diff --git a/kernel/src/misc/mod.rs b/kernel/src/misc/mod.rs index 9d8a9774a..541e1db73 100644 --- a/kernel/src/misc/mod.rs +++ b/kernel/src/misc/mod.rs @@ -1 +1,2 @@ +pub mod events; pub mod ksysfs; diff --git a/kernel/src/net/event_poll/syscall.rs b/kernel/src/net/event_poll/syscall.rs index 02d61eed3..6fd0dc941 100644 --- a/kernel/src/net/event_poll/syscall.rs +++ b/kernel/src/net/event_poll/syscall.rs @@ -3,7 +3,7 @@ use system_error::SystemError; use crate::{ arch::ipc::signal::SigSet, filesystem::vfs::file::FileMode, - ipc::signal::set_current_sig_blocked, + ipc::signal::set_current_blocked, mm::VirtAddr, syscall::{ user_access::{UserBufferReader, UserBufferWriter}, @@ -96,7 +96,7 @@ impl Syscall { sigmask: &mut SigSet, ) -> Result { // 设置屏蔽的信号 - set_current_sig_blocked(sigmask); + set_current_blocked(sigmask); let wait_ret = Self::epoll_wait(epfd, epoll_event, max_events, timespec); diff --git a/kernel/src/perf/bpf.rs b/kernel/src/perf/bpf.rs index b283b2e54..ee2576898 100644 --- a/kernel/src/perf/bpf.rs +++ b/kernel/src/perf/bpf.rs @@ -79,54 +79,28 @@ impl RingPage { } } + #[inline] fn can_write(&self, data_size: usize, data_tail: usize, data_head: usize) -> bool { - if (data_head + 1) % self.data_region_size == data_tail { - // The buffer is full - return false; - } - let capacity = if data_head >= data_tail { - self.data_region_size - data_head + data_tail - } else { - data_tail - data_head - }; + let capacity = self.data_region_size - data_head + data_tail; data_size <= capacity } pub fn write_event(&mut self, data: &[u8]) -> Result<()> { let data_tail = unsafe { &mut (*(self.ptr as *mut perf_event_mmap_page)).data_tail }; let data_head = unsafe { &mut (*(self.ptr as *mut perf_event_mmap_page)).data_head }; - // data_tail..data_head is the region that can be written - // check if there is enough space to write the event - let sample_size = PerfSample::calculate_size(data.len()); - let can_write_sample = - self.can_write(sample_size, *data_tail as usize, *data_head as usize); - // log::error!( - // "can_write_sample: {}, data_tail: {}, data_head: {}, data.len(): {}, region_size: {}", - // can_write_sample, - // *data_tail, - // *data_head, - // data.len(), - // self.data_region_size - // ); - if !can_write_sample { - //we need record it to the lost record - self.lost += 1; - // log::error!( - // "Lost record: {}, data_tail: {}, data_head: {}", - // self.lost, - // *data_tail, - // *data_head - // ); - Ok(()) - } else { - // we can write the sample to the page - // If the lost record is not zero, we need to write the lost record first. + // user lib will update the tail after read the data,but it will not % data_region_size + let perf_header_size = size_of::(); + let can_write_perf_header = + self.can_write(perf_header_size, *data_tail as usize, *data_head as usize); + + if can_write_perf_header { let can_write_lost_record = self.can_write( size_of::(), *data_tail as usize, *data_head as usize, ); + // if there is lost record, we need to write the lost record first if self.lost > 0 && can_write_lost_record { let new_data_head = self.write_lost(*data_head as usize)?; *data_head = new_data_head as u64; @@ -137,8 +111,21 @@ impl RingPage { // *data_head // ); self.lost = 0; - self.write_event(data) - } else { + // try to write the event again + return self.write_event(data); + } + let sample_size = PerfSample::calculate_size(data.len()); + let can_write_sample = + self.can_write(sample_size, *data_tail as usize, *data_head as usize); + // log::error!( + // "can_write_sample: {}, data_tail: {}, data_head: {}, data.len(): {}, region_size: {}", + // can_write_sample, + // *data_tail, + // *data_head, + // data.len(), + // self.data_region_size + // ); + if can_write_sample { let new_data_head = self.write_sample(data, *data_head as usize)?; *data_head = new_data_head as u64; // log::info!( @@ -146,20 +133,24 @@ impl RingPage { // *data_tail, // *data_head // ); - Ok(()) + } else { + self.lost += 1; } + } else { + self.lost += 1; } + Ok(()) } /// Write any data to the page. /// /// Return the new data_head - fn write_any(&mut self, data: &[u8], data_head: usize) -> Result { + fn write_any(&mut self, data: &[u8], data_head: usize) -> Result<()> { let data_region_len = self.data_region_size; let data_region = self.as_mut_slice()[PAGE_SIZE..].as_mut(); let data_len = data.len(); + let start = data_head % data_region_len; let end = (data_head + data_len) % data_region_len; - let start = data_head; if start < end { data_region[start..end].copy_from_slice(data); } else { @@ -167,40 +158,57 @@ impl RingPage { data_region[start..start + first_len].copy_from_slice(&data[..first_len]); data_region[0..end].copy_from_slice(&data[first_len..]); } - Ok(end) + Ok(()) + } + #[inline] + fn fill_size(&self, data_head_mod: usize) -> usize { + if self.data_region_size - data_head_mod < size_of::() { + // The remaining space is not enough to write the perf_event_header + // We need to fill the remaining space with 0 + self.data_region_size - data_head_mod + } else { + 0 + } } /// Write a sample to the page. fn write_sample(&mut self, data: &[u8], data_head: usize) -> Result { + let sample_size = PerfSample::calculate_size(data.len()); + let maybe_end = (data_head + sample_size) % self.data_region_size; + let fill_size = self.fill_size(maybe_end); let perf_sample = PerfSample { s_hdr: SampleHeader { header: perf_event_header { type_: perf_event_type::PERF_RECORD_SAMPLE as u32, misc: 0, - size: size_of::() as u16 + data.len() as u16, + size: size_of::() as u16 + data.len() as u16 + fill_size as u16, }, size: data.len() as u32, }, value: data, }; - let new_head = self.write_any(perf_sample.s_hdr.as_bytes(), data_head)?; - self.write_any(perf_sample.value, new_head) + self.write_any(perf_sample.s_hdr.as_bytes(), data_head)?; + self.write_any(perf_sample.value, data_head + size_of::())?; + Ok(data_head + sample_size + fill_size) } /// Write a lost record to the page. /// /// Return the new data_head fn write_lost(&mut self, data_head: usize) -> Result { + let maybe_end = (data_head + size_of::()) % self.data_region_size; + let fill_size = self.fill_size(maybe_end); let lost = LostSamples { header: perf_event_header { type_: perf_event_type::PERF_RECORD_LOST as u32, misc: 0, - size: size_of::() as u16, + size: size_of::() as u16 + fill_size as u16, }, id: 0, count: self.lost as u64, }; - self.write_any(lost.as_bytes(), data_head) + self.write_any(lost.as_bytes(), data_head)?; + Ok(data_head + size_of::() + fill_size) } pub fn readable(&self) -> bool { diff --git a/kernel/src/perf/kprobe.rs b/kernel/src/perf/kprobe.rs index 54ac829bc..698da7dc6 100644 --- a/kernel/src/perf/kprobe.rs +++ b/kernel/src/perf/kprobe.rs @@ -39,8 +39,10 @@ impl KprobePerfEvent { .downcast_arc::() .ok_or(SystemError::EINVAL)?; let prog_slice = file.insns(); - let mut vm = - EbpfVmRawOwned::new(Some(prog_slice.to_vec())).map_err(|_| SystemError::EINVAL)?; + let mut vm = EbpfVmRawOwned::new(Some(prog_slice.to_vec())).map_err(|e| { + log::error!("create ebpf vm failed: {:?}", e); + SystemError::EINVAL + })?; vm.register_helper_set(BPF_HELPER_FUN_SET.get()) .map_err(|_| SystemError::EINVAL)?; // create a callback to execute the ebpf prog @@ -75,10 +77,10 @@ impl CallBackFunc for KprobePerfCallBack { size_of::(), ) }; - let _res = self - .vm - .execute_program(probe_context) - .map_err(|_| SystemError::EINVAL); + let res = self.vm.execute_program(probe_context); + if res.is_err() { + log::error!("kprobe callback error: {:?}", res); + } } } diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index db859e261..0a9282e89 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -427,6 +427,7 @@ impl ProcessManager { } drop(thread); unsafe { pcb.basic_mut().set_user_vm(None) }; + pcb.exit_files(); // TODO 由于未实现进程组,tty记录的前台进程组等于当前进程,故退出前要置空 // 后续相关逻辑需要在SYS_EXIT_GROUP系统调用中实现 @@ -616,6 +617,32 @@ bitflags! { const NEED_MIGRATE = 1 << 7; /// 随机化的虚拟地址空间,主要用于动态链接器的加载 const RANDOMIZE = 1 << 8; + /// 进程有未处理的信号(这是一个用于快速判断的标志位) + /// 相当于Linux的TIF_SIGPENDING + const HAS_PENDING_SIGNAL = 1 << 9; + /// 进程需要恢复之前保存的信号掩码 + const RESTORE_SIG_MASK = 1 << 10; + } +} + +impl ProcessFlags { + pub const fn exit_to_user_mode_work(&self) -> Self { + Self::from_bits_truncate(self.bits & (Self::HAS_PENDING_SIGNAL.bits)) + } + + /// 测试并清除标志位 + /// + /// ## 参数 + /// + /// - `rhs` : 需要测试并清除的标志位 + /// + /// ## 返回值 + /// + /// 如果标志位在清除前是置位的,则返回 `true`,否则返回 `false` + pub const fn test_and_clear(&mut self, rhs: Self) -> bool { + let r = (self.bits & rhs.bits) != 0; + self.bits &= !rhs.bits; + r } } #[derive(Debug)] @@ -677,6 +704,7 @@ pub struct ProcessControlBlock { /// 进程作为主体的凭证集 cred: SpinLock, + self_ref: Weak, } impl ProcessControlBlock { @@ -738,7 +766,7 @@ impl ProcessControlBlock { let ppcb: Weak = ProcessManager::find(ppid) .map(|p| Arc::downgrade(&p)) .unwrap_or_default(); - let pcb = Self { + let mut pcb = Self { pid, tgid: pid, thread_pid: Arc::new(RwLock::new(PidStrcut::new())), @@ -763,6 +791,7 @@ impl ProcessControlBlock { robust_list: RwLock::new(None), nsproxy: Arc::new(RwLock::new(NsProxy::new())), cred: SpinLock::new(cred), + self_ref: Weak::new(), }; pcb.sig_info.write().set_tty(tty); @@ -773,7 +802,10 @@ impl ProcessControlBlock { .lock() .init_syscall_stack(&pcb.syscall_stack.read()); - let pcb = Arc::new(pcb); + let pcb = Arc::new_cyclic(|weak| { + pcb.self_ref = weak.clone(); + pcb + }); pcb.sched_info() .sched_entity() @@ -1021,6 +1053,11 @@ impl ProcessControlBlock { return has_pending; } + /// 根据 pcb 的 flags 判断当前进程是否有未处理的信号 + pub fn has_pending_signal_fast(&self) -> bool { + self.flags.get().contains(ProcessFlags::HAS_PENDING_SIGNAL) + } + pub fn sig_struct(&self) -> SpinLockGuard { self.sig_struct.lock_irqsave() } @@ -1060,6 +1097,19 @@ impl ProcessControlBlock { pub fn set_nsproxy(&self, nsprsy: NsProxy) { *self.nsproxy.write() = nsprsy; } + + /// Exit fd table when process exit + fn exit_files(&self) { + self.basic.write_irqsave().set_fd_table(None); + } + + pub fn children_read_irqsave(&self) -> RwLockReadGuard> { + self.children.read_irqsave() + } + + pub fn threads_read_irqsave(&self) -> RwLockReadGuard { + self.thread.read_irqsave() + } } impl Drop for ProcessControlBlock { @@ -1091,6 +1141,12 @@ pub struct ThreadInfo { group_leader: Weak, } +impl Default for ThreadInfo { + fn default() -> Self { + Self::new() + } +} + impl ThreadInfo { pub fn new() -> Self { Self { @@ -1531,8 +1587,9 @@ pub fn process_init() { #[derive(Debug)] pub struct ProcessSignalInfo { - // 当前进程 - sig_block: SigSet, + // 当前进程被屏蔽的信号 + sig_blocked: SigSet, + saved_sigmask: SigSet, // sig_pending 中存储当前线程要处理的信号 sig_pending: SigPending, // sig_shared_pending 中存储当前线程所属进程要处理的信号 @@ -1542,8 +1599,8 @@ pub struct ProcessSignalInfo { } impl ProcessSignalInfo { - pub fn sig_block(&self) -> &SigSet { - &self.sig_block + pub fn sig_blocked(&self) -> &SigSet { + &self.sig_blocked } pub fn sig_pending(&self) -> &SigPending { @@ -1555,7 +1612,15 @@ impl ProcessSignalInfo { } pub fn sig_block_mut(&mut self) -> &mut SigSet { - &mut self.sig_block + &mut self.sig_blocked + } + + pub fn saved_sigmask(&self) -> &SigSet { + &self.saved_sigmask + } + + pub fn saved_sigmask_mut(&mut self) -> &mut SigSet { + &mut self.saved_sigmask } pub fn sig_shared_pending_mut(&mut self) -> &mut SigPending { @@ -1580,12 +1645,19 @@ impl ProcessSignalInfo { /// /// - `sig_mask` 被忽略掉的信号 /// - pub fn dequeue_signal(&mut self, sig_mask: &SigSet) -> (Signal, Option) { + pub fn dequeue_signal( + &mut self, + sig_mask: &SigSet, + pcb: &Arc, + ) -> (Signal, Option) { let res = self.sig_pending.dequeue_signal(sig_mask); + pcb.recalc_sigpending(Some(self)); if res.0 != Signal::INVALID { return res; } else { - return self.sig_shared_pending.dequeue_signal(sig_mask); + let res = self.sig_shared_pending.dequeue_signal(sig_mask); + pcb.recalc_sigpending(Some(self)); + return res; } } } @@ -1593,7 +1665,8 @@ impl ProcessSignalInfo { impl Default for ProcessSignalInfo { fn default() -> Self { Self { - sig_block: SigSet::empty(), + sig_blocked: SigSet::empty(), + saved_sigmask: SigSet::empty(), sig_pending: SigPending::default(), sig_shared_pending: SigPending::default(), tty: None, diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 45dd2efa6..f9f7db29c 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -879,8 +879,11 @@ impl Syscall { } SYS_RT_SIGPROCMASK => { - warn!("SYS_RT_SIGPROCMASK has not yet been implemented"); - Ok(0) + let how = args[0] as i32; + let nset = args[1]; + let oset = args[2]; + let sigsetsize = args[3]; + Self::rt_sigprocmask(how, nset, oset, sigsetsize) } SYS_TKILL => { @@ -1213,6 +1216,8 @@ impl Syscall { let flags = args[4] as u32; Self::sys_perf_event_open(attr, pid, cpu, group_fd, flags) } + SYS_SETRLIMIT => Ok(0), + SYS_RESTART_SYSCALL => Self::restart_syscall(), _ => panic!("Unsupported syscall ID: {}", syscall_num), }; diff --git a/tools/BUILD_CONTAINER_VERSION b/tools/BUILD_CONTAINER_VERSION index 72970ee2c..8a9f2d79a 100644 --- a/tools/BUILD_CONTAINER_VERSION +++ b/tools/BUILD_CONTAINER_VERSION @@ -1 +1 @@ -v1.7 \ No newline at end of file +v1.8 \ No newline at end of file diff --git a/tools/bootstrap.sh b/tools/bootstrap.sh index b859c3ae9..ff6a5d96a 100644 --- a/tools/bootstrap.sh +++ b/tools/bootstrap.sh @@ -24,6 +24,7 @@ DEFAULT_INSTALL="false" export RUSTUP_DIST_SERVER=${RUSTUP_DIST_SERVER:-https://rsproxy.cn} export RUSTUP_UPDATE_ROOT=${RUSTUP_UPDATE_ROOT:-https://rsproxy.cn/rustup} export RUST_VERSION="${RUST_VERSION:-nightly-2024-11-05}" +export RUST_VERSION_OLD="${RUST_VERSION:-nightly-2024-07-23}" banner() { @@ -65,7 +66,7 @@ install_ubuntu_debian_pkg() lsb-release \ llvm-dev libclang-dev clang gcc-multilib \ gcc build-essential fdisk dosfstools dnsmasq bridge-utils iptables libssl-dev pkg-config \ - sphinx make git + python3-sphinx make git # 必须分开安装,否则会出现错误 sudo "$1" install -y \ gcc-riscv64-unknown-elf gcc-riscv64-linux-gnu gdb-multiarch @@ -233,21 +234,21 @@ rustInstall() { echo "正在安装DragonOS所需的rust组件...首次安装需要一些时间来更新索引,请耐心等待..." cargo install cargo-binutils cargo install bpf-linker - rustup toolchain install nightly-2024-11-05-x86_64-unknown-linux-gnu rustup toolchain install $RUST_VERSION-x86_64-unknown-linux-gnu + rustup toolchain install $RUST_VERSION_OLD-x86_64-unknown-linux-gnu rustup component add rust-src --toolchain $RUST_VERSION-x86_64-unknown-linux-gnu - rustup component add rust-src --toolchain nightly-2024-11-05-x86_64-unknown-linux-gnu + rustup component add rust-src --toolchain $RUST_VERSION_OLD-x86_64-unknown-linux-gnu rustup target add x86_64-unknown-none --toolchain $RUST_VERSION-x86_64-unknown-linux-gnu - rustup target add x86_64-unknown-none --toolchain nightly-2024-11-05-x86_64-unknown-linux-gnu - rustup target add x86_64-unknown-linux-musl --toolchain nightly-2024-11-05-x86_64-unknown-linux-gnu + rustup target add x86_64-unknown-none --toolchain $RUST_VERSION_OLD-x86_64-unknown-linux-gnu rustup target add x86_64-unknown-linux-musl --toolchain $RUST_VERSION-x86_64-unknown-linux-gnu + rustup target add x86_64-unknown-linux-musl --toolchain $RUST_VERSION_OLD-x86_64-unknown-linux-gnu rustup toolchain install $RUST_VERSION-riscv64gc-unknown-linux-gnu --force-non-host - rustup toolchain install nightly-2024-11-05-riscv64gc-unknown-linux-gnu --force-non-host + rustup toolchain install $RUST_VERSION_OLD-riscv64gc-unknown-linux-gnu --force-non-host rustup target add riscv64gc-unknown-none-elf --toolchain $RUST_VERSION-riscv64gc-unknown-linux-gnu rustup target add riscv64imac-unknown-none-elf --toolchain $RUST_VERSION-riscv64gc-unknown-linux-gnu - rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2024-11-05-riscv64gc-unknown-linux-gnu - rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2024-11-05-riscv64gc-unknown-linux-gnu + rustup target add riscv64gc-unknown-none-elf --toolchain $RUST_VERSION_OLD-riscv64gc-unknown-linux-gnu + rustup target add riscv64imac-unknown-none-elf --toolchain $RUST_VERSION_OLD-riscv64gc-unknown-linux-gnu rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu rustup component add rust-src @@ -338,9 +339,9 @@ cargo install dadk || exit 1 bashpath=$(cd `dirname $0`; pwd) # 编译安装musl交叉编译工具链 -bash ${bashpath}/install_musl_gcc.sh || (echo "musl交叉编译工具链安装失败" && exit 1) +$SHELL ${bashpath}/install_musl_gcc.sh || (echo "musl交叉编译工具链安装失败" && exit 1) # 编译安装grub -bash ${bashpath}/grub_auto_install.sh || (echo "grub安装失败" && exit 1) +$SHELL ${bashpath}/grub_auto_install.sh || (echo "grub安装失败" && exit 1) # 解决kvm权限问题 USR=$USER diff --git a/tools/change_rust_src.sh b/tools/change_rust_src.sh index 5d3ac7f23..6f5225413 100644 --- a/tools/change_rust_src.sh +++ b/tools/change_rust_src.sh @@ -2,7 +2,8 @@ echo "正在为rust换源" sparse="false" -CONFIG_FILE=~/.cargo/config.toml +CARGO_HOME=${CARGO_HOME:-~/.cargo} +CONFIG_FILE=$CARGO_HOME/config.toml # 创建父目录 if [ ! -d ~/.cargo ]; then mkdir -p ~/.cargo diff --git a/user/apps/test_ebpf/syscall_ebpf/.gitignore b/user/apps/syscall_ebpf/.gitignore similarity index 95% rename from user/apps/test_ebpf/syscall_ebpf/.gitignore rename to user/apps/syscall_ebpf/.gitignore index 9db7029fd..57e36ada9 100644 --- a/user/apps/test_ebpf/syscall_ebpf/.gitignore +++ b/user/apps/syscall_ebpf/.gitignore @@ -7,3 +7,4 @@ target/ # These are backup files generated by rustfmt **/*.rs.bk +/install/ \ No newline at end of file diff --git a/user/apps/syscall_ebpf/Cargo.toml b/user/apps/syscall_ebpf/Cargo.toml new file mode 100644 index 000000000..7437b807b --- /dev/null +++ b/user/apps/syscall_ebpf/Cargo.toml @@ -0,0 +1,31 @@ +[workspace] +resolver = "2" +members = ["syscall_ebpf", "syscall_ebpf-common", "syscall_ebpf-ebpf"] +default-members = ["syscall_ebpf", "syscall_ebpf-common"] + +[workspace.dependencies] +aya = { version = "0.13.0", default-features = false } +aya-ebpf = { version = "0.1.1", default-features = false } +aya-log = { version = "0.2.1", default-features = false } +aya-log-ebpf = { version = "0.1.1", default-features = false } +anyhow = { version = "1", default-features = false } +cargo_metadata = { version = "0.18.0", default-features = false } +# `std` feature is currently required to build `clap`. +# +# See https://github.com/clap-rs/clap/blob/61f5ee5/clap_builder/src/lib.rs#L15. +clap = { version = "4.5.20", default-features = false, features = ["std"] } +env_logger = { version = "0.11.5", default-features = false } +libc = { version = "0.2.159", default-features = false } +log = { version = "0.4.22", default-features = false } +tokio = { version = "1.40.0", default-features = false } +which = { version = "6.0.0", default-features = false } + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" + +[profile.release.package.syscall_ebpf-ebpf] +debug = 2 +codegen-units = 1 diff --git a/user/apps/test_ebpf/Makefile b/user/apps/syscall_ebpf/Makefile similarity index 84% rename from user/apps/test_ebpf/Makefile rename to user/apps/syscall_ebpf/Makefile index 0b5d9e43a..c8dadc36e 100644 --- a/user/apps/test_ebpf/Makefile +++ b/user/apps/syscall_ebpf/Makefile @@ -21,10 +21,10 @@ endif run: RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) -build:build-ebpf +build: RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) -clean:clean-ebpf +clean: RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) test: @@ -42,20 +42,16 @@ fmt-check: run-release: RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release -build-release:build-ebpf +build-release: RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release -clean-release:clean-ebpf +clean-release: RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release test-release: RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release -build-ebpf: - cd ./syscall_ebpf && RUST_LOG=debug cargo xtask build --release -clean-ebpf: - cd ./syscall_ebpf && cargo clean .PHONY: install -install:build-ebpf - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force +install: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path ./syscall_ebpf --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/syscall_ebpf/README.md b/user/apps/syscall_ebpf/README.md new file mode 100644 index 000000000..635b37bbd --- /dev/null +++ b/user/apps/syscall_ebpf/README.md @@ -0,0 +1,33 @@ +# syscall_ebpf + +## Prerequisites + +1. stable rust toolchains: `rustup toolchain install stable` +1. nightly rust toolchains: `rustup toolchain install nightly --component rust-src` +1. (if cross-compiling) rustup target: `rustup target add ${ARCH}-unknown-linux-musl` +1. (if cross-compiling) LLVM: (e.g.) `brew install llvm` (on macOS) +1. (if cross-compiling) C toolchain: (e.g.) [`brew install filosottile/musl-cross/musl-cross`](https://github.com/FiloSottile/homebrew-musl-cross) (on macOS) +1. bpf-linker: `cargo install bpf-linker` (`--no-default-features` on macOS) + +## Build & Run + +Use `cargo build`, `cargo check`, etc. as normal. Run your program with: + +```shell +cargo run --release --config 'target."cfg(all())".runner="sudo -E"' +``` + +Cargo build scripts are used to automatically build the eBPF correctly and include it in the +program. + +## Cross-compiling on macOS + +Cross compilation should work on both Intel and Apple Silicon Macs. + +```shell +CC=${ARCH}-linux-musl-gcc cargo build --package syscall_ebpf --release \ + --target=${ARCH}-unknown-linux-musl \ + --config=target.${ARCH}-unknown-linux-musl.linker=\"${ARCH}-linux-musl-gcc\" +``` +The cross-compiled program `target/${ARCH}-unknown-linux-musl/release/syscall_ebpf` can be +copied to a Linux server or VM and run there. diff --git a/user/apps/syscall_ebpf/rustfmt.toml b/user/apps/syscall_ebpf/rustfmt.toml new file mode 100644 index 000000000..53f7b6d7a --- /dev/null +++ b/user/apps/syscall_ebpf/rustfmt.toml @@ -0,0 +1,4 @@ +group_imports = "StdExternalCrate" +imports_granularity = "Crate" +reorder_imports = true +unstable_features = true diff --git a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-common/Cargo.toml b/user/apps/syscall_ebpf/syscall_ebpf-common/Cargo.toml similarity index 56% rename from user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-common/Cargo.toml rename to user/apps/syscall_ebpf/syscall_ebpf-common/Cargo.toml index 7acc25d40..b453f61b7 100644 --- a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-common/Cargo.toml +++ b/user/apps/syscall_ebpf/syscall_ebpf-common/Cargo.toml @@ -8,7 +8,7 @@ default = [] user = ["aya"] [dependencies] -aya = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/tiny-aya.git", rev = "0689f13", optional = true } +aya = { workspace = true, optional = true } [lib] path = "src/lib.rs" diff --git a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-common/src/lib.rs b/user/apps/syscall_ebpf/syscall_ebpf-common/src/lib.rs similarity index 100% rename from user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-common/src/lib.rs rename to user/apps/syscall_ebpf/syscall_ebpf-common/src/lib.rs diff --git a/user/apps/syscall_ebpf/syscall_ebpf-ebpf/.cargo/config.toml b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/.cargo/config.toml new file mode 100644 index 000000000..d8d7a20cd --- /dev/null +++ b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/.cargo/config.toml @@ -0,0 +1,12 @@ +# We have this so that one doesn't need to manually pass +# --target=bpfel-unknown-none -Z build-std=core when running cargo +# check/build/doc etc. +# +# NB: this file gets loaded only if you run cargo from this directory, it's +# ignored if you run from the workspace root. See +# https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure +[build] +target = ["bpfeb-unknown-none", "bpfel-unknown-none"] + +[unstable] +build-std = ["core"] diff --git a/user/apps/syscall_ebpf/syscall_ebpf-ebpf/Cargo.toml b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/Cargo.toml new file mode 100644 index 000000000..fd061ea9b --- /dev/null +++ b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "syscall_ebpf-ebpf" +version = "0.1.0" +edition = "2021" + +[dependencies] +syscall_ebpf-common = { path = "../syscall_ebpf-common" } + +aya-ebpf = { workspace = true } +aya-log-ebpf = { workspace = true } + +[build-dependencies] +which = { workspace = true } + +[[bin]] +name = "syscall_ebpf" +path = "src/main.rs" diff --git a/user/apps/syscall_ebpf/syscall_ebpf-ebpf/build.rs b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/build.rs new file mode 100644 index 000000000..f83c317a1 --- /dev/null +++ b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/build.rs @@ -0,0 +1,17 @@ +use which::which; + +/// Building this crate has an undeclared dependency on the `bpf-linker` binary. This would be +/// better expressed by [artifact-dependencies][bindeps] but issues such as +/// https://github.com/rust-lang/cargo/issues/12385 make their use impractical for the time being. +/// +/// This file implements an imperfect solution: it causes cargo to rebuild the crate whenever the +/// mtime of `which bpf-linker` changes. Note that possibility that a new bpf-linker is added to +/// $PATH ahead of the one used as the cache key still exists. Solving this in the general case +/// would require rebuild-if-changed-env=PATH *and* rebuild-if-changed={every-directory-in-PATH} +/// which would likely mean far too much cache invalidation. +/// +/// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies +fn main() { + let bpf_linker = which("bpf-linker").unwrap(); + println!("cargo:rerun-if-changed={}", bpf_linker.to_str().unwrap()); +} diff --git a/user/apps/syscall_ebpf/syscall_ebpf-ebpf/rust-toolchain.toml b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/rust-toolchain.toml new file mode 100644 index 000000000..f70d22540 --- /dev/null +++ b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly" +components = ["rust-src"] diff --git a/user/apps/syscall_ebpf/syscall_ebpf-ebpf/src/lib.rs b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/src/lib.rs new file mode 100644 index 000000000..3ac3e595b --- /dev/null +++ b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/src/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +// This file exists to enable the library target. diff --git a/user/apps/syscall_ebpf/syscall_ebpf-ebpf/src/main.rs b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/src/main.rs new file mode 100644 index 000000000..c75810172 --- /dev/null +++ b/user/apps/syscall_ebpf/syscall_ebpf-ebpf/src/main.rs @@ -0,0 +1,50 @@ +#![no_std] +#![no_main] + +use aya_ebpf::{ + helpers::bpf_ktime_get_ns, + macros::{kprobe, map}, + maps::HashMap, + programs::ProbeContext, +}; +use aya_log_ebpf::info; + +#[kprobe] +pub fn syscall_ebpf(ctx: ProbeContext) -> u32 { + try_syscall_ebpf(ctx).unwrap_or_else(|ret| ret) +} + +fn try_syscall_ebpf(ctx: ProbeContext) -> Result { + let pt_regs = unsafe { &*ctx.regs }; + // first arg -> rdi + // second arg -> rsi + // third arg -> rdx + // four arg -> rcx + let syscall_num = pt_regs.rsi as usize; + if syscall_num != 1 { + unsafe { + if let Some(v) = SYSCALL_LIST.get(&(syscall_num as u32)) { + let new_v = *v + 1; + SYSCALL_LIST + .insert(&(syscall_num as u32), &new_v, 0) + .unwrap(); + } else { + SYSCALL_LIST.insert(&(syscall_num as u32), &1, 0).unwrap(); + } + } + let time = unsafe { bpf_ktime_get_ns() }; + info!(&ctx, "[{}] invoke syscall {}", time, syscall_num); + } + Ok(0) +} + +#[map] +static SYSCALL_LIST: HashMap = HashMap::::with_max_entries(1024, 0); + +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + // we need use this because the verifier will forbid loop + unsafe { core::hint::unreachable_unchecked() } + // loop{} +} diff --git a/user/apps/syscall_ebpf/syscall_ebpf/Cargo.toml b/user/apps/syscall_ebpf/syscall_ebpf/Cargo.toml new file mode 100644 index 000000000..6191f0495 --- /dev/null +++ b/user/apps/syscall_ebpf/syscall_ebpf/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "syscall_ebpf" +version = "0.1.0" +edition = "2021" + +[dependencies] +syscall_ebpf-common = { path = "../syscall_ebpf-common", features = ["user"] } + +anyhow = { workspace = true, default-features = true } +aya = { workspace = true } +aya-log = { workspace = true } +env_logger = { workspace = true } +libc = { workspace = true } +log = { workspace = true } +tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread", "net", "signal", "time"] } + +[build-dependencies] +cargo_metadata = { workspace = true } +# TODO(https://github.com/rust-lang/cargo/issues/12375): this should be an artifact dependency, but +# it's not possible to tell cargo to use `-Z build-std` to build it. We cargo-in-cargo in the build +# script to build this, but we want to teach cargo about the dependecy so that cache invalidation +# works properly. +# +# Note also that https://github.com/rust-lang/cargo/issues/10593 occurs when `target = ...` is added +# to an artifact dependency; it seems possible to work around that by setting `resolver = "1"` in +# Cargo.toml in the workspace root. +# +# Finally note that *any* usage of `artifact = ...` in *any* Cargo.toml in the workspace breaks +# workflows with stable cargo; stable cargo outright refuses to load manifests that use unstable +# features. +syscall_ebpf-ebpf = { path = "../syscall_ebpf-ebpf" } + +[[bin]] +name = "syscall_ebpf" +path = "src/main.rs" diff --git a/user/apps/syscall_ebpf/syscall_ebpf/build.rs b/user/apps/syscall_ebpf/syscall_ebpf/build.rs new file mode 100644 index 000000000..52b2ec291 --- /dev/null +++ b/user/apps/syscall_ebpf/syscall_ebpf/build.rs @@ -0,0 +1,150 @@ +use std::{ + env, fs, + io::{BufRead as _, BufReader}, + path::PathBuf, + process::{Child, Command, Stdio}, +}; + +use cargo_metadata::{ + Artifact, CompilerMessage, Message, Metadata, MetadataCommand, Package, Target, +}; + +/// This crate has a runtime dependency on artifacts produced by the `syscall_ebpf-ebpf` crate. +/// This would be better expressed as one or more [artifact-dependencies][bindeps] but issues such +/// as: +/// +/// * https://github.com/rust-lang/cargo/issues/12374 +/// * https://github.com/rust-lang/cargo/issues/12375 +/// * https://github.com/rust-lang/cargo/issues/12385 +/// +/// prevent their use for the time being. +/// +/// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies +fn main() { + let Metadata { packages, .. } = MetadataCommand::new().no_deps().exec().unwrap(); + let ebpf_package = packages + .into_iter() + .find(|Package { name, .. }| name == "syscall_ebpf-ebpf") + .unwrap(); + + let out_dir = env::var_os("OUT_DIR").unwrap(); + let out_dir = PathBuf::from(out_dir); + + let endian = env::var_os("CARGO_CFG_TARGET_ENDIAN").unwrap(); + let target = if endian == "big" { + "bpfeb" + } else if endian == "little" { + "bpfel" + } else { + panic!("unsupported endian={:?}", endian) + }; + + // TODO(https://github.com/rust-lang/cargo/issues/4001): Make this `false` if we can determine + // we're in a check build. + let build_ebpf = true; + if build_ebpf { + let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap(); + + let target = format!("{target}-unknown-none"); + + let Package { manifest_path, .. } = ebpf_package; + let ebpf_dir = manifest_path.parent().unwrap(); + + // We have a build-dependency on `syscall_ebpf-ebpf`, so cargo will automatically rebuild us + // if `syscall_ebpf-ebpf`'s *library* target or any of its dependencies change. Since we + // depend on `syscall_ebpf-ebpf`'s *binary* targets, that only gets us half of the way. This + // stanza ensures cargo will rebuild us on changes to the binaries too, which gets us the + // rest of the way. + println!("cargo:rerun-if-changed={}", ebpf_dir.as_str()); + + let mut cmd = Command::new("cargo"); + cmd.args([ + "build", + "-Z", + "build-std=core", + "--bins", + "--message-format=json", + "--release", + "--target", + &target, + ]); + + cmd.env("CARGO_CFG_BPF_TARGET_ARCH", arch); + + // Workaround to make sure that the rust-toolchain.toml is respected. + for key in ["RUSTUP_TOOLCHAIN", "RUSTC", "RUSTC_WORKSPACE_WRAPPER"] { + cmd.env_remove(key); + } + cmd.current_dir(ebpf_dir); + + // Workaround for https://github.com/rust-lang/cargo/issues/6412 where cargo flocks itself. + let ebpf_target_dir = out_dir.join("../syscall_ebpf-ebpf"); + cmd.arg("--target-dir").arg(&ebpf_target_dir); + + let mut child = cmd + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}")); + let Child { stdout, stderr, .. } = &mut child; + + // Trampoline stdout to cargo warnings. + let stderr = stderr.take().unwrap(); + let stderr = BufReader::new(stderr); + let stderr = std::thread::spawn(move || { + for line in stderr.lines() { + let line = line.unwrap(); + println!("cargo:warning={line}"); + } + }); + + let stdout = stdout.take().unwrap(); + let stdout = BufReader::new(stdout); + let mut executables = Vec::new(); + for message in Message::parse_stream(stdout) { + #[allow(clippy::collapsible_match)] + match message.expect("valid JSON") { + Message::CompilerArtifact(Artifact { + executable, + target: Target { name, .. }, + .. + }) => { + if let Some(executable) = executable { + executables.push((name, executable.into_std_path_buf())); + } + } + Message::CompilerMessage(CompilerMessage { message, .. }) => { + for line in message.rendered.unwrap_or_default().split('\n') { + println!("cargo:warning={line}"); + } + } + Message::TextLine(line) => { + println!("cargo:warning={line}"); + } + _ => {} + } + } + + let status = child + .wait() + .unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}")); + assert_eq!(status.code(), Some(0), "{cmd:?} failed: {status:?}"); + + stderr.join().map_err(std::panic::resume_unwind).unwrap(); + + for (name, binary) in executables { + let dst = out_dir.join(name); + let _: u64 = fs::copy(&binary, &dst) + .unwrap_or_else(|err| panic!("failed to copy {binary:?} to {dst:?}: {err}")); + } + } else { + let Package { targets, .. } = ebpf_package; + for Target { name, kind, .. } in targets { + if *kind != ["bin"] { + continue; + } + let dst = out_dir.join(name); + fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}")); + } + } +} diff --git a/user/apps/syscall_ebpf/syscall_ebpf/src/main.rs b/user/apps/syscall_ebpf/syscall_ebpf/src/main.rs new file mode 100644 index 000000000..a65e18036 --- /dev/null +++ b/user/apps/syscall_ebpf/syscall_ebpf/src/main.rs @@ -0,0 +1,74 @@ +use aya::{maps::HashMap, programs::KProbe}; +#[rustfmt::skip] +use log::{debug, warn}; +use tokio::{signal, task::yield_now, time}; + +extern crate libc; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> anyhow::Result<()> { + // env_logger::init(); + env_logger::builder() + .filter_level(log::LevelFilter::Warn) + .format_timestamp(None) + .init(); + + // Bump the memlock rlimit. This is needed for older kernels that don't use the + // new memcg based accounting, see https://lwn.net/Articles/837122/ + let rlim = libc::rlimit { + rlim_cur: libc::RLIM_INFINITY, + rlim_max: libc::RLIM_INFINITY, + }; + let ret = unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; + if ret != 0 { + debug!("remove limit on locked memory failed, ret is: {}", ret); + } + + // This will include your eBPF object file as raw bytes at compile-time and load it at + // runtime. This approach is recommended for most real-world use cases. If you would + // like to specify the eBPF program at runtime rather than at compile-time, you can + // reach for `Bpf::load_file` instead. + let mut ebpf = aya::Ebpf::load(aya::include_bytes_aligned!(concat!( + env!("OUT_DIR"), + "/syscall_ebpf" + )))?; + if let Err(e) = aya_log::EbpfLogger::init(&mut ebpf) { + // This can happen if you remove all log statements from your eBPF program. + warn!("failed to initialize eBPF logger: {}", e); + } + + let program: &mut KProbe = ebpf.program_mut("syscall_ebpf").unwrap().try_into()?; + program.load()?; + program.attach("dragonos_kernel::syscall::Syscall::handle", 0)?; + // println!("attacch the kprobe to dragonos_kernel::syscall::Syscall::handle"); + + // print the value of the blocklist per 5 seconds + tokio::spawn(async move { + let blocklist: HashMap<_, u32, u32> = + HashMap::try_from(ebpf.map("SYSCALL_LIST").unwrap()).unwrap(); + let mut now = time::Instant::now(); + loop { + let new_now = time::Instant::now(); + let duration = new_now.duration_since(now); + if duration.as_secs() >= 5 { + println!("------------SYSCALL_LIST----------------"); + let iter = blocklist.iter(); + for item in iter { + if let Ok((key, value)) = item { + println!("syscall: {:?}, count: {:?}", key, value); + } + } + println!("----------------------------------------"); + now = new_now; + } + yield_now().await; + } + }); + + let ctrl_c = signal::ctrl_c(); + println!("Waiting for Ctrl-C..."); + ctrl_c.await?; + println!("Exiting..."); + + Ok(()) +} diff --git a/user/apps/test-sigprocmask/.gitignore b/user/apps/test-sigprocmask/.gitignore new file mode 100644 index 000000000..cf1f429a0 --- /dev/null +++ b/user/apps/test-sigprocmask/.gitignore @@ -0,0 +1 @@ +test-sigprocmask \ No newline at end of file diff --git a/user/apps/test-sigprocmask/Makefile b/user/apps/test-sigprocmask/Makefile new file mode 100644 index 000000000..d0e4c2e37 --- /dev/null +++ b/user/apps/test-sigprocmask/Makefile @@ -0,0 +1,20 @@ +ifeq ($(ARCH), x86_64) + CROSS_COMPILE=x86_64-linux-musl- +else ifeq ($(ARCH), riscv64) + CROSS_COMPILE=riscv64-linux-musl- +endif + +CC=$(CROSS_COMPILE)gcc + +.PHONY: all +all: main.c + $(CC) -static -o test-sigprocmask main.c + +.PHONY: install clean +install: all + mv test-sigprocmask $(DADK_CURRENT_BUILD_DIR)/test-sigprocmask + +clean: + rm test-sigprocmask *.o + +fmt: diff --git a/user/apps/test-sigprocmask/main.c b/user/apps/test-sigprocmask/main.c new file mode 100644 index 000000000..870d86d47 --- /dev/null +++ b/user/apps/test-sigprocmask/main.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include + +#define TEST_ASSERT(left, right, success_msg, fail_msg) \ + do { \ + if ((left) == (right)) { \ + printf("[PASS] %s\n", success_msg); \ + } else { \ + printf("[FAIL] %s: Expected 0x%lx, but got 0x%lx\n", \ + fail_msg, \ + (unsigned long)(right), \ + (unsigned long)(left)); \ + } \ + } while (0) + + +static int signal_received = 0; + +void signal_handler(int signo) { + if (signo == SIGINT) { + printf("\nReceived SIGINT (Ctrl+C)\n"); + signal_received = 1; + } +} + +void print_signal_mask(const char *msg, const sigset_t *mask) { + printf("%s: ", msg); + for (int signo = 1; signo < NSIG; ++signo) { + if (sigismember(mask, signo)) { + printf("%d ", signo); + } + } + printf("\n"); +} + +// 获取当前屏蔽字的函数 +unsigned long get_signal_mask() { + sigset_t sigset; + if (sigprocmask(SIG_BLOCK, NULL, &sigset) == -1) { + perror("sigprocmask"); + return -1; // 返回错误标记 + } + + // 将信号集编码为位掩码 + unsigned long mask = 0; + for (int i = 1; i < NSIG; i++) { + if (sigismember(&sigset, i)) { + mask |= 1UL << (i - 1); + } + } + return mask; +} + +int main() { + sigset_t new_mask, old_mask; + sigemptyset(&old_mask); + + // 注册 SIGINT 的信号处理函数 + if (signal(SIGINT, signal_handler) == SIG_ERR) { + perror("signal"); + exit(EXIT_FAILURE); + } + printf("Signal handler for SIGINT is registered.\n"); + signal_received = 0; + kill(getpid(), SIGINT); + sleep(5); + + TEST_ASSERT(signal_received, 1, "SIGINT was received", "SIGINT was not received"); + signal_received = 0; + + // 初始化新的信号集,并将 SIGINT 添加到其中 + sigemptyset(&new_mask); + sigaddset(&new_mask, SIGINT); + + // 打印 new_mask 的值 + print_signal_mask("new_mask", &new_mask); + + // 屏蔽 SIGINT + if (sigprocmask(SIG_BLOCK, &new_mask, &old_mask) < 0) { + perror("sigprocmask - SIG_BLOCK"); + exit(EXIT_FAILURE); + } + + // 打印 old_mask 的值 + print_signal_mask("old_mask", &old_mask); + + // 检查 SIGINT 是否被屏蔽 + unsigned long actual_mask = get_signal_mask(); + unsigned long expected_mask = (1UL << (SIGINT - 1)); + TEST_ASSERT(actual_mask, + expected_mask, + "Signal mask is as expected", + "Signal mask mismatch"); + + printf("SIGINT is now blocked.\n"); + signal_received = 0; + // 向当前进程发送 SIGINT + kill(getpid(), SIGINT); + + // 等待 5 秒,以便测试 SIGINT 是否被屏蔽 + sleep(5); + TEST_ASSERT(signal_received, 0, "SIGINT was blocked", "SIGINT was not blocked"); + signal_received = 0; + // 恢复原来的信号屏蔽字 + if (sigprocmask(SIG_SETMASK, &old_mask, &old_mask) < 0) { + perror("sigprocmask - SIG_SETMASK"); + exit(EXIT_FAILURE); + } + print_signal_mask("old_mask returned", &old_mask); + + // 检查 SIGINT 是否被解除屏蔽 + actual_mask = get_signal_mask(); + expected_mask = 0; + TEST_ASSERT(actual_mask, + expected_mask, + "Signal mask is as expected", + "Signal mask mismatch"); + + printf("SIGINT is now unblocked.\n"); + + signal_received = 0; + kill(getpid(), SIGINT); + + // 等待 5 秒,以便测试 SIGINT 是否解除屏蔽 + sleep(5); + TEST_ASSERT(signal_received, 1, "SIGINT was received", "SIGINT was not received"); + + printf("Exiting program.\n"); + return 0; +} diff --git a/user/apps/test_ebpf/.gitignore b/user/apps/test_ebpf/.gitignore deleted file mode 100644 index 1ac354611..000000000 --- a/user/apps/test_ebpf/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -Cargo.lock -/install/ \ No newline at end of file diff --git a/user/apps/test_ebpf/Cargo.toml b/user/apps/test_ebpf/Cargo.toml deleted file mode 100644 index ab4a3b7f9..000000000 --- a/user/apps/test_ebpf/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "test_ebpf" -version = "0.1.0" -edition = "2021" - -[dependencies] -aya = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/tiny-aya.git", rev = "0689f13" } -aya-log = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/tiny-aya.git", rev = "0689f13" } - -log = "0.4.22" -env_logger = "0.11.5" -tokio = { version = "1.25", features = ["macros", "rt", "rt-multi-thread", "net", "signal", "time"] } - -[profile.release] -lto = true -strip = true diff --git a/user/apps/test_ebpf/src/main.rs b/user/apps/test_ebpf/src/main.rs deleted file mode 100644 index 1909aeeb9..000000000 --- a/user/apps/test_ebpf/src/main.rs +++ /dev/null @@ -1,60 +0,0 @@ -use aya::maps::HashMap; -use aya::programs::KProbe; -use aya::{include_bytes_aligned, Ebpf}; -use aya_log::EbpfLogger; -use log::{info, warn}; -use std::error::Error; -use tokio::task::yield_now; -use tokio::{signal, time}; - -#[tokio::main(flavor = "current_thread")] -async fn main() -> Result<(), Box> { - env_logger::builder() - .filter_level(log::LevelFilter::Warn) - .format_timestamp(None) - .init(); - - let mut bpf = Ebpf::load(include_bytes_aligned!( - "../syscall_ebpf/target/bpfel-unknown-none/release/syscall_ebpf" - ))?; - - // create a async task to read the log - if let Err(e) = EbpfLogger::init(&mut bpf) { - // This can happen if you remove all log statements from your eBPF program. - warn!("failed to initialize eBPF logger: {}", e); - } - - let program: &mut KProbe = bpf.program_mut("syscall_ebpf").unwrap().try_into()?; - program.load()?; - program.attach("dragonos_kernel::syscall::Syscall::handle", 0)?; - - info!("attacch the kprobe to dragonos_kernel::syscall::Syscall::handle"); - - // print the value of the blocklist per 5 seconds - tokio::spawn(async move { - let blocklist: HashMap<_, u32, u32> = - HashMap::try_from(bpf.map("SYSCALL_LIST").unwrap()).unwrap(); - let mut now = time::Instant::now(); - loop { - let new_now = time::Instant::now(); - let duration = new_now.duration_since(now); - if duration.as_secs() >= 5 { - println!("------------SYSCALL_LIST----------------"); - let iter = blocklist.iter(); - for item in iter { - if let Ok((key, value)) = item { - println!("syscall: {:?}, count: {:?}", key, value); - } - } - println!("----------------------------------------"); - now = new_now; - } - yield_now().await; - } - }); - - info!("Waiting for Ctrl-C..."); - signal::ctrl_c().await?; - info!("Exiting..."); - Ok(()) -} diff --git a/user/apps/test_ebpf/syscall_ebpf/.cargo/config.toml b/user/apps/test_ebpf/syscall_ebpf/.cargo/config.toml deleted file mode 100644 index 35049cbcb..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[alias] -xtask = "run --package xtask --" diff --git a/user/apps/test_ebpf/syscall_ebpf/.vscode/settings.json b/user/apps/test_ebpf/syscall_ebpf/.vscode/settings.json deleted file mode 100644 index 0c82ac973..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rust-analyzer.linkedProjects": ["Cargo.toml", "syscall_ebpf-ebpf/Cargo.toml"] -} diff --git a/user/apps/test_ebpf/syscall_ebpf/Cargo.toml b/user/apps/test_ebpf/syscall_ebpf/Cargo.toml deleted file mode 100644 index 6eb4e6322..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/Cargo.toml +++ /dev/null @@ -1,3 +0,0 @@ -[workspace] -resolver = "2" -members = ["xtask", "syscall_ebpf-common"] diff --git a/user/apps/test_ebpf/syscall_ebpf/README.md b/user/apps/test_ebpf/syscall_ebpf/README.md deleted file mode 100644 index fe5ed32d3..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# syscall_ebpf - -## Prerequisites - -1. Install bpf-linker: `cargo install bpf-linker` - -## Build eBPF - -```bash -cargo xtask build-ebpf -``` - -To perform a release build you can use the `--release` flag. -You may also change the target architecture with the `--target` flag. - -## Build Userspace - -```bash -cargo build -``` - -## Build eBPF and Userspace - -```bash -cargo xtask build -``` - -## Run - -```bash -RUST_LOG=info cargo xtask run -``` diff --git a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.cargo/config.toml b/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.cargo/config.toml deleted file mode 100644 index 4302a7f16..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.cargo/config.toml +++ /dev/null @@ -1,6 +0,0 @@ -[build] -target-dir = "../target" -target = "bpfel-unknown-none" - -[unstable] -build-std = ["core"] diff --git a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.helix/config.toml b/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.helix/config.toml deleted file mode 100644 index da5424f19..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.helix/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[editor] -workspace-lsp-roots = [] diff --git a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.vim/coc-settings.json b/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.vim/coc-settings.json deleted file mode 100644 index e2211a64f..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.vim/coc-settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rust-analyzer.cargo.target": "bpfel-unknown-none", - "rust-analyzer.checkOnSave.allTargets": false -} diff --git a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.vscode/settings.json b/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.vscode/settings.json deleted file mode 100644 index e2211a64f..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rust-analyzer.cargo.target": "bpfel-unknown-none", - "rust-analyzer.checkOnSave.allTargets": false -} diff --git a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/Cargo.toml b/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/Cargo.toml deleted file mode 100644 index 1911fa43c..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "syscall_ebpf-ebpf" -version = "0.1.0" -edition = "2021" - -[dependencies] -aya-ebpf = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/aya.git", rev = "3d57d35" } -aya-log-ebpf = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/aya.git", rev = "3d57d35" } - -syscall_ebpf-common = { path = "../syscall_ebpf-common" } - -[[bin]] -name = "syscall_ebpf" -path = "src/main.rs" - -[profile.dev] -opt-level = 3 -debug = false -debug-assertions = false -overflow-checks = false -lto = true -panic = "abort" -incremental = false -codegen-units = 1 -rpath = false - -[profile.release] -lto = true -panic = "abort" -codegen-units = 1 - -[workspace] -members = [] diff --git a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/rust-toolchain.toml b/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/rust-toolchain.toml deleted file mode 100644 index fda4ec82e..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/rust-toolchain.toml +++ /dev/null @@ -1,13 +0,0 @@ -[toolchain] -channel = "nightly-2024-11-05" -# The source code of rustc, provided by the rust-src component, is needed for -# building eBPF programs. -components = [ - "cargo", - "clippy", - "rust-docs", - "rust-src", - "rust-std", - "rustc", - "rustfmt", -] diff --git a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/src/main.rs b/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/src/main.rs deleted file mode 100644 index 7f9b79b65..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/syscall_ebpf-ebpf/src/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![no_std] -#![no_main] - -use aya_ebpf::{macros::kprobe, programs::ProbeContext}; -use aya_ebpf::macros::map; -use aya_ebpf::maps::HashMap; -use aya_log_ebpf::info; - -#[kprobe] -pub fn syscall_ebpf(ctx: ProbeContext) -> u32 { - try_syscall_ebpf(ctx).unwrap_or_else(|ret| ret) -} - -fn try_syscall_ebpf(ctx: ProbeContext) -> Result { - let pt_regs = unsafe { - &*ctx.regs - }; - // first arg -> rdi - // second arg -> rsi - // third arg -> rdx - // four arg -> rcx - let syscall_num = pt_regs.rsi as usize; - if syscall_num != 1 { - unsafe { - if let Some(v) = SYSCALL_LIST.get(&(syscall_num as u32)){ - let new_v = *v + 1; - SYSCALL_LIST.insert(&(syscall_num as u32), &new_v,0).unwrap(); - }else { - SYSCALL_LIST.insert(&(syscall_num as u32), &1,0).unwrap(); - } - } - info!(&ctx, "invoke syscall {}", syscall_num); - } - Ok(0) -} - -#[map] // -static SYSCALL_LIST: HashMap = - HashMap::::with_max_entries(1024, 0); - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - unsafe { core::hint::unreachable_unchecked() } -} diff --git a/user/apps/test_ebpf/syscall_ebpf/xtask/Cargo.toml b/user/apps/test_ebpf/syscall_ebpf/xtask/Cargo.toml deleted file mode 100644 index c4dea5d16..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/xtask/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "xtask" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow = "1" -clap = { version = "4.1", features = ["derive"] } diff --git a/user/apps/test_ebpf/syscall_ebpf/xtask/src/build.rs b/user/apps/test_ebpf/syscall_ebpf/xtask/src/build.rs deleted file mode 100644 index ddeee4496..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/xtask/src/build.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::process::Command; - -use anyhow::Context as _; -use clap::Parser; - -use crate::build_ebpf::{build_ebpf, Architecture, Options as BuildOptions}; - -#[derive(Debug, Parser)] -pub struct Options { - /// Set the endianness of the BPF target - #[clap(default_value = "bpfel-unknown-none", long)] - pub bpf_target: Architecture, - /// Build and run the release target - #[clap(long)] - pub release: bool, -} - -/// Build the project -fn build_project(opts: &Options) -> Result<(), anyhow::Error> { - let mut args = vec!["build"]; - if opts.release { - args.push("--release") - } - let status = Command::new("cargo") - .args(&args) - .status() - .expect("failed to build userspace"); - assert!(status.success()); - Ok(()) -} - -/// Build our ebpf program and the project -pub fn build(opts: Options) -> Result<(), anyhow::Error> { - // build our ebpf program followed by our application - build_ebpf(BuildOptions { - target: opts.bpf_target, - release: opts.release, - }) - .context("Error while building eBPF program")?; - build_project(&opts).context("Error while building userspace application")?; - Ok(()) -} \ No newline at end of file diff --git a/user/apps/test_ebpf/syscall_ebpf/xtask/src/build_ebpf.rs b/user/apps/test_ebpf/syscall_ebpf/xtask/src/build_ebpf.rs deleted file mode 100644 index 8c6e323f5..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/xtask/src/build_ebpf.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::{path::PathBuf, process::Command}; - -use clap::Parser; - -#[derive(Debug, Copy, Clone)] -pub enum Architecture { - BpfEl, - BpfEb, -} - -impl std::str::FromStr for Architecture { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - "bpfel-unknown-none" => Architecture::BpfEl, - "bpfeb-unknown-none" => Architecture::BpfEb, - _ => return Err("invalid target".to_owned()), - }) - } -} - -impl std::fmt::Display for Architecture { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - Architecture::BpfEl => "bpfel-unknown-none", - Architecture::BpfEb => "bpfeb-unknown-none", - }) - } -} - -#[derive(Debug, Parser)] -pub struct Options { - /// Set the endianness of the BPF target - #[clap(default_value = "bpfel-unknown-none", long)] - pub target: Architecture, - /// Build the release target - #[clap(long)] - pub release: bool, -} - -pub fn build_ebpf(opts: Options) -> Result<(), anyhow::Error> { - let dir = PathBuf::from("syscall_ebpf-ebpf"); - let target = format!("--target={}", opts.target); - let mut args = vec![ - "build", - target.as_str(), - "-Z", - "build-std=core", - ]; - if opts.release { - args.push("--release") - } - - // Command::new creates a child process which inherits all env variables. This means env - // vars set by the cargo xtask command are also inherited. RUSTUP_TOOLCHAIN is removed - // so the rust-toolchain.toml file in the -ebpf folder is honored. - - let status = Command::new("cargo") - .current_dir(dir) - .env_remove("RUSTUP_TOOLCHAIN") - .args(&args) - .status() - .expect("failed to build bpf program"); - assert!(status.success()); - Ok(()) -} diff --git a/user/apps/test_ebpf/syscall_ebpf/xtask/src/main.rs b/user/apps/test_ebpf/syscall_ebpf/xtask/src/main.rs deleted file mode 100644 index 507945899..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/xtask/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -mod build_ebpf; -mod build; -mod run; - -use std::process::exit; - -use clap::Parser; - -#[derive(Debug, Parser)] -pub struct Options { - #[clap(subcommand)] - command: Command, -} - -#[derive(Debug, Parser)] -enum Command { - BuildEbpf(build_ebpf::Options), - Build(build::Options), - Run(run::Options), -} - -fn main() { - let opts = Options::parse(); - - use Command::*; - let ret = match opts.command { - BuildEbpf(opts) => build_ebpf::build_ebpf(opts), - Run(opts) => run::run(opts), - Build(opts) => build::build(opts), - }; - - if let Err(e) = ret { - eprintln!("{e:#}"); - exit(1); - } -} diff --git a/user/apps/test_ebpf/syscall_ebpf/xtask/src/run.rs b/user/apps/test_ebpf/syscall_ebpf/xtask/src/run.rs deleted file mode 100644 index 19af11c45..000000000 --- a/user/apps/test_ebpf/syscall_ebpf/xtask/src/run.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::process::Command; - -use anyhow::Context as _; -use clap::Parser; - -use crate::{build::{build, Options as BuildOptions}, build_ebpf::Architecture}; - -#[derive(Debug, Parser)] -pub struct Options { - /// Set the endianness of the BPF target - #[clap(default_value = "bpfel-unknown-none", long)] - pub bpf_target: Architecture, - /// Build and run the release target - #[clap(long)] - pub release: bool, - /// The command used to wrap your application - #[clap(short, long, default_value = "sudo -E")] - pub runner: String, - /// Arguments to pass to your application - #[clap(name = "args", last = true)] - pub run_args: Vec, -} - - -/// Build and run the project -pub fn run(opts: Options) -> Result<(), anyhow::Error> { - // Build our ebpf program and the project - build(BuildOptions{ - bpf_target: opts.bpf_target, - release: opts.release, - }).context("Error while building project")?; - - // profile we are building (release or debug) - let profile = if opts.release { "release" } else { "debug" }; - let bin_path = format!("target/{profile}/syscall_ebpf"); - - // arguments to pass to the application - let mut run_args: Vec<_> = opts.run_args.iter().map(String::as_str).collect(); - - // configure args - let mut args: Vec<_> = opts.runner.trim().split_terminator(' ').collect(); - args.push(bin_path.as_str()); - args.append(&mut run_args); - - // run the command - let status = Command::new(args.first().expect("No first argument")) - .args(args.iter().skip(1)) - .status() - .expect("failed to run the command"); - - if !status.success() { - anyhow::bail!("Failed to run `{}`", args.join(" ")); - } - Ok(()) -} diff --git a/user/apps/test_signal_restart/.gitignore b/user/apps/test_signal_restart/.gitignore new file mode 100644 index 000000000..802b4439e --- /dev/null +++ b/user/apps/test_signal_restart/.gitignore @@ -0,0 +1 @@ +test_signal diff --git a/user/apps/test_signal_restart/Makefile b/user/apps/test_signal_restart/Makefile new file mode 100644 index 000000000..2a9e25aac --- /dev/null +++ b/user/apps/test_signal_restart/Makefile @@ -0,0 +1,20 @@ +ifeq ($(ARCH), x86_64) + CROSS_COMPILE=x86_64-linux-musl- +else ifeq ($(ARCH), riscv64) + CROSS_COMPILE=riscv64-linux-musl- +endif + +CC=$(CROSS_COMPILE)gcc + +.PHONY: all +all: main.c + $(CC) -static -o test_signal_restart main.c + +.PHONY: install clean +install: all + mv test_signal_restart $(DADK_CURRENT_BUILD_DIR)/test_signal_restart + +clean: + rm test_signal_restart *.o + +fmt: diff --git a/user/apps/test_signal_restart/main.c b/user/apps/test_signal_restart/main.c new file mode 100644 index 000000000..e0c90756b --- /dev/null +++ b/user/apps/test_signal_restart/main.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 1024 + +#define MSG "Hello from parent!\n" + +static int handled_signal = 0; +// 子进程的信号处理函数 +void child_signal_handler(int sig) { + printf("Child received signal %d\n", sig); + handled_signal = 1; +} + +// 父进程的信号处理函数 +void parent_signal_handler(int sig) { + printf("Parent received signal %d\n", sig); +} + +int main() { + int pipefd[2]; + pid_t pid; + char buffer[BUFFER_SIZE]; + + // 创建管道 + if (pipe(pipefd) == -1) { + perror("pipe"); + exit(EXIT_FAILURE); + } + + // 创建子进程 + pid = fork(); + if (pid == -1) { + perror("fork"); + exit(EXIT_FAILURE); + } + + if (pid == 0) { + // 子进程 + close(pipefd[1]); // 关闭写端 + + // 设置子进程的信号处理函数 + signal(SIGUSR1, child_signal_handler); + + printf("Child: Waiting for data...\n"); + + // 尝试从管道中读取数据 + ssize_t bytes_read = read(pipefd[0], buffer, BUFFER_SIZE - 1); + if (bytes_read == -1) { + printf("[FAILED]: Child: read error, errno=%d\n", errno); + exit(EXIT_FAILURE); + } else if (bytes_read == 0) { + printf("Child: End of file\n"); + } + + if (bytes_read != sizeof(MSG) - 1) { + printf("[FAILED]: Child: read error: got %ld bytes, expected %ld\n", + bytes_read, sizeof(MSG) - 1); + } else { + printf("[PASS]: Child: read success: got %ld bytes, expected %ld\n", + bytes_read, sizeof(MSG) - 1); + } + + buffer[bytes_read] = '\0'; + printf("Child: Received message: %s", buffer); + + close(pipefd[0]); + + if (!handled_signal) + printf("[FAILED]: Parent: child did not handle signal\n"); + else + printf("[PASS]: Parent: child handled signal\n"); + exit(EXIT_SUCCESS); + } else { + // 父进程 + close(pipefd[0]); // 关闭读端 + + // 设置父进程的信号处理函数 + signal(SIGCHLD, parent_signal_handler); + + // 发送信号给子进程,中断它的读操作 + sleep(1); // 确保子进程已经开始读取 + // printf("Parent: Sending SIGCHLD to child...\n"); + // kill(pid, SIGCHLD); + printf("Parent: Sending SIGUSR1 to child...\n"); + kill(pid, SIGUSR1); + sleep(1); // 确保子进程已经处理了信号 + + write(pipefd[1], MSG, strlen(MSG)); + + printf("Parent: Sent message: %s", MSG); + + // 等待子进程结束 + waitpid(pid, NULL, 0); + + printf("Parent: Child process finished.\n"); + + close(pipefd[1]); + exit(EXIT_SUCCESS); + } +} \ No newline at end of file diff --git a/user/dadk/config/test_ebpf_0_1_0.toml b/user/dadk/config/syscall_ebpf_0_1_0.toml similarity index 91% rename from user/dadk/config/test_ebpf_0_1_0.toml rename to user/dadk/config/syscall_ebpf_0_1_0.toml index 6937a23e8..3df5f6250 100644 --- a/user/dadk/config/test_ebpf_0_1_0.toml +++ b/user/dadk/config/syscall_ebpf_0_1_0.toml @@ -1,5 +1,5 @@ # 用户程序名称 -name = "test_ebpf" +name = "test_ebpf_new" # 版本号 version = "0.1.0" # 用户程序描述信息 @@ -21,7 +21,7 @@ type = "build-from-source" # "install_from_prebuilt" 可选值:"local", "archive" source = "local" # 路径或URL -source-path = "user/apps/test_ebpf" +source-path = "user/apps/syscall_ebpf" # 构建相关信息 [build] # (可选)构建命令 diff --git a/user/dadk/config/test_signal_restart-0.1.0.toml b/user/dadk/config/test_signal_restart-0.1.0.toml new file mode 100644 index 000000000..0b3896255 --- /dev/null +++ b/user/dadk/config/test_signal_restart-0.1.0.toml @@ -0,0 +1,41 @@ +# 用户程序名称 +name = "test_signal_restart" +# 版本号 +version = "0.1.0" +# 用户程序描述信息 +description = "一个用来测试signal能够正常运行的app" +# (可选)默认: false 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果 +build-once = false +# (可选) 默认: false 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装 +install-once = false +# 目标架构 +# 可选值:"x86_64", "aarch64", "riscv64" +target-arch = ["x86_64"] +# 任务源 +[task-source] +# 构建类型 +# 可选值:"build-from_source", "install-from-prebuilt" +type = "build-from-source" +# 构建来源 +# "build_from_source" 可选值:"git", "local", "archive" +# "install_from_prebuilt" 可选值:"local", "archive" +source = "local" +# 路径或URL +source-path = "user/apps/test_signal_restart" +# 构建相关信息 +[build] +# (可选)构建命令 +build-command = "make install" +# 安装相关信息 +[install] +# (可选)安装到DragonOS的路径 +in-dragonos-path = "/bin" +# 清除相关信息 +[clean] +# (可选)清除命令 +clean-command = "make clean" +# (可选)依赖项 +# 注意:如果没有依赖项,忽略此项,不允许只留一个[[depends]] +# 由于原文件中依赖项为空,此处省略[[depends]]部分 +# (可选)环境变量 +# 由于原文件中环境变量为空,此处省略[[envs]]部分 diff --git a/user/dadk/config/test_sigprocmask_0_1_0.toml b/user/dadk/config/test_sigprocmask_0_1_0.toml new file mode 100644 index 000000000..015e1203a --- /dev/null +++ b/user/dadk/config/test_sigprocmask_0_1_0.toml @@ -0,0 +1,41 @@ +# 用户程序名称 +name = "test_sigprocmask" +# 版本号 +version = "0.1.0" +# 用户程序描述信息 +description = "一个用来测试sigprocmask能够正常运行的app" +# (可选)默认: false 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果 +build-once = false +# (可选) 默认: false 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装 +install-once = false +# 目标架构 +# 可选值:"x86_64", "aarch64", "riscv64" +target-arch = ["x86_64"] +# 任务源 +[task-source] +# 构建类型 +# 可选值:"build-from_source", "install-from-prebuilt" +type = "build-from-source" +# 构建来源 +# "build_from_source" 可选值:"git", "local", "archive" +# "install_from_prebuilt" 可选值:"local", "archive" +source = "local" +# 路径或URL +source-path = "user/apps/test-sigprocmask" +# 构建相关信息 +[build] +# (可选)构建命令 +build-command = "make install" +# 安装相关信息 +[install] +# (可选)安装到DragonOS的路径 +in-dragonos-path = "/bin" +# 清除相关信息 +[clean] +# (可选)清除命令 +clean-command = "make clean" +# (可选)依赖项 +# 注意:如果没有依赖项,忽略此项,不允许只留一个[[depends]] +# 由于原文件中依赖项为空,此处省略[[depends]]部分 +# (可选)环境变量 +# 由于原文件中环境变量为空,此处省略[[envs]]部分