xref: /DragonOS/kernel/crates/kprobe/src/arch/rv64/mod.rs (revision 7b0ef10895108a0de5ff5ef3d2f93f40cf2e33a5)
1 use alloc::sync::Arc;
2 use core::{
3     arch::riscv64::sfence_vma_all,
4     fmt::Debug,
5     ops::{Deref, DerefMut},
6 };
7 
8 use crate::{KprobeBasic, KprobeBuilder, KprobeOps};
9 const EBREAK_INST: u32 = 0x00100073; // ebreak
10 const C_EBREAK_INST: u32 = 0x9002; // c.ebreak
11 const INSN_LENGTH_MASK: u16 = 0x3;
12 const INSN_LENGTH_32: u16 = 0x3;
13 
14 #[derive(Debug)]
15 pub struct Kprobe {
16     basic: KprobeBasic,
17     point: Arc<Rv64KprobePoint>,
18 }
19 
20 #[derive(Debug)]
21 enum OpcodeTy {
22     Inst16(u16),
23     Inst32(u32),
24 }
25 #[derive(Debug)]
26 pub struct Rv64KprobePoint {
27     addr: usize,
28     old_instruction: OpcodeTy,
29     inst_tmp: [u8; 8],
30 }
31 
32 impl Deref for Kprobe {
33     type Target = KprobeBasic;
34 
35     fn deref(&self) -> &Self::Target {
36         &self.basic
37     }
38 }
39 
40 impl DerefMut for Kprobe {
41     fn deref_mut(&mut self) -> &mut Self::Target {
42         &mut self.basic
43     }
44 }
45 
46 impl Kprobe {
47     pub fn probe_point(&self) -> &Arc<Rv64KprobePoint> {
48         &self.point
49     }
50 }
51 
52 impl Drop for Rv64KprobePoint {
53     fn drop(&mut self) {
54         let address = self.addr;
55         match self.old_instruction {
56             OpcodeTy::Inst16(inst_16) => unsafe {
57                 core::ptr::write(address as *mut u16, inst_16);
58             },
59             OpcodeTy::Inst32(inst_32) => unsafe {
60                 core::ptr::write(address as *mut u32, inst_32);
61             },
62         }
63         unsafe {
64             sfence_vma_all();
65         }
66         log::trace!(
67             "Kprobe::uninstall: address: {:#x}, old_instruction: {:?}",
68             address,
69             self.old_instruction
70         );
71     }
72 }
73 
74 impl KprobeBuilder {
75     pub fn install(self) -> (Kprobe, Arc<Rv64KprobePoint>) {
76         let probe_point = match &self.probe_point {
77             Some(point) => point.clone(),
78             None => self.replace_inst(),
79         };
80         let kprobe = Kprobe {
81             basic: KprobeBasic::from(self),
82             point: probe_point.clone(),
83         };
84         (kprobe, probe_point)
85     }
86     /// # 安装kprobe
87     ///
88     /// 不同的架构下需要保存原指令,然后替换为断点指令
89     fn replace_inst(&self) -> Arc<Rv64KprobePoint> {
90         let address = self.symbol_addr + self.offset;
91         let inst_16 = unsafe { core::ptr::read(address as *const u16) };
92         // See https://elixir.bootlin.com/linux/v6.10.2/source/arch/riscv/kernel/probes/kprobes.c#L68
93         let is_inst_16 = if (inst_16 & INSN_LENGTH_MASK) == INSN_LENGTH_32 {
94             false
95         } else {
96             true
97         };
98         let mut point = Rv64KprobePoint {
99             old_instruction: OpcodeTy::Inst16(0),
100             inst_tmp: [0; 8],
101             addr: address,
102         };
103         let inst_tmp_ptr = point.inst_tmp.as_ptr() as usize;
104         if is_inst_16 {
105             point.old_instruction = OpcodeTy::Inst16(inst_16);
106             unsafe {
107                 core::ptr::write(address as *mut u16, C_EBREAK_INST as u16);
108                 // inst_16 :0-16
109                 // c.ebreak:16-32
110                 core::ptr::write(inst_tmp_ptr as *mut u16, inst_16);
111                 core::ptr::write((inst_tmp_ptr + 2) as *mut u16, C_EBREAK_INST as u16);
112             }
113         } else {
114             let inst_32 = unsafe { core::ptr::read(address as *const u32) };
115             point.old_instruction = OpcodeTy::Inst32(inst_32);
116             unsafe {
117                 core::ptr::write(address as *mut u32, EBREAK_INST);
118                 // inst_32 :0-32
119                 // ebreak  :32-64
120                 core::ptr::write(inst_tmp_ptr as *mut u32, inst_32);
121                 core::ptr::write((inst_tmp_ptr + 4) as *mut u32, EBREAK_INST);
122             }
123         }
124         unsafe {
125             sfence_vma_all();
126         }
127         log::trace!(
128             "Kprobe::install: address: {:#x}, func_name: {:?}, opcode: {:x?}",
129             address,
130             self.symbol,
131             point.old_instruction
132         );
133         Arc::new(point)
134     }
135 }
136 
137 impl KprobeOps for Rv64KprobePoint {
138     fn return_address(&self) -> usize {
139         let address = self.addr;
140         match self.old_instruction {
141             OpcodeTy::Inst16(_) => address + 2,
142             OpcodeTy::Inst32(_) => address + 4,
143         }
144     }
145     fn single_step_address(&self) -> usize {
146         self.inst_tmp.as_ptr() as usize
147     }
148     fn debug_address(&self) -> usize {
149         match self.old_instruction {
150             OpcodeTy::Inst16(_) => self.inst_tmp.as_ptr() as usize + 2,
151             OpcodeTy::Inst32(_) => self.inst_tmp.as_ptr() as usize + 4,
152         }
153     }
154     fn break_address(&self) -> usize {
155         self.addr
156     }
157 }
158