From 590d66332784e3a7a0602a6c96dab447e76dedbd Mon Sep 17 00:00:00 2001 From: Godones Date: Sat, 20 Sep 2025 23:30:50 +0800 Subject: [PATCH 1/7] feat: Add conversion of trapframe to kprobe's pt_regs Add custom callback for break exception. Signed-off-by: Godones --- Cargo.lock | 41 ++++++++++++++++ Cargo.toml | 1 + src/loongarch64/context.rs | 43 ++++++++++++++++- src/loongarch64/trap.rs | 11 +++-- src/riscv/context.rs | 97 +++++++++++++++++++++++++++++++++++--- src/riscv/trap.rs | 21 ++++++--- src/trap.rs | 4 ++ 7 files changed, 199 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e10161..989dbfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,7 @@ version = "0.2.2" dependencies = [ "aarch64-cpu", "axbacktrace", + "axprobe", "cfg-if", "lazyinit", "linkme", @@ -50,6 +51,17 @@ dependencies = [ "x86_64", ] +[[package]] +name = "axprobe" +version = "0.1.0" +source = "git+https://github.com/Starry-OS/axprobe#772e2e164e896e2d27759204d3c790aa36a7c2c3" +dependencies = [ + "lock_api", + "log", + "yaxpeax-arch", + "yaxpeax-x86", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -144,6 +156,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d4054cba279515fa87761b101d857333ce06391dbe8f18a11347204a7111416" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "page_table_entry" version = "0.5.5" @@ -339,3 +360,23 @@ dependencies = [ "rustversion", "volatile", ] + +[[package]] +name = "yaxpeax-arch" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36274fcc5403da2a7636ffda4d02eca12a1b2b8267b9d2e04447bd2ccfc72082" +dependencies = [ + "num-traits", +] + +[[package]] +name = "yaxpeax-x86" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9a30b7dd533c7b1a73eaf7c4ea162a7a632a2bb29b9fff47d8f2cc8513a883" +dependencies = [ + "cfg-if", + "num-traits", + "yaxpeax-arch", +] diff --git a/Cargo.toml b/Cargo.toml index 3b71305..9460cdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ cfg-if = "1.0" memory_addr = "0.4" page_table_entry = "0.5" static_assertions = "1.1.0" +axprobe = { git = "https://github.com/Starry-OS/axprobe" } [target.'cfg(target_arch = "x86_64")'.dependencies] lazyinit = "0.2" diff --git a/src/loongarch64/context.rs b/src/loongarch64/context.rs index 3d9b75f..3330904 100644 --- a/src/loongarch64/context.rs +++ b/src/loongarch64/context.rs @@ -1,6 +1,8 @@ use core::arch::naked_asm; #[cfg(feature = "fp-simd")] use core::mem::offset_of; + +use axprobe::PtRegs; use memory_addr::VirtAddr; /// General registers of Loongarch64. @@ -259,8 +261,8 @@ impl TaskContext { /// Switches to another task. /// - /// It first saves the current task's context from CPU to this place, and then - /// restores the next task's context from `next_ctx` to CPU. + /// It first saves the current task's context from CPU to this place, and + /// then restores the next task's context from `next_ctx` to CPU. pub fn switch_to(&mut self, next_ctx: &Self) { #[cfg(feature = "tls")] { @@ -353,3 +355,40 @@ unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: ret", ) } + +impl TrapFrame { + pub fn to_pt_regs(&mut self) -> axprobe::PtRegs { + let regs = [0; 32]; + unsafe { + core::ptr::copy_nonoverlapping( + &self.regs.zero as *const usize, + regs.as_ptr() as *mut usize, + 32, + ); + } + PtRegs { + regs, + orig_a0: 0, + csr_era: self.era, + csr_badvaddr: 0, + csr_crmd: 0, + csr_prmd: self.prmd, + csr_euen: 0, + csr_ecfg: 0, + csr_estat: 0, + } + } + + pub fn update_from_pt_regs(&mut self, pt_regs: axprobe::PtRegs) { + unsafe { + core::ptr::copy_nonoverlapping( + pt_regs.regs.as_ptr() as *const usize, + &mut self.regs.zero as *mut usize, + 32, + ); + } + self.era = pt_regs.csr_era; + self.prmd = pt_regs.csr_prmd; + // other csr fields are ignored + } +} diff --git a/src/loongarch64/trap.rs b/src/loongarch64/trap.rs index 193ddad..bfea70b 100644 --- a/src/loongarch64/trap.rs +++ b/src/loongarch64/trap.rs @@ -12,9 +12,12 @@ core::arch::global_asm!( trapframe_size = const (core::mem::size_of::()), ); -fn handle_breakpoint(era: &mut usize) { - debug!("Exception(Breakpoint) @ {era:#x} "); - *era += 4; +fn handle_breakpoint(tf: &mut TrapFrame) { + if core::hint::likely(handle_trap!(BREAK_HANDLER, tf)) { + return; + } + debug!("Exception(Breakpoint) @ {:#x} ", tf.era); + tf.era += 2; // skip ebreak instruction } fn handle_page_fault(tf: &mut TrapFrame, access_flags: PageFaultFlags) { @@ -54,7 +57,7 @@ fn loongarch64_trap_handler(tf: &mut TrapFrame) { | Trap::Exception(Exception::PageNonExecutableFault) => { handle_page_fault(tf, PageFaultFlags::EXECUTE); } - Trap::Exception(Exception::Breakpoint) => handle_breakpoint(&mut tf.era), + Trap::Exception(Exception::Breakpoint) => handle_breakpoint(tf), Trap::Exception(Exception::AddressNotAligned) => unsafe { tf.emulate_unaligned().unwrap(); }, diff --git a/src/riscv/context.rs b/src/riscv/context.rs index 7878aa5..ff6f98f 100644 --- a/src/riscv/context.rs +++ b/src/riscv/context.rs @@ -1,4 +1,5 @@ use core::arch::naked_asm; + use memory_addr::VirtAddr; use riscv::register::sstatus::{self, FS}; @@ -85,7 +86,8 @@ impl FpState { /// Handles floating-point state context switching /// - /// Saves the current task's FP state (if needed) and restores the next task's FP state + /// Saves the current task's FP state (if needed) and restores the next + /// task's FP state pub fn switch_to(&mut self, next_fp_state: &FpState) { // get the real FP state of the current task let current_fs = sstatus::read().fs(); @@ -98,9 +100,12 @@ impl FpState { } // restore the next task's FP state match next_fp_state.fs { - FS::Clean => next_fp_state.restore(), // the next task's FP state is clean, we should restore it - FS::Initial => FpState::clear(), // restore the FP state as constant values(all 0) - FS::Off => {} // do nothing + // the next task's FP state is clean, we should restore it + FS::Clean => next_fp_state.restore(), + // restore the FP state as constant values(all 0) + FS::Initial => FpState::clear(), + // do nothing + FS::Off => {} FS::Dirty => unreachable!("FP state of the next task should not be dirty"), } unsafe { sstatus::set_fs(next_fp_state.fs) }; // set the FP state to the next task's FP state @@ -326,8 +331,8 @@ impl TaskContext { /// Switches to another task. /// - /// It first saves the current task's context from CPU to this place, and then - /// restores the next task's context from `next_ctx` to CPU. + /// It first saves the current task's context from CPU to this place, and + /// then restores the next task's context from `next_ctx` to CPU. pub fn switch_to(&mut self, next_ctx: &Self) { #[cfg(feature = "tls")] { @@ -425,3 +430,83 @@ unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: ret", ) } + +impl TrapFrame { + pub fn to_pt_regs(&mut self) -> axprobe::PtRegs { + axprobe::PtRegs { + epc: self.sepc, + ra: self.regs.ra, + sp: self.regs.sp, + gp: self.regs.gp, + tp: self.regs.tp, + t0: self.regs.t0, + t1: self.regs.t1, + t2: self.regs.t2, + s0: self.regs.s0, + s1: self.regs.s1, + a0: self.regs.a0, + a1: self.regs.a1, + a2: self.regs.a2, + a3: self.regs.a3, + a4: self.regs.a4, + a5: self.regs.a5, + a6: self.regs.a6, + a7: self.regs.a7, + s2: self.regs.s2, + s3: self.regs.s3, + s4: self.regs.s4, + s5: self.regs.s5, + s6: self.regs.s6, + s7: self.regs.s7, + s8: self.regs.s8, + s9: self.regs.s9, + s10: self.regs.s10, + s11: self.regs.s11, + t3: self.regs.t3, + t4: self.regs.t4, + t5: self.regs.t5, + t6: self.regs.t6, + status: self.sstatus.bits(), + // todo : other fields + badaddr: 0, + cause: 0, + orig_a0: 0, + } + } + + pub fn update_from_pt_regs(&mut self, pt_regs: axprobe::PtRegs) { + self.sepc = pt_regs.epc; + self.regs.ra = pt_regs.ra; + self.regs.sp = pt_regs.sp; + self.regs.gp = pt_regs.gp; + self.regs.tp = pt_regs.tp; + self.regs.t0 = pt_regs.t0; + self.regs.t1 = pt_regs.t1; + self.regs.t2 = pt_regs.t2; + self.regs.s0 = pt_regs.s0; + self.regs.s1 = pt_regs.s1; + self.regs.a0 = pt_regs.a0; + self.regs.a1 = pt_regs.a1; + self.regs.a2 = pt_regs.a2; + self.regs.a3 = pt_regs.a3; + self.regs.a4 = pt_regs.a4; + self.regs.a5 = pt_regs.a5; + self.regs.a6 = pt_regs.a6; + self.regs.a7 = pt_regs.a7; + self.regs.s2 = pt_regs.s2; + self.regs.s3 = pt_regs.s3; + self.regs.s4 = pt_regs.s4; + self.regs.s5 = pt_regs.s5; + self.regs.s6 = pt_regs.s6; + self.regs.s7 = pt_regs.s7; + self.regs.s8 = pt_regs.s8; + self.regs.s9 = pt_regs.s9; + self.regs.s10 = pt_regs.s10; + self.regs.s11 = pt_regs.s11; + self.regs.t3 = pt_regs.t3; + self.regs.t4 = pt_regs.t4; + self.regs.t5 = pt_regs.t5; + self.regs.t6 = pt_regs.t6; + // todo : other fields + } +} diff --git a/src/riscv/trap.rs b/src/riscv/trap.rs index 44187e3..4269895 100644 --- a/src/riscv/trap.rs +++ b/src/riscv/trap.rs @@ -1,8 +1,12 @@ -use riscv::interrupt::supervisor::{Exception as E, Interrupt as I}; -use riscv::interrupt::Trap; #[cfg(feature = "fp-simd")] use riscv::register::sstatus; -use riscv::register::{scause, stval}; +use riscv::{ + interrupt::{ + supervisor::{Exception as E, Interrupt as I}, + Trap, + }, + register::{scause, stval}, +}; use super::TrapFrame; use crate::trap::PageFaultFlags; @@ -13,9 +17,12 @@ core::arch::global_asm!( trapframe_size = const core::mem::size_of::(), ); -fn handle_breakpoint(sepc: &mut usize) { - debug!("Exception(Breakpoint) @ {sepc:#x} "); - *sepc += 2 +fn handle_breakpoint(tf: &mut TrapFrame) { + if core::hint::likely(handle_trap!(BREAK_HANDLER, tf)) { + return; + } + debug!("Exception(Breakpoint) @ {:#x} ", tf.sepc); + tf.sepc += 2; // skip ebreak instruction } fn handle_page_fault(tf: &mut TrapFrame, access_flags: PageFaultFlags) { @@ -48,7 +55,7 @@ fn riscv_trap_handler(tf: &mut TrapFrame) { Trap::Exception(E::InstructionPageFault) => { handle_page_fault(tf, PageFaultFlags::EXECUTE) } - Trap::Exception(E::Breakpoint) => handle_breakpoint(&mut tf.sepc), + Trap::Exception(E::Breakpoint) => handle_breakpoint(tf), Trap::Interrupt(_) => { handle_trap!(IRQ, scause.bits()); } diff --git a/src/trap.rs b/src/trap.rs index 21fc4db..12bf0c9 100644 --- a/src/trap.rs +++ b/src/trap.rs @@ -15,6 +15,10 @@ pub static IRQ: [fn(usize) -> bool]; #[def_trap_handler] pub static PAGE_FAULT: [fn(VirtAddr, PageFaultFlags) -> bool]; +/// A slice of breakpoint handler functions. +#[def_trap_handler] +pub static BREAK_HANDLER: [fn(&mut TrapFrame) -> bool]; + #[allow(unused_macros)] macro_rules! handle_trap { ($trap:ident, $($args:tt)*) => {{ From ff7749813b74e28efed4a86c6136a15ef7154559 Mon Sep 17 00:00:00 2001 From: Godones Date: Mon, 22 Sep 2025 21:51:46 +0800 Subject: [PATCH 2/7] feat: Add a feature for kprobe Refactor the conversion implementation of TrapFrame and PtRegs. Signed-off-by: Godones --- Cargo.lock | 25 +++---- Cargo.toml | 5 +- src/loongarch64/context.rs | 30 ++++---- src/loongarch64/trap.rs | 4 +- src/riscv/context.rs | 144 +++++++++++++++++++------------------ src/riscv/trap.rs | 2 +- src/x86_64/context.rs | 56 +++++++++++++++ src/x86_64/trap.rs | 9 ++- 8 files changed, 174 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 989dbfd..388c347 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,8 +34,8 @@ version = "0.2.2" dependencies = [ "aarch64-cpu", "axbacktrace", - "axprobe", "cfg-if", + "kprobe", "lazyinit", "linkme", "log", @@ -51,17 +51,6 @@ dependencies = [ "x86_64", ] -[[package]] -name = "axprobe" -version = "0.1.0" -source = "git+https://github.com/Starry-OS/axprobe#772e2e164e896e2d27759204d3c790aa36a7c2c3" -dependencies = [ - "lock_api", - "log", - "yaxpeax-arch", - "yaxpeax-x86", -] - [[package]] name = "bit_field" version = "0.10.2" @@ -98,6 +87,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" +[[package]] +name = "kprobe" +version = "0.1.0" +source = "git+https://github.com/Starry-OS/kprobe?rev=318cbb2#318cbb242ec8285903a909433fdf8da54554b324" +dependencies = [ + "cfg-if", + "lock_api", + "log", + "yaxpeax-arch", + "yaxpeax-x86", +] + [[package]] name = "lazyinit" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 9460cdf..ae35618 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ fp-simd = [] tls = [] uspace = [] arm-el2 = [] +kprobe = ["dep:kprobe"] [dependencies] axbacktrace = "0.1" @@ -36,7 +37,9 @@ cfg-if = "1.0" memory_addr = "0.4" page_table_entry = "0.5" static_assertions = "1.1.0" -axprobe = { git = "https://github.com/Starry-OS/axprobe" } + +[target.'cfg(not(target_arch = "aarch64"))'.dependencies] +kprobe = { git = "https://github.com/Starry-OS/kprobe", rev = "318cbb2", optional = true } [target.'cfg(target_arch = "x86_64")'.dependencies] lazyinit = "0.2" diff --git a/src/loongarch64/context.rs b/src/loongarch64/context.rs index 3330904..ff1248c 100644 --- a/src/loongarch64/context.rs +++ b/src/loongarch64/context.rs @@ -1,8 +1,6 @@ use core::arch::naked_asm; #[cfg(feature = "fp-simd")] use core::mem::offset_of; - -use axprobe::PtRegs; use memory_addr::VirtAddr; /// General registers of Loongarch64. @@ -356,39 +354,43 @@ unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: ) } -impl TrapFrame { - pub fn to_pt_regs(&mut self) -> axprobe::PtRegs { +#[cfg(feature = "kprobe")] +impl From<&TrapFrame> for kprobe::PtRegs { + fn from(tf: &TrapFrame) -> Self { let regs = [0; 32]; unsafe { core::ptr::copy_nonoverlapping( - &self.regs.zero as *const usize, + &tf.regs as *const GeneralRegisters as *const usize, regs.as_ptr() as *mut usize, 32, ); } - PtRegs { + kprobe::PtRegs { regs, orig_a0: 0, - csr_era: self.era, + csr_era: tf.era, csr_badvaddr: 0, csr_crmd: 0, - csr_prmd: self.prmd, + csr_prmd: tf.prmd, csr_euen: 0, csr_ecfg: 0, csr_estat: 0, } } +} - pub fn update_from_pt_regs(&mut self, pt_regs: axprobe::PtRegs) { +#[cfg(feature = "kprobe")] +impl TrapFrame { + /// Update the TrapFrame from kprobe::PtRegs + pub fn update_from_ptregs(&mut self, ptregs: kprobe::PtRegs) { unsafe { core::ptr::copy_nonoverlapping( - pt_regs.regs.as_ptr() as *const usize, - &mut self.regs.zero as *mut usize, + ptregs.regs.as_ptr() as *const usize, + &mut self.regs as *mut GeneralRegisters as *mut usize, 32, ); } - self.era = pt_regs.csr_era; - self.prmd = pt_regs.csr_prmd; - // other csr fields are ignored + self.era = ptregs.csr_era; + self.prmd = ptregs.csr_prmd; } } diff --git a/src/loongarch64/trap.rs b/src/loongarch64/trap.rs index bfea70b..a2ea05c 100644 --- a/src/loongarch64/trap.rs +++ b/src/loongarch64/trap.rs @@ -13,11 +13,11 @@ core::arch::global_asm!( ); fn handle_breakpoint(tf: &mut TrapFrame) { + debug!("Exception(Breakpoint) @ {:#x} ", tf.era); if core::hint::likely(handle_trap!(BREAK_HANDLER, tf)) { return; } - debug!("Exception(Breakpoint) @ {:#x} ", tf.era); - tf.era += 2; // skip ebreak instruction + tf.era += 4; } fn handle_page_fault(tf: &mut TrapFrame, access_flags: PageFaultFlags) { diff --git a/src/riscv/context.rs b/src/riscv/context.rs index ff6f98f..64616ae 100644 --- a/src/riscv/context.rs +++ b/src/riscv/context.rs @@ -431,82 +431,86 @@ unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: ) } -impl TrapFrame { - pub fn to_pt_regs(&mut self) -> axprobe::PtRegs { - axprobe::PtRegs { - epc: self.sepc, - ra: self.regs.ra, - sp: self.regs.sp, - gp: self.regs.gp, - tp: self.regs.tp, - t0: self.regs.t0, - t1: self.regs.t1, - t2: self.regs.t2, - s0: self.regs.s0, - s1: self.regs.s1, - a0: self.regs.a0, - a1: self.regs.a1, - a2: self.regs.a2, - a3: self.regs.a3, - a4: self.regs.a4, - a5: self.regs.a5, - a6: self.regs.a6, - a7: self.regs.a7, - s2: self.regs.s2, - s3: self.regs.s3, - s4: self.regs.s4, - s5: self.regs.s5, - s6: self.regs.s6, - s7: self.regs.s7, - s8: self.regs.s8, - s9: self.regs.s9, - s10: self.regs.s10, - s11: self.regs.s11, - t3: self.regs.t3, - t4: self.regs.t4, - t5: self.regs.t5, - t6: self.regs.t6, - status: self.sstatus.bits(), +#[cfg(feature = "kprobe")] +impl From<&TrapFrame> for kprobe::PtRegs { + fn from(tf: &TrapFrame) -> Self { + kprobe::PtRegs { + epc: tf.sepc, + ra: tf.regs.ra, + sp: tf.regs.sp, + gp: tf.regs.gp, + tp: tf.regs.tp, + t0: tf.regs.t0, + t1: tf.regs.t1, + t2: tf.regs.t2, + s0: tf.regs.s0, + s1: tf.regs.s1, + a0: tf.regs.a0, + a1: tf.regs.a1, + a2: tf.regs.a2, + a3: tf.regs.a3, + a4: tf.regs.a4, + a5: tf.regs.a5, + a6: tf.regs.a6, + a7: tf.regs.a7, + s2: tf.regs.s2, + s3: tf.regs.s3, + s4: tf.regs.s4, + s5: tf.regs.s5, + s6: tf.regs.s6, + s7: tf.regs.s7, + s8: tf.regs.s8, + s9: tf.regs.s9, + s10: tf.regs.s10, + s11: tf.regs.s11, + t3: tf.regs.t3, + t4: tf.regs.t4, + t5: tf.regs.t5, + t6: tf.regs.t6, + status: tf.sstatus.bits(), // todo : other fields badaddr: 0, cause: 0, orig_a0: 0, } } +} - pub fn update_from_pt_regs(&mut self, pt_regs: axprobe::PtRegs) { - self.sepc = pt_regs.epc; - self.regs.ra = pt_regs.ra; - self.regs.sp = pt_regs.sp; - self.regs.gp = pt_regs.gp; - self.regs.tp = pt_regs.tp; - self.regs.t0 = pt_regs.t0; - self.regs.t1 = pt_regs.t1; - self.regs.t2 = pt_regs.t2; - self.regs.s0 = pt_regs.s0; - self.regs.s1 = pt_regs.s1; - self.regs.a0 = pt_regs.a0; - self.regs.a1 = pt_regs.a1; - self.regs.a2 = pt_regs.a2; - self.regs.a3 = pt_regs.a3; - self.regs.a4 = pt_regs.a4; - self.regs.a5 = pt_regs.a5; - self.regs.a6 = pt_regs.a6; - self.regs.a7 = pt_regs.a7; - self.regs.s2 = pt_regs.s2; - self.regs.s3 = pt_regs.s3; - self.regs.s4 = pt_regs.s4; - self.regs.s5 = pt_regs.s5; - self.regs.s6 = pt_regs.s6; - self.regs.s7 = pt_regs.s7; - self.regs.s8 = pt_regs.s8; - self.regs.s9 = pt_regs.s9; - self.regs.s10 = pt_regs.s10; - self.regs.s11 = pt_regs.s11; - self.regs.t3 = pt_regs.t3; - self.regs.t4 = pt_regs.t4; - self.regs.t5 = pt_regs.t5; - self.regs.t6 = pt_regs.t6; - // todo : other fields +impl TrapFrame { + /// Update the TrapFrame from kprobe::PtRegs + pub fn update_from_ptregs(&mut self, ptregs: kprobe::PtRegs) { + self.sepc = ptregs.epc; + self.regs.ra = ptregs.ra; + self.regs.sp = ptregs.sp; + self.regs.gp = ptregs.gp; + self.regs.tp = ptregs.tp; + self.regs.t0 = ptregs.t0; + self.regs.t1 = ptregs.t1; + self.regs.t2 = ptregs.t2; + self.regs.s0 = ptregs.s0; + self.regs.s1 = ptregs.s1; + self.regs.a0 = ptregs.a0; + self.regs.a1 = ptregs.a1; + self.regs.a2 = ptregs.a2; + self.regs.a3 = ptregs.a3; + self.regs.a4 = ptregs.a4; + self.regs.a5 = ptregs.a5; + self.regs.a6 = ptregs.a6; + self.regs.a7 = ptregs.a7; + self.regs.s2 = ptregs.s2; + self.regs.s3 = ptregs.s3; + self.regs.s4 = ptregs.s4; + self.regs.s5 = ptregs.s5; + self.regs.s6 = ptregs.s6; + self.regs.s7 = ptregs.s7; + self.regs.s8 = ptregs.s8; + self.regs.s9 = ptregs.s9; + self.regs.s10 = ptregs.s10; + self.regs.s11 = ptregs.s11; + self.regs.t3 = ptregs.t3; + self.regs.t4 = ptregs.t4; + self.regs.t5 = ptregs.t5; + self.regs.t6 = ptregs.t6; + self.sstatus = sstatus::Sstatus::from_bits(ptregs.status); } } diff --git a/src/riscv/trap.rs b/src/riscv/trap.rs index 4269895..6bcf14c 100644 --- a/src/riscv/trap.rs +++ b/src/riscv/trap.rs @@ -18,10 +18,10 @@ core::arch::global_asm!( ); fn handle_breakpoint(tf: &mut TrapFrame) { + debug!("Exception(Breakpoint) @ {:#x} ", tf.sepc); if core::hint::likely(handle_trap!(BREAK_HANDLER, tf)) { return; } - debug!("Exception(Breakpoint) @ {:#x} ", tf.sepc); tf.sepc += 2; // skip ebreak instruction } diff --git a/src/x86_64/context.rs b/src/x86_64/context.rs index 7fe0e5d..ec8b470 100644 --- a/src/x86_64/context.rs +++ b/src/x86_64/context.rs @@ -350,3 +350,59 @@ unsafe extern "C" fn context_switch(_current_stack: &mut u64, _next_stack: &u64) ret", ) } +#[cfg(feature = "kprobe")] +impl From<&TrapFrame> for kprobe::PtRegs { + fn from(value: &TrapFrame) -> Self { + kprobe::PtRegs { + r15: value.r15 as _, + r14: value.r14 as _, + r13: value.r13 as _, + r12: value.r12 as _, + rbp: value.rbp as _, + rbx: value.rbx as _, + r11: value.r11 as _, + r10: value.r10 as _, + r9: value.r9 as _, + r8: value.r8 as _, + rax: value.rax as _, + rcx: value.rcx as _, + rdx: value.rdx as _, + rsi: value.rsi as _, + rdi: value.rdi as _, + orig_rax: value.vector as _, + rip: value.rip as _, + cs: value.cs as _, + rsp: value.rsp as _, + ss: value.ss as _, + rflags: value.rflags as _, + } + } +} + +#[cfg(feature = "kprobe")] +impl TrapFrame { + /// Update the TrapFrame from kprobe::PtRegs + pub fn update_from_ptregs(&mut self, ptregs: kprobe::PtRegs) { + self.r15 = ptregs.r15 as _; + self.r14 = ptregs.r14 as _; + self.r13 = ptregs.r13 as _; + self.r12 = ptregs.r12 as _; + self.rbp = ptregs.rbp as _; + self.rbx = ptregs.rbx as _; + self.r11 = ptregs.r11 as _; + self.r10 = ptregs.r10 as _; + self.r9 = ptregs.r9 as _; + self.r8 = ptregs.r8 as _; + self.rax = ptregs.rax as _; + self.rcx = ptregs.rcx as _; + self.rdx = ptregs.rdx as _; + self.rsi = ptregs.rsi as _; + self.rdi = ptregs.rdi as _; + self.vector = ptregs.orig_rax as _; + self.rip = ptregs.rip as _; + self.cs = ptregs.cs as _; + self.rflags = ptregs.rflags as _; + self.rsp = ptregs.rsp as _; + self.ss = ptregs.ss as _; + } +} diff --git a/src/x86_64/trap.rs b/src/x86_64/trap.rs index 81f401e..14a4891 100644 --- a/src/x86_64/trap.rs +++ b/src/x86_64/trap.rs @@ -39,11 +39,18 @@ fn handle_page_fault(tf: &mut TrapFrame) { ); } +fn handle_breakpoint(tf: &mut TrapFrame) { + debug!("#BP @ {:#x} ", tf.rip); + if core::hint::likely(handle_trap!(BREAK_HANDLER, tf)) { + return; + } +} + #[unsafe(no_mangle)] fn x86_trap_handler(tf: &mut TrapFrame) { match tf.vector as u8 { PAGE_FAULT_VECTOR => handle_page_fault(tf), - BREAKPOINT_VECTOR => debug!("#BP @ {:#x} ", tf.rip), + BREAKPOINT_VECTOR => handle_breakpoint(tf), GENERAL_PROTECTION_FAULT_VECTOR => { panic!( "#GP @ {:#x}, error_code={:#x}:\n{:#x?}\n{}", From c28827efdc1a8ea5f3ea4b0a4c9cf17252616064 Mon Sep 17 00:00:00 2001 From: Godones Date: Mon, 6 Oct 2025 21:24:55 +0800 Subject: [PATCH 3/7] fix: add #![feature(likely_unlikely)] Signed-off-by: Godones --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index e612e65..cbf281d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(likely_unlikely)] #![cfg_attr(not(test), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] #![feature(cold_path)] From 1b026eeaf3544e83fd2b8c8bcd30503ba5cde4c6 Mon Sep 17 00:00:00 2001 From: Godones Date: Wed, 29 Oct 2025 21:22:50 +0800 Subject: [PATCH 4/7] update kprobe dep Signed-off-by: Godones --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 388c347..3c2a9b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,7 +90,7 @@ checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" [[package]] name = "kprobe" version = "0.1.0" -source = "git+https://github.com/Starry-OS/kprobe?rev=318cbb2#318cbb242ec8285903a909433fdf8da54554b324" +source = "git+https://github.com/Starry-OS/kprobe#318cbb242ec8285903a909433fdf8da54554b324" dependencies = [ "cfg-if", "lock_api", diff --git a/Cargo.toml b/Cargo.toml index ae35618..aa36ff6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ page_table_entry = "0.5" static_assertions = "1.1.0" [target.'cfg(not(target_arch = "aarch64"))'.dependencies] -kprobe = { git = "https://github.com/Starry-OS/kprobe", rev = "318cbb2", optional = true } +kprobe = { git = "https://github.com/Starry-OS/kprobe", optional = true } [target.'cfg(target_arch = "x86_64")'.dependencies] lazyinit = "0.2" From 0c32b2f5896d08cc52680578e202abd787979c0c Mon Sep 17 00:00:00 2001 From: Godones Date: Sat, 8 Nov 2025 19:32:32 +0800 Subject: [PATCH 5/7] add debug handler for x86_64 Signed-off-by: Godones --- src/trap.rs | 4 ++++ src/x86_64/trap.rs | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/trap.rs b/src/trap.rs index 12bf0c9..72ca9be 100644 --- a/src/trap.rs +++ b/src/trap.rs @@ -19,6 +19,10 @@ pub static PAGE_FAULT: [fn(VirtAddr, PageFaultFlags) -> bool]; #[def_trap_handler] pub static BREAK_HANDLER: [fn(&mut TrapFrame) -> bool]; +/// A slice of debug handler functions. +#[def_trap_handler] +pub static DEBUG_HANDLER: [fn(&mut TrapFrame) -> bool]; + #[allow(unused_macros)] macro_rules! handle_trap { ($trap:ident, $($args:tt)*) => {{ diff --git a/src/x86_64/trap.rs b/src/x86_64/trap.rs index 14a4891..cf38c8c 100644 --- a/src/x86_64/trap.rs +++ b/src/x86_64/trap.rs @@ -46,10 +46,18 @@ fn handle_breakpoint(tf: &mut TrapFrame) { } } +fn handle_debug(tf: &mut TrapFrame) { + debug!("#DB @ {:#x} ", tf.rip); + if core::hint::likely(handle_trap!(DEBUG_HANDLER, tf)) { + return; + } +} + #[unsafe(no_mangle)] fn x86_trap_handler(tf: &mut TrapFrame) { match tf.vector as u8 { PAGE_FAULT_VECTOR => handle_page_fault(tf), + DEBUG_VECTOR => handle_debug(tf), BREAKPOINT_VECTOR => handle_breakpoint(tf), GENERAL_PROTECTION_FAULT_VECTOR => { panic!( From cc00dd0a1670c285bfb724955902970104831b86 Mon Sep 17 00:00:00 2001 From: Godones Date: Mon, 10 Nov 2025 19:49:51 +0800 Subject: [PATCH 6/7] Add kprobe support to aarch64 arch and extend break handler parameter. Signed-off-by: Godones --- Cargo.lock | 2 +- Cargo.toml | 2 -- src/aarch64/context.rs | 28 ++++++++++++++++++++++++++++ src/aarch64/trap.rs | 13 +++++++++---- src/loongarch64/trap.rs | 2 +- src/riscv/trap.rs | 2 +- src/trap.rs | 2 +- src/x86_64/trap.rs | 2 +- 8 files changed, 42 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c2a9b1..1a6f296 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,7 +90,7 @@ checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" [[package]] name = "kprobe" version = "0.1.0" -source = "git+https://github.com/Starry-OS/kprobe#318cbb242ec8285903a909433fdf8da54554b324" +source = "git+https://github.com/Starry-OS/kprobe#c5e419f4d4171d0ab0bc00ff5423e4c39b5a4fef" dependencies = [ "cfg-if", "lock_api", diff --git a/Cargo.toml b/Cargo.toml index aa36ff6..19b4519 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,8 +37,6 @@ cfg-if = "1.0" memory_addr = "0.4" page_table_entry = "0.5" static_assertions = "1.1.0" - -[target.'cfg(not(target_arch = "aarch64"))'.dependencies] kprobe = { git = "https://github.com/Starry-OS/kprobe", optional = true } [target.'cfg(target_arch = "x86_64")'.dependencies] diff --git a/src/aarch64/context.rs b/src/aarch64/context.rs index 22965c1..662e971 100644 --- a/src/aarch64/context.rs +++ b/src/aarch64/context.rs @@ -31,6 +31,34 @@ impl fmt::Debug for TrapFrame { } } +#[cfg(feature = "kprobe")] +impl From<&TrapFrame> for kprobe::PtRegs { + fn from(tf: &TrapFrame) -> Self { + use core::u64; + + use kprobe::PtRegs; + PtRegs { + regs: tf.x, + sp: u64::MAX, + pc: tf.elr, + pstate: tf.spsr, + orig_x0: u64::MAX, + syscallno: -1, + unused2: 0, + } + } +} + +#[cfg(feature = "kprobe")] +impl TrapFrame { + /// Update the TrapFrame from kprobe::PtRegs + pub fn update_from_ptregs(&mut self, ptregs: kprobe::PtRegs) { + self.x = ptregs.regs; + self.spsr = ptregs.pstate; + self.elr = ptregs.pc; + } +} + impl TrapFrame { /// Gets the 0th syscall argument. pub const fn arg0(&self) -> usize { diff --git a/src/aarch64/trap.rs b/src/aarch64/trap.rs index f2a425a..5311ab4 100644 --- a/src/aarch64/trap.rs +++ b/src/aarch64/trap.rs @@ -41,6 +41,14 @@ pub(super) fn is_valid_page_fault(iss: u64) -> bool { matches!(iss & 0b111100, 0b0100 | 0b1100) // IFSC or DFSC bits } +fn handle_breakpoint(tf: &mut TrapFrame, iss: u64) { + debug!("BRK #{:#x} @ {:#x} ", iss, tf.elr); + if core::hint::likely(handle_trap!(BREAK_HANDLER, tf, iss)) { + return; + } + tf.elr += 4; +} + fn handle_page_fault(tf: &mut TrapFrame, access_flags: PageFaultFlags) { let vaddr = va!(FAR_EL1.get() as usize); if handle_trap!(PAGE_FAULT, vaddr, access_flags) { @@ -99,10 +107,7 @@ fn aarch64_trap_handler(tf: &mut TrapFrame, kind: TrapKind, source: TrapSource) }, ); } - Some(ESR_EL1::EC::Value::Brk64) => { - debug!("BRK #{:#x} @ {:#x} ", iss, tf.elr); - tf.elr += 4; - } + Some(ESR_EL1::EC::Value::Brk64) => handle_breakpoint(tf, iss), e => { let vaddr = va!(FAR_EL1.get() as usize); panic!( diff --git a/src/loongarch64/trap.rs b/src/loongarch64/trap.rs index a2ea05c..de8bc06 100644 --- a/src/loongarch64/trap.rs +++ b/src/loongarch64/trap.rs @@ -14,7 +14,7 @@ core::arch::global_asm!( fn handle_breakpoint(tf: &mut TrapFrame) { debug!("Exception(Breakpoint) @ {:#x} ", tf.era); - if core::hint::likely(handle_trap!(BREAK_HANDLER, tf)) { + if core::hint::likely(handle_trap!(BREAK_HANDLER, tf, 0)) { return; } tf.era += 4; diff --git a/src/riscv/trap.rs b/src/riscv/trap.rs index 6bcf14c..a441982 100644 --- a/src/riscv/trap.rs +++ b/src/riscv/trap.rs @@ -19,7 +19,7 @@ core::arch::global_asm!( fn handle_breakpoint(tf: &mut TrapFrame) { debug!("Exception(Breakpoint) @ {:#x} ", tf.sepc); - if core::hint::likely(handle_trap!(BREAK_HANDLER, tf)) { + if core::hint::likely(handle_trap!(BREAK_HANDLER, tf, 0)) { return; } tf.sepc += 2; // skip ebreak instruction diff --git a/src/trap.rs b/src/trap.rs index 72ca9be..61bf56b 100644 --- a/src/trap.rs +++ b/src/trap.rs @@ -17,7 +17,7 @@ pub static PAGE_FAULT: [fn(VirtAddr, PageFaultFlags) -> bool]; /// A slice of breakpoint handler functions. #[def_trap_handler] -pub static BREAK_HANDLER: [fn(&mut TrapFrame) -> bool]; +pub static BREAK_HANDLER: [fn(&mut TrapFrame, arg:u64) -> bool]; /// A slice of debug handler functions. #[def_trap_handler] diff --git a/src/x86_64/trap.rs b/src/x86_64/trap.rs index cf38c8c..bb0cd92 100644 --- a/src/x86_64/trap.rs +++ b/src/x86_64/trap.rs @@ -41,7 +41,7 @@ fn handle_page_fault(tf: &mut TrapFrame) { fn handle_breakpoint(tf: &mut TrapFrame) { debug!("#BP @ {:#x} ", tf.rip); - if core::hint::likely(handle_trap!(BREAK_HANDLER, tf)) { + if core::hint::likely(handle_trap!(BREAK_HANDLER, tf, 0)) { return; } } From fc3154ba6499c8e458301cf51607f4d883f1f2ae Mon Sep 17 00:00:00 2001 From: Godones Date: Fri, 28 Nov 2025 12:33:17 +0800 Subject: [PATCH 7/7] add debug exception for x86_64 and fix the break exception definition error in aarch64. Signed-off-by: Godones --- Cargo.lock | 2 +- src/aarch64/uspace.rs | 2 +- src/uspace_common.rs | 3 +++ src/x86_64/uspace.rs | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a6f296..c0338d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,7 +90,7 @@ checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" [[package]] name = "kprobe" version = "0.1.0" -source = "git+https://github.com/Starry-OS/kprobe#c5e419f4d4171d0ab0bc00ff5423e4c39b5a4fef" +source = "git+https://github.com/Starry-OS/kprobe#985e06a8b66e78400dfa55d2faa3813619aa0dcf" dependencies = [ "cfg-if", "lock_api", diff --git a/src/aarch64/uspace.rs b/src/aarch64/uspace.rs index 0467810..dc04f50 100644 --- a/src/aarch64/uspace.rs +++ b/src/aarch64/uspace.rs @@ -150,7 +150,7 @@ impl ExceptionInfo { /// Returns a generalized kind of this exception. pub fn kind(&self) -> ExceptionKind { match self.esr.read_as_enum(ESR_EL1::EC) { - Some(ESR_EL1::EC::Value::BreakpointLowerEL) => ExceptionKind::Breakpoint, + Some(ESR_EL1::EC::Value::Brk64) => ExceptionKind::Breakpoint, Some(ESR_EL1::EC::Value::IllegalExecutionState) => ExceptionKind::IllegalInstruction, Some(ESR_EL1::EC::Value::PCAlignmentFault) | Some(ESR_EL1::EC::Value::SPAlignmentFault) => ExceptionKind::Misaligned, diff --git a/src/uspace_common.rs b/src/uspace_common.rs index d94a2fd..12badaf 100644 --- a/src/uspace_common.rs +++ b/src/uspace_common.rs @@ -21,6 +21,9 @@ pub enum ReturnReason { /// A generalized kind for [`ExceptionInfo`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExceptionKind { + #[cfg(target_arch = "x86_64")] + /// A debug exception. + Debug, /// A breakpoint exception. Breakpoint, /// An illegal instruction exception. diff --git a/src/x86_64/uspace.rs b/src/x86_64/uspace.rs index 3a00ffa..6eef039 100644 --- a/src/x86_64/uspace.rs +++ b/src/x86_64/uspace.rs @@ -143,6 +143,7 @@ impl ExceptionInfo { /// Returns a generalized kind of this exception. pub fn kind(&self) -> ExceptionKind { match ExceptionVector::try_from(self.vector) { + Ok(ExceptionVector::Debug) => ExceptionKind::Debug, Ok(ExceptionVector::Breakpoint) => ExceptionKind::Breakpoint, Ok(ExceptionVector::InvalidOpcode) => ExceptionKind::IllegalInstruction, _ => ExceptionKind::Other,