1use alloc::{
21 collections::BTreeMap,
22 sync::{Arc, Weak},
23 vec::Vec,
24};
25use core::{
26 fmt,
27 num::NonZeroI32,
28 sync::atomic::{AtomicI32, Ordering},
29};
30
31use ax_memory_addr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr, VirtAddrRange};
32use ax_runtime::hal::{
33 cpu::{KernelTrapFrame, UserRegisters},
34 paging::MappingFlags,
35};
36use kprobe::{
37 KprobeAuxiliaryOps, KretprobeBuilder, ProbeBuilder, ProbePointList,
38 register_kprobe as kprobe_crate_register_kprobe,
39 register_kretprobe as kprobe_crate_register_kretprobe, retprobe::RetprobeInstance,
40 unregister_kprobe as kprobe_crate_unregister_kprobe,
41 unregister_kretprobe as kprobe_crate_unregister_kretprobe,
42};
43
44use crate::{
45 StarryError, StarryResult,
46 sync::{IrqMutex, RawSpinNoIrq},
47 task::{AsThread, PidIdentity},
48};
49
50static NEXT_UPROBE_TARGET_ID: AtomicI32 = AtomicI32::new(1);
51static UPROBE_TARGETS: IrqMutex<BTreeMap<UprobeTargetId, Weak<PidIdentity>>> =
52 IrqMutex::new(BTreeMap::new());
53
54#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
56#[repr(transparent)]
57struct UprobeTargetId(NonZeroI32);
58
59impl UprobeTargetId {
60 fn allocate() -> StarryResult<Self> {
61 let id = NEXT_UPROBE_TARGET_ID.fetch_add(1, Ordering::Relaxed);
62 (id > 0)
63 .then(|| NonZeroI32::new(id).map(Self))
64 .flatten()
65 .ok_or(StarryError::NoMemory)
66 }
67
68 const fn get(self) -> i32 {
69 self.0.get()
70 }
71}
72
73pub(crate) struct UprobeTargetLease {
75 id: UprobeTargetId,
76 identity: Arc<PidIdentity>,
77}
78
79impl UprobeTargetLease {
80 pub(crate) fn register(identity: Arc<PidIdentity>) -> StarryResult<Self> {
81 let id = UprobeTargetId::allocate()?;
82 UPROBE_TARGETS.lock().insert(id, Arc::downgrade(&identity));
83 Ok(Self { id, identity })
84 }
85
86 pub(crate) const fn opaque_id(&self) -> i32 {
87 self.id.get()
88 }
89}
90
91impl fmt::Debug for UprobeTargetLease {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 f.debug_struct("UprobeTargetLease")
94 .field("id", &self.id)
95 .field("identity_id", &self.identity.id())
96 .finish()
97 }
98}
99
100impl Drop for UprobeTargetLease {
101 fn drop(&mut self) {
102 UPROBE_TARGETS.lock().remove(&self.id);
103 }
104}
105
106fn uprobe_target_task(opaque_id: i32) -> ax_task::AxTaskRef {
107 let id = NonZeroI32::new(opaque_id)
108 .map(UprobeTargetId)
109 .expect("uprobe target handle must be non-zero");
110 let identity = UPROBE_TARGETS
111 .lock()
112 .get(&id)
113 .and_then(Weak::upgrade)
114 .expect("uprobe target generation is no longer registered");
115 identity
116 .live_task()
117 .expect("uprobe target task exited while probe remained armed")
118}
119
120pub type KernelRawMutex = RawSpinNoIrq;
132
133#[derive(Debug)]
134pub struct KernelKprobeOps;
135
136impl KprobeAuxiliaryOps for KernelKprobeOps {
137 fn copy_memory(src: *const u8, dst: *mut u8, len: usize, user_pid: Option<i32>) {
138 if let Some(pid) = user_pid {
139 let task = uprobe_target_task(pid);
148 let aspace = task.as_thread().proc_data.aspace();
149 let mm = aspace.lock();
150 let pt = mm.page_table();
151 let mut copied = 0;
152 while copied < len {
153 let vaddr = VirtAddr::from(src as usize + copied);
154 let Ok((paddr, ..)) = pt.query(vaddr) else {
155 warn!(
156 "kprobe copy_memory: user addr {:#x} not mapped",
157 vaddr.as_usize()
158 );
159 return;
160 };
161 let page_off = vaddr.as_usize() & (PAGE_SIZE_4K - 1);
162 let chunk = core::cmp::min(len - copied, PAGE_SIZE_4K - page_off);
163 let kvaddr = ax_runtime::hal::mem::phys_to_virt(paddr);
164 unsafe {
165 core::ptr::copy_nonoverlapping(kvaddr.as_ptr(), dst.add(copied), chunk);
166 }
167 copied += chunk;
168 }
169 } else {
170 unsafe {
171 core::ptr::copy_nonoverlapping(src, dst, len);
172 }
173 }
174 }
175
176 fn set_writeable_for_address<F: FnOnce(*mut u8)>(
177 address: usize,
178 len: usize,
179 user_pid: Option<i32>,
180 action: F,
181 ) {
182 if let Some(pid) = user_pid {
183 let task = uprobe_target_task(pid);
192 let aspace = task.as_thread().proc_data.aspace();
193 let mm = aspace.lock();
194 let vaddr = VirtAddr::from(address);
195 let (paddr, ..) = mm
196 .page_table()
197 .query(vaddr)
198 .expect("uprobe: target address not mapped");
199 let kvaddr = ax_runtime::hal::mem::phys_to_virt(paddr);
200 action(kvaddr.as_mut_ptr());
201 ax_runtime::hal::cache::sync_kernel_text(vaddr.align_down_4k(), PAGE_SIZE_4K);
202 return;
203 }
204 let addr = VirtAddr::from(address);
205 crate::mm::patch_kernel_text(addr, len, action)
206 .expect("kprobe: set_writeable: patch kernel text failed");
207 }
208
209 fn alloc_kernel_exec_memory() -> *mut u8 {
210 let mut guard = ax_mm::kernel_aspace().lock();
211 let range = VirtAddrRange::new(guard.base(), guard.end());
212 let vaddr = guard
213 .find_free_area(guard.base(), PAGE_SIZE_4K, range)
214 .expect("kprobe: no free virtual address for exec memory");
215 guard
216 .map_alloc(
217 vaddr,
218 PAGE_SIZE_4K,
219 MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE,
220 true,
221 )
222 .expect("kprobe: map_alloc for exec memory failed");
223 vaddr.as_mut_ptr()
224 }
225
226 fn free_kernel_exec_memory(ptr: *mut u8) {
227 let vaddr = VirtAddr::from(ptr as usize);
228 let mut guard = ax_mm::kernel_aspace().lock();
229 guard
230 .unmap(vaddr, PAGE_SIZE_4K)
231 .expect("kprobe: unmap exec memory failed");
232 }
233
234 fn alloc_user_exec_memory<F: FnOnce(*mut u8)>(pid: Option<i32>, action: F) -> *mut u8 {
235 let pid = pid.expect("uprobe: alloc_user_exec_memory needs a pid");
240 let task = uprobe_target_task(pid);
241 let aspace = task.as_thread().proc_data.aspace();
242 let mut mm = aspace.lock();
243 let range = VirtAddrRange::new(mm.base(), mm.end());
244 let vaddr = mm
245 .find_free_area(mm.base(), PAGE_SIZE_4K, range, PAGE_SIZE_4K)
246 .expect("uprobe: no free user va for exec memory");
247 let backend = crate::mm::Backend::new_alloc(vaddr, PAGE_SIZE_4K, "uprobe-ols");
248 mm.map(
249 vaddr,
250 PAGE_SIZE_4K,
251 MappingFlags::READ | MappingFlags::EXECUTE | MappingFlags::USER,
252 true,
253 backend,
254 )
255 .expect("uprobe: map user exec memory failed");
256 let (paddr, ..) = mm
257 .page_table()
258 .query(vaddr)
259 .expect("uprobe: exec page not mapped after populate");
260 let kvaddr = ax_runtime::hal::mem::phys_to_virt(paddr);
261 action(kvaddr.as_mut_ptr());
262 ax_runtime::hal::cache::sync_kernel_text(vaddr, PAGE_SIZE_4K);
263 vaddr.as_mut_ptr()
264 }
265
266 fn free_user_exec_memory(pid: Option<i32>, ptr: *mut u8) {
267 let pid = pid.expect("uprobe: free_user_exec_memory needs a pid");
268 let task = uprobe_target_task(pid);
269 let aspace = task.as_thread().proc_data.aspace();
270 let mut mm = aspace.lock();
271 mm.unmap(VirtAddr::from(ptr as usize), PAGE_SIZE_4K)
272 .expect("uprobe: unmap user exec memory failed");
273 }
274
275 fn insert_kretprobe_instance_to_task(instance: RetprobeInstance) {
276 let task = ax_task::current_may_uninit();
277 if let Some(task) = task {
278 let thread = task.try_as_thread();
279 if let Some(thread) = thread {
280 let mut kretprobe_instances = thread.kretprobe_stack.lock();
281 kretprobe_instances.push(instance);
282 return;
283 }
284 }
285 let mut instances = INSTANCE.lock();
287 instances.push(instance);
288 }
289
290 fn pop_kretprobe_instance_from_task() -> RetprobeInstance {
291 let task = ax_task::current_may_uninit();
292 if let Some(task) = task {
293 let thread = task.try_as_thread();
294 if let Some(thread) = thread {
295 let mut kretprobe_instances = thread.kretprobe_stack.lock();
296 return kretprobe_instances
297 .pop()
298 .expect("kretprobe instance stack underflow");
299 }
300 }
301 let mut instances = INSTANCE.lock();
303 instances.pop().unwrap()
304 }
305}
306
307pub(crate) type KprobeManager = kprobe::ProbeManager<KernelRawMutex, KernelKprobeOps>;
308pub(crate) type KprobePointList = ProbePointList<KernelKprobeOps>;
309
310pub type KernelKprobe = kprobe::Kprobe<KernelRawMutex, KernelKprobeOps>;
313pub type KernelKretprobe = kprobe::Kretprobe<KernelRawMutex, KernelKprobeOps>;
315pub type KprobeAuxiliary = KernelKprobeOps;
317
318static KPROBE_MANAGER: KprobeManager = KprobeManager::new();
319static KPROBE_POINT_LIST: IrqMutex<KprobePointList> = IrqMutex::new(KprobePointList::new());
320static INSTANCE: IrqMutex<Vec<RetprobeInstance>> = IrqMutex::new(Vec::new());
321
322fn with_manager<F, R>(f: F) -> R
323where
324 F: FnOnce(&KprobeManager) -> R,
325{
326 f(&KPROBE_MANAGER)
327}
328
329fn with_manager_and_list<F, R>(f: F) -> R
330where
331 F: FnOnce(&KprobeManager, &mut KprobePointList) -> R,
332{
333 let mut list = KPROBE_POINT_LIST.try_lock().unwrap();
334 f(&KPROBE_MANAGER, &mut list)
335}
336
337#[inline(never)]
339pub fn register_kprobe(builder: ProbeBuilder<KernelKprobeOps>) -> Arc<KernelKprobe> {
340 with_manager_and_list(|mgr, list| {
341 kprobe_crate_register_kprobe(mgr, list, builder).expect("Failed to register kprobe")
342 })
343}
344
345#[inline(never)]
347pub fn unregister_kprobe(kprobe: Arc<KernelKprobe>) {
348 with_manager_and_list(|mgr, list| kprobe_crate_unregister_kprobe(mgr, list, kprobe));
349}
350
351#[inline(never)]
353pub fn register_kretprobe(builder: KretprobeBuilder<KernelRawMutex>) -> Arc<KernelKretprobe> {
354 with_manager_and_list(|mgr, list| {
355 kprobe_crate_register_kretprobe(mgr, list, builder).expect("Failed to register kretprobe")
356 })
357}
358
359#[inline(never)]
361pub fn unregister_kretprobe(kretprobe: Arc<KernelKretprobe>) {
362 with_manager_and_list(|mgr, list| kprobe_crate_unregister_kretprobe(mgr, list, kretprobe));
363}
364
365pub(crate) fn trapframe_to_ptregs(tf: &UserRegisters) -> kprobe::PtRegs {
366 #[cfg(target_arch = "x86_64")]
367 {
368 kprobe::PtRegs {
369 r15: tf.r15 as usize,
370 r14: tf.r14 as usize,
371 r13: tf.r13 as usize,
372 r12: tf.r12 as usize,
373 rbp: tf.rbp as usize,
374 rbx: tf.rbx as usize,
375 r11: tf.r11 as usize,
376 r10: tf.r10 as usize,
377 r9: tf.r9 as usize,
378 r8: tf.r8 as usize,
379 rax: tf.rax as usize,
380 rcx: tf.rcx as usize,
381 rdx: tf.rdx as usize,
382 rsi: tf.rsi as usize,
383 rdi: tf.rdi as usize,
384 orig_rax: tf.vector as usize,
385 rip: tf.rip as usize,
386 cs: tf.cs as usize,
387 rflags: tf.rflags as usize,
388 rsp: tf.rsp as usize,
389 ss: tf.ss as usize,
390 }
391 }
392 #[cfg(target_arch = "riscv64")]
393 {
394 kprobe::PtRegs {
395 epc: tf.sepc,
396 ra: tf.regs.ra,
397 sp: tf.regs.sp,
398 gp: tf.regs.gp,
399 tp: tf.regs.tp,
400 t0: tf.regs.t0,
401 t1: tf.regs.t1,
402 t2: tf.regs.t2,
403 s0: tf.regs.s0,
404 s1: tf.regs.s1,
405 a0: tf.regs.a0,
406 a1: tf.regs.a1,
407 a2: tf.regs.a2,
408 a3: tf.regs.a3,
409 a4: tf.regs.a4,
410 a5: tf.regs.a5,
411 a6: tf.regs.a6,
412 a7: tf.regs.a7,
413 s2: tf.regs.s2,
414 s3: tf.regs.s3,
415 s4: tf.regs.s4,
416 s5: tf.regs.s5,
417 s6: tf.regs.s6,
418 s7: tf.regs.s7,
419 s8: tf.regs.s8,
420 s9: tf.regs.s9,
421 s10: tf.regs.s10,
422 s11: tf.regs.s11,
423 t3: tf.regs.t3,
424 t4: tf.regs.t4,
425 t5: tf.regs.t5,
426 t6: tf.regs.t6,
427 status: tf.sstatus.bits(),
428 badaddr: 0,
429 cause: 0,
430 orig_a0: tf.regs.a0,
431 }
432 }
433 #[cfg(target_arch = "aarch64")]
434 {
435 kprobe::PtRegs {
436 regs: tf.x,
437 sp: 0, pc: tf.elr,
439 pstate: tf.spsr,
440 orig_x0: tf.x[0],
441 syscallno: -1,
442 unused2: 0,
443 }
444 }
445 #[cfg(target_arch = "loongarch64")]
446 {
447 kprobe::PtRegs {
448 regs: [
449 tf.regs.zero,
450 tf.regs.ra,
451 tf.regs.tp,
452 tf.regs.sp,
453 tf.regs.a0,
454 tf.regs.a1,
455 tf.regs.a2,
456 tf.regs.a3,
457 tf.regs.a4,
458 tf.regs.a5,
459 tf.regs.a6,
460 tf.regs.a7,
461 tf.regs.t0,
462 tf.regs.t1,
463 tf.regs.t2,
464 tf.regs.t3,
465 tf.regs.t4,
466 tf.regs.t5,
467 tf.regs.t6,
468 tf.regs.t7,
469 tf.regs.t8,
470 tf.regs.u0,
471 tf.regs.fp,
472 tf.regs.s0,
473 tf.regs.s1,
474 tf.regs.s2,
475 tf.regs.s3,
476 tf.regs.s4,
477 tf.regs.s5,
478 tf.regs.s6,
479 tf.regs.s7,
480 tf.regs.s8,
481 ],
482 orig_a0: 0,
483 csr_era: tf.era,
484 csr_badvaddr: 0,
485 csr_crmd: 0,
486 csr_prmd: tf.prmd,
487 csr_euen: 0,
488 csr_ecfg: 0,
489 csr_estat: 0,
490 }
491 }
492}
493
494pub(crate) fn ptregs_write_back(pt: &kprobe::PtRegs, tf: &mut UserRegisters) {
495 #[cfg(target_arch = "x86_64")]
496 {
497 tf.r15 = pt.r15 as u64;
498 tf.r14 = pt.r14 as u64;
499 tf.r13 = pt.r13 as u64;
500 tf.r12 = pt.r12 as u64;
501 tf.rbp = pt.rbp as u64;
502 tf.rbx = pt.rbx as u64;
503 tf.r11 = pt.r11 as u64;
504 tf.r10 = pt.r10 as u64;
505 tf.r9 = pt.r9 as u64;
506 tf.r8 = pt.r8 as u64;
507 tf.rax = pt.rax as u64;
508 tf.rcx = pt.rcx as u64;
509 tf.rdx = pt.rdx as u64;
510 tf.rsi = pt.rsi as u64;
511 tf.rdi = pt.rdi as u64;
512 tf.rip = pt.rip as u64;
513 tf.cs = pt.cs as u64;
514 tf.vector = pt.orig_rax as u64;
515 tf.rflags = pt.rflags as u64;
516 tf.rsp = pt.rsp as u64;
517 tf.ss = pt.ss as u64;
518 }
519 #[cfg(target_arch = "riscv64")]
520 {
521 tf.sepc = pt.epc;
522 tf.regs.ra = pt.ra;
523 tf.regs.sp = pt.sp;
524 tf.regs.gp = pt.gp;
525 tf.regs.tp = pt.tp;
526 tf.regs.t0 = pt.t0;
527 tf.regs.t1 = pt.t1;
528 tf.regs.t2 = pt.t2;
529 tf.regs.s0 = pt.s0;
530 tf.regs.s1 = pt.s1;
531 tf.regs.a0 = pt.a0;
532 tf.regs.a1 = pt.a1;
533 tf.regs.a2 = pt.a2;
534 tf.regs.a3 = pt.a3;
535 tf.regs.a4 = pt.a4;
536 tf.regs.a5 = pt.a5;
537 tf.regs.a6 = pt.a6;
538 tf.regs.a7 = pt.a7;
539 tf.regs.s2 = pt.s2;
540 tf.regs.s3 = pt.s3;
541 tf.regs.s4 = pt.s4;
542 tf.regs.s5 = pt.s5;
543 tf.regs.s6 = pt.s6;
544 tf.regs.s7 = pt.s7;
545 tf.regs.s8 = pt.s8;
546 tf.regs.s9 = pt.s9;
547 tf.regs.s10 = pt.s10;
548 tf.regs.s11 = pt.s11;
549 tf.regs.t3 = pt.t3;
550 tf.regs.t4 = pt.t4;
551 tf.regs.t5 = pt.t5;
552 tf.regs.t6 = pt.t6;
553 }
554 #[cfg(target_arch = "aarch64")]
555 {
556 tf.x = pt.regs;
557 tf.elr = pt.pc;
558 tf.spsr = pt.pstate;
559 }
560 #[cfg(target_arch = "loongarch64")]
561 {
562 tf.regs.zero = pt.regs[0];
563 tf.regs.ra = pt.regs[1];
564 tf.regs.tp = pt.regs[2];
565 tf.regs.sp = pt.regs[3];
566 tf.regs.a0 = pt.regs[4];
567 tf.regs.a1 = pt.regs[5];
568 tf.regs.a2 = pt.regs[6];
569 tf.regs.a3 = pt.regs[7];
570 tf.regs.a4 = pt.regs[8];
571 tf.regs.a5 = pt.regs[9];
572 tf.regs.a6 = pt.regs[10];
573 tf.regs.a7 = pt.regs[11];
574 tf.regs.t0 = pt.regs[12];
575 tf.regs.t1 = pt.regs[13];
576 tf.regs.t2 = pt.regs[14];
577 tf.regs.t3 = pt.regs[15];
578 tf.regs.t4 = pt.regs[16];
579 tf.regs.t5 = pt.regs[17];
580 tf.regs.t6 = pt.regs[18];
581 tf.regs.t7 = pt.regs[19];
582 tf.regs.t8 = pt.regs[20];
583 tf.regs.u0 = pt.regs[21];
584 tf.regs.fp = pt.regs[22];
585 tf.regs.s0 = pt.regs[23];
586 tf.regs.s1 = pt.regs[24];
587 tf.regs.s2 = pt.regs[25];
588 tf.regs.s3 = pt.regs[26];
589 tf.regs.s4 = pt.regs[27];
590 tf.regs.s5 = pt.regs[28];
591 tf.regs.s6 = pt.regs[29];
592 tf.regs.s7 = pt.regs[30];
593 tf.regs.s8 = pt.regs[31];
594 tf.era = pt.csr_era;
595 tf.prmd = pt.csr_prmd;
596 }
597}
598
599pub fn handle_breakpoint(tf: &mut KernelTrapFrame<'_>) -> bool {
600 let mut updated = tf.snapshot();
601 let mut pt_regs = trapframe_to_ptregs(&updated);
602 let handled = with_manager(|manager| kprobe::kprobe_handler_from_break(manager, &mut pt_regs));
603 if handled.is_some() {
604 ptregs_write_back(&pt_regs, &mut updated);
605 tf.apply_registers(&updated);
606 return true;
607 }
608 false
609}
610
611#[cfg(target_arch = "x86_64")]
612pub fn handle_debug(tf: &mut KernelTrapFrame<'_>) -> bool {
613 let mut updated = tf.snapshot();
614 let mut pt_regs = trapframe_to_ptregs(&updated);
615 let handled = with_manager(|manager| kprobe::kprobe_handler_from_debug(manager, &mut pt_regs));
616 if handled.is_some() {
617 ptregs_write_back(&pt_regs, &mut updated);
618 tf.apply_registers(&updated);
619 return true;
620 }
621 false
622}