1use qcode::value::ValueId;
21
22use crate::{
23 hook::{BlockView, Emitter, Hook, Site},
24 vm::{Interrupt, InterruptKind},
25};
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29pub struct HookId(pub u64);
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum HookAction {
34 Continue,
36 Stop,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum InsnAction {
43 Handled(Option<u128>),
46 Unhandled,
49 Stop,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub struct MemAccess {
56 pub pc: Option<u64>,
58 pub addr: u64,
59 pub size: u64,
60 pub value: Option<u64>,
63}
64
65pub const TABLE_CODES: u64 = 1 << 62;
68
69pub(crate) type CodeCallback<S> = Box<dyn FnMut(&mut crate::Vm<S>, u64) -> HookAction>;
70pub(crate) type MemCallback<S> = Box<dyn FnMut(&mut crate::Vm<S>, &MemAccess) -> HookAction>;
71pub(crate) type InsnCallback<S> = Box<dyn FnMut(&mut crate::Vm<S>, &Interrupt) -> InsnAction>;
72
73pub(crate) enum Callback<S> {
74 Code(CodeCallback<S>),
75 Mem(MemCallback<S>),
76 Insn {
78 name: Option<Box<str>>,
79 callback: InsnCallback<S>,
80 },
81}
82
83pub(crate) struct HookTable<S> {
85 next: u64,
86 pub(crate) callbacks: Vec<(HookId, Callback<S>)>,
87}
88
89impl<S> Default for HookTable<S> {
90 fn default() -> Self {
91 Self {
92 next: 0,
93 callbacks: Vec::new(),
94 }
95 }
96}
97
98impl<S> HookTable<S> {
99 pub(crate) fn register(&mut self, callback: Callback<S>) -> HookId {
100 let id = HookId(self.next);
101 self.next += 1;
102 self.callbacks.push((id, callback));
103 id
104 }
105
106 pub(crate) fn remove(&mut self, id: HookId) -> bool {
107 let before = self.callbacks.len();
108 self.callbacks.retain(|(have, _)| *have != id);
109 self.callbacks.len() != before
110 }
111
112 pub(crate) fn code(id: HookId) -> u64 {
114 TABLE_CODES + id.0
115 }
116
117 pub(crate) fn owner(interrupt: &Interrupt) -> Option<HookId> {
119 match interrupt.kind {
120 InterruptKind::Explicit { code } if code >= TABLE_CODES => {
121 Some(HookId(code - TABLE_CODES))
122 }
123 _ => None,
124 }
125 }
126}
127
128pub(crate) fn in_range(begin: u64, end: u64, addr: u64) -> bool {
131 begin > end || (begin..=end).contains(&addr)
132}
133
134pub(crate) struct CodeRangeHook {
136 pub begin: u64,
137 pub end: u64,
138 pub code: u64,
139}
140
141impl Hook for CodeRangeHook {
142 fn sites(&mut self, block: &BlockView<'_>) -> Vec<Site> {
143 block
144 .addresses()
145 .into_iter()
146 .filter(|site| {
147 matches!(site, Site::Address { address, .. } if in_range(self.begin, self.end, *address))
148 })
149 .collect()
150 }
151
152 fn instrument(&mut self, _site: &Site, emit: &mut Emitter<'_>) {
153 let address = emit.constant(emit.address().unwrap_or_default(), 8);
154 emit.interrupt(self.code, &[address]);
155 }
156}
157
158pub(crate) struct ReadWatch {
162 pub begin: u64,
163 pub end: u64,
164 pub code: u64,
165}
166
167impl Hook for ReadWatch {
168 fn sites(&mut self, block: &BlockView<'_>) -> Vec<Site> {
169 block.loads()
170 }
171
172 fn instrument(&mut self, _site: &Site, emit: &mut Emitter<'_>) {
173 let Some((ptr, size)) = emit.load_operands() else {
174 return;
175 };
176 let cond = emit.in_range(ptr, self.begin, self.end);
177 let args: Vec<ValueId> = vec![emit.zext(ptr, 8), emit.constant(size as u64, 8)];
178 emit.interrupt_if(cond, self.code, &args);
179 }
180}