Skip to main content

dynarmic_sys/
lib.rs

1extern crate alloc;
2
3use crate::ffi::{DyHook, SFHook};
4use anyhow::anyhow;
5use log::{debug, error, warn};
6use std::cell::UnsafeCell;
7use std::ffi::c_void;
8use std::marker::PhantomData;
9use std::process::exit;
10use std::ptr::null_mut;
11use std::sync::Arc;
12
13mod ffi;
14
15pub type DynarmicContext = Arc<DynarmicContextInner>;
16
17#[derive(Clone)]
18pub struct DynarmicContextInner {
19    inner_context: *mut c_void,
20}
21
22impl DynarmicContextInner {
23    pub fn destroy(&self) {
24        unsafe {
25            ffi::dynarmic_context_free(self.inner_context);
26        }
27    }
28}
29
30impl Drop for DynarmicContextInner {
31    fn drop(&mut self) {
32        self.destroy();
33    }
34}
35
36unsafe impl Send for DynarmicContextInner {}
37unsafe impl Sync for DynarmicContextInner {}
38
39pub type DynarmicContext32 = Arc<DynarmicContext32Inner>;
40
41#[derive(Clone)]
42pub struct DynarmicContext32Inner {
43    inner_context: *mut c_void,
44}
45
46impl DynarmicContext32Inner {
47    pub fn destroy(&self) {
48        unsafe {
49            ffi::dynarmic_context32_free(self.inner_context);
50        }
51    }
52}
53
54impl Drop for DynarmicContext32Inner {
55    fn drop(&mut self) {
56        self.destroy();
57    }
58}
59
60unsafe impl Send for DynarmicContext32Inner {}
61unsafe impl Sync for DynarmicContext32Inner {}
62
63/// Returns the version of the underlying Dynarmic engine.
64pub fn dynarmic_version() -> u32 {
65    unsafe { ffi::dynarmic_version() }
66}
67
68/// A little surprise from the developer.
69pub fn dynarmic_colorful_egg() -> String {
70    unsafe {
71        let c_str = ffi::dynarmic_colorful_egg();
72        if c_str.is_null() {
73            return String::new();
74        }
75        let c_str = std::ffi::CStr::from_ptr(c_str);
76        c_str.to_string_lossy().into_owned()
77    }
78}
79
80struct Metadata<'a> {
81    svc_callback: Option<Box<dyn SFHook + 'a>>,
82    unmapped_mem_callback: Option<Box<dyn SFHook + 'a>>,
83    until: u64,
84    _memory: *mut c_void,
85    _monitor: *mut c_void,
86    _page_table: *mut *mut c_void,
87    handle: *mut c_void,
88}
89
90impl Drop for Metadata<'_> {
91    fn drop(&mut self) {
92        log::info!("[dynarmic] Dropping Metadata");
93        unsafe {
94            ffi::dynarmic_destroy(self.handle);
95        }
96    }
97}
98
99unsafe impl Send for Metadata<'_> {}
100unsafe impl Sync for Metadata<'_> {}
101
102/// A high-level wrapper around the Dynarmic ARM dynamic recompiler.
103///
104/// This struct provides a safe(r) interface for memory mapping, register access,
105/// and execution control for both ARM32 and ARM64 architectures.
106#[derive(Clone)]
107pub struct Dynarmic<'a, T: Clone + Send + Sync> {
108    cur_handle: *mut c_void,
109    metadata: Arc<UnsafeCell<Metadata<'a>>>,
110    pd: PhantomData<&'a T>,
111}
112
113unsafe impl<'a, T: Clone + Send + Sync> Send for Dynarmic<'a, T> {}
114unsafe impl<'a, T: Clone + Send + Sync> Sync for Dynarmic<'a, T> {}
115
116impl<'a, T: Clone + Send + Sync> Dynarmic<'a, T> {
117    /// Creates a new Dynarmic instance for ARM64 (AArch64) emulation.
118    ///
119    /// The JIT cache size can be configured via the `DYNARMIC_JIT_SIZE` environment variable (in MB).
120    /// Defaults to 64MB. Valid range: 8MB to 512MB.
121    pub fn new() -> Dynarmic<'static, T> {
122        let memory = unsafe { ffi::dynarmic_init_memory() };
123        if memory == null_mut() {
124            error!("Failed to initialize memory");
125            exit(0)
126        }
127
128        let mut jit_size = std::env::var("DYNARMIC_JIT_SIZE")
129            .ok()
130            .and_then(|s| s.parse::<u64>().ok())
131            .unwrap_or(64);
132
133        if jit_size < 8 {
134            warn!("JIT size {}MB is too small, setting to 8MB", jit_size);
135            jit_size = 8;
136        } else if jit_size > 512 {
137            warn!("JIT size {}MB is too large, setting to 512MB", jit_size);
138            jit_size = 512;
139        }
140
141        let monitor = unsafe { ffi::dynarmic_init_monitor(1) };
142        let page_table = unsafe { ffi::dynarmic_init_page_table() };
143        let handle = unsafe {
144            ffi::dynarmic_new(0, memory, monitor, page_table, jit_size * 1024 * 1024, true)
145        };
146
147        debug!(
148            "[Dynarmic] Created new Dynarmic instance: {:X} with {}MB JIT",
149            handle as usize, jit_size
150        );
151
152        Dynarmic {
153            cur_handle: handle,
154            metadata: Arc::new(UnsafeCell::new(Metadata {
155                svc_callback: None,
156                unmapped_mem_callback: None,
157                until: 0,
158                _memory: memory,
159                _monitor: monitor,
160                _page_table: page_table,
161                handle,
162            })),
163            pd: PhantomData,
164        }
165    }
166
167    /// Creates a new Dynarmic instance for ARM32 (A32) emulation.
168    ///
169    /// The JIT cache size can be configured via the `DYNARMIC_JIT_SIZE` environment variable (in MB).
170    /// Defaults to 64MB. Valid range: 8MB to 512MB.
171    pub fn new_a32() -> Dynarmic<'static, T> {
172        let memory = unsafe { ffi::dynarmic_init_memory() };
173        if memory == null_mut() {
174            error!("Failed to initialize memory");
175            exit(0)
176        }
177
178        let mut jit_size = std::env::var("DYNARMIC_JIT_SIZE")
179            .ok()
180            .and_then(|s| s.parse::<u64>().ok())
181            .unwrap_or(64);
182
183        if jit_size < 8 {
184            warn!("JIT size {}MB is too small, setting to 8MB", jit_size);
185            jit_size = 8;
186        } else if jit_size > 512 {
187            warn!("JIT size {}MB is too large, setting to 512MB", jit_size);
188            jit_size = 512;
189        }
190
191        let monitor = unsafe { ffi::dynarmic_init_monitor(1) };
192        let page_table = unsafe { ffi::dynarmic_init_page_table() };
193        let handle = unsafe {
194            ffi::dynarmic_new_a32(
195                0,
196                memory,
197                monitor,
198                page_table,
199                jit_size * 1024 * 1024,
200                true,
201                null_mut(),
202            )
203        };
204
205        debug!(
206            "[Dynarmic] Created new Dynarmic A32 instance: {:X} with {}MB JIT",
207            handle as usize, jit_size
208        );
209
210        Dynarmic {
211            cur_handle: handle,
212            metadata: Arc::new(UnsafeCell::new(Metadata {
213                svc_callback: None,
214                unmapped_mem_callback: None,
215                until: 0,
216                _memory: memory,
217                _monitor: monitor,
218                _page_table: page_table,
219                handle,
220            })),
221            pd: PhantomData,
222        }
223    }
224
225    /// Starts emulation from the given PC until the `until` address is reached.
226    ///
227    /// * `pc`: The starting Program Counter address.
228    /// * `until`: The address at which to stop execution.
229    pub fn emu_start(&self, pc: u64, until: u64) -> anyhow::Result<()> {
230        unsafe {
231            debug!("[Dynarmic] Starting emulator: pc=0x{:x}", pc);
232
233            // saturating_add so callers can pass u64::MAX as a sentinel
234            // "no synthetic stop" without overflowing.
235            (*self.metadata.get()).until = until.saturating_add(4);
236
237            let ret = ffi::dynarmic_emu_start(self.cur_handle, pc);
238            if ret != 0 {
239                return Err(anyhow!("Failed to start emulator: code={}", ret));
240            }
241            Ok(())
242        }
243    }
244
245    /// Stops the emulation.
246    pub fn emu_stop(&self) -> anyhow::Result<()> {
247        unsafe {
248            debug!("[Dynarmic] Stopping emulator");
249            let ret = ffi::dynarmic_emu_stop(self.cur_handle);
250            if ret != 0 {
251                return Err(anyhow!("Failed to stop emulator: code={}", ret));
252            }
253            Ok(())
254        }
255    }
256
257    /// Returns the current size of the JIT code cache.
258    pub fn get_cache_size(&self) -> u64 {
259        unsafe { ffi::dynarmic_get_cache_size(self.cur_handle) }
260    }
261
262    /// Allocates a new ARM64 context.
263    pub fn context_alloc(&self) -> DynarmicContext {
264        unsafe {
265            let inner_context = ffi::dynarmic_context_alloc();
266            Arc::new(DynarmicContextInner { inner_context })
267        }
268    }
269
270    /// Saves the current ARM64 CPU state into the provided context.
271    pub fn context_save(&self, context: &mut DynarmicContext) -> anyhow::Result<()> {
272        unsafe {
273            let ret = ffi::dynarmic_context_save(self.cur_handle, context.inner_context);
274            if ret != 0 {
275                return Err(anyhow!("Failed to save context: code={}", ret));
276            }
277            Ok(())
278        }
279    }
280
281    /// Restores the ARM64 CPU state from the provided context.
282    pub fn context_restore(&self, context: &DynarmicContext) -> anyhow::Result<()> {
283        unsafe {
284            let ret = ffi::dynarmic_context_restore(self.cur_handle, context.inner_context);
285            if ret != 0 {
286                return Err(anyhow!("Failed to restore context: code={}", ret));
287            }
288            Ok(())
289        }
290    }
291
292    /// Allocates a new ARM32 context.
293    pub fn context32_alloc(&self) -> DynarmicContext32 {
294        unsafe {
295            let inner_context = ffi::dynarmic_context32_alloc();
296            Arc::new(DynarmicContext32Inner { inner_context })
297        }
298    }
299
300    /// Saves the current ARM32 CPU state into the provided context.
301    pub fn context32_save(&self, context: &mut DynarmicContext32) -> anyhow::Result<()> {
302        unsafe {
303            let ret = ffi::dynarmic_context32_save(self.cur_handle, context.inner_context);
304            if ret != 0 {
305                return Err(anyhow!("Failed to save A32 context: code={}", ret));
306            }
307            Ok(())
308        }
309    }
310
311    /// Restores the ARM32 CPU state from the provided context.
312    pub fn context32_restore(&self, context: &DynarmicContext32) -> anyhow::Result<()> {
313        unsafe {
314            let ret = ffi::dynarmic_context32_restore(self.cur_handle, context.inner_context);
315            if ret != 0 {
316                return Err(anyhow!("Failed to restore A32 context: code={}", ret));
317            }
318            Ok(())
319        }
320    }
321
322    /// Maps a region of memory for the emulator.
323    ///
324    /// * `addr`: The guest virtual address.
325    /// * `size`: The size of the region in bytes (must be page-aligned).
326    /// * `prot`: Protection flags (1: Read, 2: Write, 4: Execute).
327    pub fn mem_map(&self, addr: u64, size: usize, prot: u32) -> anyhow::Result<()> {
328        unsafe {
329            debug!(
330                "[Dynarmic] Mapping memory: addr=0x{:x}, size=0x{:x}, prot={}",
331                addr, size, prot
332            );
333            let ret =
334                ffi::dynarmic_mmap(self.cur_handle, addr, size as u64, u32::cast_signed(prot));
335            if ret == 4 {
336                warn!("Replace mmap?");
337            }
338            if ret != 0 {
339                return Err(anyhow!("Failed to map memory: code={}", ret));
340            }
341            debug!(
342                "[Dynarmic] Mapped memory: addr=0x{:x}, size=0x{:x}, prot={}",
343                addr, size, prot
344            );
345            Ok(())
346        }
347    }
348
349    /// Maps a region of memory using a host pointer.
350    pub fn mem_map_ptr(
351        &self,
352        addr: u64,
353        size: usize,
354        prot: u32,
355        ptr: *mut c_void,
356    ) -> anyhow::Result<()> {
357        unsafe {
358            debug!(
359                "[Dynarmic] Mapping memory ptr: addr=0x{:x}, size=0x{:x}, prot={}, ptr={:?}",
360                addr, size, prot, ptr
361            );
362            let ret = ffi::dynarmic_mem_map_ptr(
363                self.cur_handle,
364                addr,
365                size as u64,
366                u32::cast_signed(prot),
367                ptr,
368            );
369            if ret == 4 {
370                warn!("Replace mmap?");
371            }
372            if ret != 0 {
373                return Err(anyhow!("Failed to map memory ptr: code={}", ret));
374            }
375            debug!(
376                "[Dynarmic] Mapped memory ptr: addr=0x{:x}, size=0x{:x}, prot={}",
377                addr, size, prot
378            );
379            Ok(())
380        }
381    }
382
383    /// Unmaps a region of memory.
384    pub fn mem_unmap(&self, addr: u64, size: usize) -> anyhow::Result<()> {
385        unsafe {
386            debug!(
387                "[Dynarmic] Unmapping memory: addr=0x{:x}, size=0x{:x}",
388                addr, size
389            );
390            let ret = ffi::dynarmic_munmap(self.cur_handle, addr, size as u64);
391            if ret != 0 {
392                return Err(anyhow!("Failed to unmap memory: code={}", ret));
393            }
394            Ok(())
395        }
396    }
397
398    /// Updates protection flags for a memory region.
399    pub fn mem_protect(&self, addr: u64, size: usize, prot: u32) -> anyhow::Result<()> {
400        unsafe {
401            debug!(
402                "[Dynarmic] Protecting memory: addr=0x{:x}, size=0x{:x}, prot={}",
403                addr, size, prot
404            );
405            let ret = ffi::dynarmic_mem_protect(
406                self.cur_handle,
407                addr,
408                size as u64,
409                u32::cast_signed(prot),
410            );
411            if ret != 0 {
412                return Err(anyhow!("Failed to protect memory: code={}", ret));
413            }
414            Ok(())
415        }
416    }
417
418    /// Reads a 64-bit register value (ARM64).
419    pub fn reg_read(&self, index: usize) -> anyhow::Result<u64> {
420        unsafe { Ok(ffi::reg_read(self.cur_handle, index as u64)) }
421    }
422
423    /// Reads the Link Register (X30 in ARM64).
424    pub fn reg_read_lr(&self) -> anyhow::Result<u64> {
425        unsafe { Ok(ffi::reg_read(self.cur_handle, 30)) }
426    }
427
428    /// Reads the NZCV (flags) register.
429    pub fn reg_read_nzcv(&self) -> anyhow::Result<u64> {
430        unsafe { Ok(ffi::reg_read_nzcv(self.cur_handle)) }
431    }
432
433    /// Reads the Stack Pointer.
434    pub fn reg_read_sp(&self) -> anyhow::Result<u64> {
435        unsafe { Ok(ffi::reg_read_sp(self.cur_handle)) }
436    }
437
438    /// Reads the Thread ID Register (EL0).
439    pub fn reg_read_tpidr_el0(&self) -> anyhow::Result<u64> {
440        unsafe { Ok(ffi::reg_read_tpidr_el0(self.cur_handle)) }
441    }
442
443    /// Reads the Program Counter.
444    pub fn reg_read_pc(&self) -> anyhow::Result<u64> {
445        unsafe { Ok(ffi::reg_read_pc(self.cur_handle)) }
446    }
447
448    /// Writes the Program Counter.
449    pub fn reg_write_pc(&self, value: u64) -> anyhow::Result<()> {
450        unsafe {
451            debug!("[Dynarmic] Writing PC: value=0x{:x}", value);
452            let ret = ffi::reg_write_pc(self.cur_handle, value);
453            if ret != 0 {
454                return Err(anyhow!("Failed to write PC: code={}", ret));
455            }
456            Ok(())
457        }
458    }
459
460    /// Writes the Stack Pointer.
461    pub fn reg_write_sp(&self, value: u64) -> anyhow::Result<()> {
462        unsafe {
463            debug!("[Dynarmic] Writing SP: value=0x{:x}", value);
464            let ret = ffi::reg_write_sp(self.cur_handle, value);
465            if ret != 0 {
466                return Err(anyhow!("Failed to write SP: code={}", ret));
467            }
468            Ok(())
469        }
470    }
471
472    /// Writes the Link Register.
473    pub fn reg_write_lr(&self, value: u64) -> anyhow::Result<()> {
474        unsafe {
475            debug!("[Dynarmic] Writing LR: value=0x{:x}", value);
476            let ret = ffi::reg_write(self.cur_handle, 30, value);
477            if ret != 0 {
478                return Err(anyhow!("Failed to write LR: code={}", ret));
479            }
480            Ok(())
481        }
482    }
483
484    /// Writes the Thread ID Register (EL0).
485    pub fn reg_write_tpidr_el0(&self, value: u64) -> anyhow::Result<()> {
486        unsafe {
487            debug!("[Dynarmic] Writing TPIDR_EL0: value=0x{:x}", value);
488            let ret = ffi::reg_write_tpidr_el0(self.cur_handle, value);
489            if ret != 0 {
490                return Err(anyhow!("Failed to write TPIDR_EL0: code={}", ret));
491            }
492            Ok(())
493        }
494    }
495
496    /// Writes the Thread ID Read-Only Register (EL0). On real ARM64 this is
497    /// a separate system register from `TPIDR_EL0` (R/W) and is read by
498    /// `MRS Xn, TPIDRRO_EL0`. Prior to 0.2.1 this method incorrectly aliased
499    /// to `reg_write_tpidr_el0`, so guests reading `TPIDRRO_EL0` always saw
500    /// 0 — breaking any TLS scheme (libnx) that uses that register.
501    pub fn reg_write_tpidrr0_el0(&self, value: u64) -> anyhow::Result<()> {
502        unsafe {
503            ffi::reg_write_tpidrr0_el0(self.cur_handle, value);
504        }
505        Ok(())
506    }
507
508    /// Reads the Thread ID Read-Only Register (EL0). Returns whatever was
509    /// last written via `reg_write_tpidrr0_el0`; distinct from `TPIDR_EL0`.
510    pub fn reg_read_tpidrr0_el0(&self) -> anyhow::Result<u64> {
511        unsafe { Ok(ffi::reg_read_tpidrr0_el0(self.cur_handle)) }
512    }
513
514    /// Writes the NZCV (flags) register.
515    pub fn reg_write_nzcv(&self, value: u64) -> anyhow::Result<()> {
516        unsafe {
517            debug!("[Dynarmic] Writing NZCV: value=0x{:x}", value);
518            let ret = ffi::reg_write_nzcv(self.cur_handle, value);
519            if ret != 0 {
520                return Err(anyhow!("Failed to write NZCV: code={}", ret));
521            }
522            Ok(())
523        }
524    }
525
526    /// Writes a generic register value by index.
527    pub fn reg_write_raw(&self, index: usize, value: u64) -> anyhow::Result<()> {
528        unsafe {
529            debug!(
530                "[Dynarmic] Writing register: index={}, value=0x{:x}",
531                index, value
532            );
533            let ret = ffi::reg_write(self.cur_handle, index as u64, value);
534            if ret != 0 {
535                return Err(anyhow!("Failed to write register: code={}", ret));
536            }
537            Ok(())
538        }
539    }
540
541    /// Writes to CP15 c13 0 3 (ARM32 Thread ID).
542    pub fn reg_write_c13_c0_3(&self, value: u32) -> anyhow::Result<()> {
543        unsafe {
544            debug!("[Dynarmic] Writing CP15 c13 0 3: value=0x{:x}", value);
545            let ret = ffi::reg_write_c13_c0_3(self.cur_handle, value);
546            if ret != 0 {
547                return Err(anyhow!("Failed to write CP15 c13 0 3: code={}", ret));
548            }
549            Ok(())
550        }
551    }
552
553    /// Reads from CP15 c13 0 3 (ARM32 Thread ID).
554    pub fn reg_read_c13_c0_3(&self) -> anyhow::Result<u32> {
555        unsafe { Ok(ffi::reg_read_c13_c0_3(self.cur_handle)) }
556    }
557
558    /// Writes an ARM32 general-purpose register.
559    pub fn reg_write_r(&self, index: u32, value: u32) -> anyhow::Result<()> {
560        unsafe {
561            let ret = ffi::reg_write_r(self.cur_handle, index, value);
562            if ret != 0 {
563                return Err(anyhow!("Failed to write R register: code={}", ret));
564            }
565            Ok(())
566        }
567    }
568
569    /// Reads an ARM32 general-purpose register.
570    pub fn reg_read_r(&self, index: u32) -> anyhow::Result<u32> {
571        unsafe { Ok(ffi::reg_read_r(self.cur_handle, index)) }
572    }
573
574    /// Writes the CPSR register (ARM32).
575    pub fn reg_write_cpsr(&self, value: u32) -> anyhow::Result<()> {
576        unsafe {
577            let ret = ffi::reg_write_cpsr(self.cur_handle, value);
578            if ret != 0 {
579                return Err(anyhow!("Failed to write CPSR: code={}", ret));
580            }
581            Ok(())
582        }
583    }
584
585    /// Reads the CPSR register (ARM32).
586    pub fn reg_read_cpsr(&self) -> anyhow::Result<u32> {
587        unsafe { Ok(ffi::reg_read_cpsr(self.cur_handle)) }
588    }
589
590    /// Reads a null-terminated C string from guest memory.
591    pub fn mem_read_c_string(&self, mut addr: u64) -> anyhow::Result<String> {
592        let mut buf = Vec::new();
593        let mut byte = [0u8];
594        loop {
595            self.mem_read(addr, &mut byte)?;
596            if byte[0] == 0 {
597                break;
598            }
599            buf.push(byte[0]);
600            addr += 1;
601        }
602        unsafe { Ok(String::from_utf8_unchecked(buf)) }
603    }
604
605    /// Reads guest memory into a mutable buffer.
606    pub fn mem_read(&self, addr: u64, dest: &mut [u8]) -> anyhow::Result<()> {
607        unsafe {
608            debug!(
609                "[Dynarmic] Reading memory: addr=0x{:x}, size=0x{:x}",
610                addr,
611                dest.len()
612            );
613            let ret = ffi::dynarmic_mem_read(
614                self.cur_handle,
615                addr,
616                dest.as_mut_ptr() as *mut _,
617                dest.len(),
618            );
619            if ret != 0 {
620                return Err(anyhow!("Failed to read memory: code={}", ret));
621            }
622            Ok(())
623        }
624    }
625
626    /// Reads guest memory and returns it as a Vec<u8>.
627    pub fn mem_read_as_vec(&self, addr: u64, size: usize) -> anyhow::Result<Vec<u8>> {
628        let mut buf = vec![0; size];
629        self.mem_read(addr, &mut buf)?;
630        Ok(buf)
631    }
632
633    /// Writes a buffer into guest memory.
634    pub fn mem_write(&self, addr: u64, value: &[u8]) -> anyhow::Result<()> {
635        unsafe {
636            debug!(
637                "[Dynarmic] Writing memory: addr=0x{:x}, size=0x{:x}",
638                addr,
639                value.len()
640            );
641            let ret = ffi::dynarmic_mem_write(
642                self.cur_handle,
643                addr,
644                value.as_ptr() as *const _,
645                value.len(),
646            );
647            if ret != 0 {
648                return Err(anyhow!("Failed to write memory: code={}", ret));
649            }
650            Ok(())
651        }
652    }
653
654    /// Sets a callback for SVC (Supervisor Call) instructions.
655    ///
656    /// The callback receives the emulator instance, the SWI number, the end address, and the current PC.
657    pub fn set_svc_callback<F: 'a>(&self, callback: F)
658    where
659        F: FnMut(&Dynarmic<T>, u32, u64, u64) + Send + Sync,
660    {
661        debug!("[Dynarmic] Setting SVC callback");
662        unsafe {
663            let mut cb = Box::new(DyHook {
664                callback,
665                dy: self.clone(),
666            });
667            let user_data = cb.as_mut() as *mut _ as *const c_void;
668
669            extern "C" fn svc_callback_wrapper<T: Clone + Send + Sync, F>(swi: u32, user_data: *const c_void)
670            where
671                F: FnMut(&Dynarmic<T>, u32, u64, u64) + Send + Sync,
672            {
673                if swi == 114514 {
674                    return;
675                }
676                unsafe {
677                    let cb = &mut *(user_data as *mut DyHook<T, F>);
678                    let dynarmic = &cb.dy;
679                    let pc = ffi::reg_read_pc(dynarmic.cur_handle);
680                    let until = (*dynarmic.metadata.get()).until;
681                    (cb.callback)(dynarmic, swi, until, pc);
682                }
683            }
684
685            ffi::dynarmic_set_svc_callback(
686                self.cur_handle,
687                svc_callback_wrapper::<T, F>,
688                user_data,
689            );
690            (*self.metadata.get()).svc_callback = Some(cb);
691        }
692    }
693
694    /// Sets a callback for unmapped memory accesses.
695    ///
696    /// The callback should return `true` if the access was handled, `false` otherwise.
697    pub fn set_unmapped_mem_callback<F: 'a>(&self, callback: F)
698    where
699        F: FnMut(&Dynarmic<T>, u64, usize, u64) -> bool + Send + Sync,
700    {
701        debug!("[Dynarmic] Setting unmapped memory callback");
702        unsafe {
703            let mut cb = Box::new(DyHook {
704                callback,
705                dy: self.clone(),
706            });
707            let user_data = cb.as_mut() as *mut _ as *const c_void;
708
709            extern "C" fn unmapped_mem_callback_wrapper<T: Clone + Send + Sync, F>(
710                addr: u64,
711                size: usize,
712                value: u64,
713                user_data: *const c_void,
714            ) -> bool
715            where
716                F: FnMut(&Dynarmic<T>, u64, usize, u64) -> bool + Send + Sync,
717            {
718                unsafe {
719                    let cb = &mut *(user_data as *mut DyHook<T, F>);
720                    let dynarmic = &cb.dy;
721                    (cb.callback)(dynarmic, addr, size, value)
722                }
723            }
724
725            ffi::dynarmic_set_unmapped_mem_callback(
726                self.cur_handle,
727                unmapped_mem_callback_wrapper::<T, F>,
728                user_data,
729            );
730            (*self.metadata.get()).unmapped_mem_callback = Some(cb);
731        }
732    }
733
734    /// Clears and removes all active callbacks.
735    pub fn destroy_callback(&self) {
736        unsafe {
737            extern "C" fn empty_svc_callback(_: u32, _: *const c_void) {}
738            extern "C" fn empty_unmapped_callback(
739                _: u64,
740                _: usize,
741                _: u64,
742                _: *const c_void,
743            ) -> bool {
744                false
745            }
746
747            ffi::dynarmic_set_svc_callback(self.cur_handle, empty_svc_callback, null_mut());
748            let callback = (*self.metadata.get()).svc_callback.take();
749            drop(callback);
750
751            ffi::dynarmic_set_unmapped_mem_callback(
752                self.cur_handle,
753                empty_unmapped_callback,
754                null_mut(),
755            );
756            let callback = (*self.metadata.get()).unmapped_mem_callback.take();
757            drop(callback);
758        }
759    }
760}