1#![deny(missing_docs)]
74#![cfg_attr(all(unix, feature = "unix-weak-link"), feature(linkage))]
75#![cfg_attr(feature = "num-traits", feature(generic_const_exprs))]
76
77#[cfg(all(unix, feature = "unix-weak-link"))]
78mod unix_weak_link;
79
80#[cfg(windows)]
81mod win_link_hook;
82
83use crate::error::{Error, Result};
84#[cfg(feature = "num-traits")]
85use num_traits::{FromBytes, PrimInt};
86#[cfg(feature = "plugin-api-v3")]
87use qemu_plugin_sys::qemu_plugin_cond;
88use qemu_plugin_sys::{
89 qemu_plugin_cb_flags, qemu_plugin_hwaddr, qemu_plugin_id_t, qemu_plugin_insn,
90 qemu_plugin_mem_rw, qemu_plugin_meminfo_t, qemu_plugin_op, qemu_plugin_simple_cb_t,
91 qemu_plugin_tb, qemu_plugin_vcpu_simple_cb_t, qemu_plugin_vcpu_syscall_cb_t,
92 qemu_plugin_vcpu_syscall_ret_cb_t, qemu_plugin_vcpu_tb_trans_cb_t,
93};
94#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
95use qemu_plugin_sys::{
96 qemu_plugin_read_register, qemu_plugin_reg_descriptor, qemu_plugin_register,
97 qemu_plugin_scoreboard, qemu_plugin_u64, GArray, GByteArray,
98};
99use std::{
100 ffi::{c_uint, c_void, CStr, CString},
101 marker::PhantomData,
102 path::PathBuf,
103 sync::{Mutex, OnceLock},
104};
105#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
106use std::{
107 fmt::{Debug, Formatter},
108 mem::MaybeUninit,
109};
110
111pub mod error;
112pub mod install;
113pub mod plugin;
114pub mod sys;
115pub mod version;
116
117#[cfg(not(windows))]
118extern "C" {
119 fn g_free(mem: *mut c_void);
121}
122
123#[cfg(all(
124 not(windows),
125 any(feature = "plugin-api-v2", feature = "plugin-api-v3")
126))]
127extern "C" {
128 fn g_byte_array_new() -> *mut GByteArray;
130 fn g_byte_array_free(array: *mut GByteArray, free_segment: bool) -> *mut u8;
132 fn g_array_free(array: *mut GArray, free_segment: bool) -> *mut u8;
134}
135
136#[cfg(windows)]
137lazy_static::lazy_static! {
138 static ref G_FREE : libloading::os::windows::Symbol<unsafe extern fn(*mut c_void)> = {
139 let lib =
140 libloading::os::windows::Library::open_already_loaded("libglib-2.0-0.dll")
141 .expect("libglib-2.0-0.dll should already be loaded");
142 unsafe{lib.get(b"g_free").expect("find g_free")}
146 };
147}
148
149#[cfg(all(windows, any(feature = "plugin-api-v2", feature = "plugin-api-v3")))]
150lazy_static::lazy_static! {
151 static ref G_BYTE_ARRAY_NEW: libloading::os::windows::Symbol<unsafe extern fn() -> *mut GByteArray> = {
152 let lib =
153 libloading::os::windows::Library::open_already_loaded("libglib-2.0-0.dll")
154 .expect("libglib-2.0-0.dll should already be loaded");
155 unsafe{lib.get(b"g_byte_array_new").expect("find g_byte_array_new")}
159 };
160}
161
162#[cfg(all(windows, any(feature = "plugin-api-v2", feature = "plugin-api-v3")))]
163lazy_static::lazy_static! {
164 static ref G_BYTE_ARRAY_FREE: libloading::os::windows::Symbol<unsafe extern fn(*mut c_void, bool) -> *mut u8> = {
165 let lib =
166 libloading::os::windows::Library::open_already_loaded("libglib-2.0-0.dll")
167 .expect("libglib-2.0-0.dll should already be loaded");
168 unsafe{lib.get(b"g_byte_array_free").expect("find g_byte_array_free")}
172 };
173}
174
175#[cfg(all(windows, any(feature = "plugin-api-v2", feature = "plugin-api-v3")))]
176lazy_static::lazy_static! {
177 static ref G_ARRAY_FREE: libloading::os::windows::Symbol<unsafe extern fn(*mut c_void, bool) -> *mut u8> = {
178 let lib =
179 libloading::os::windows::Library::open_already_loaded("libglib-2.0-0.dll")
180 .expect("libglib-2.0-0.dll should already be loaded");
181 unsafe{lib.get(b"g_array_free").expect("find g_array_free")}
185 };
186}
187
188#[cfg(windows)]
189unsafe fn g_free(mem: *mut c_void) {
198 unsafe { G_FREE(mem) }
199}
200
201#[cfg(all(windows, any(feature = "plugin-api-v2", feature = "plugin-api-v3")))]
202unsafe fn g_byte_array_new() -> *mut GByteArray {
210 unsafe { G_BYTE_ARRAY_NEW() }
211}
212
213#[cfg(all(windows, any(feature = "plugin-api-v2", feature = "plugin-api-v3")))]
214unsafe fn g_byte_array_free(array: *mut GByteArray, free_segment: bool) -> *mut u8 {
223 unsafe { G_BYTE_ARRAY_FREE(array as *mut c_void, free_segment) }
224}
225
226#[cfg(all(windows, any(feature = "plugin-api-v2", feature = "plugin-api-v3")))]
227unsafe fn g_array_free(array: *mut GArray, free_segment: bool) -> *mut u8 {
235 unsafe { G_ARRAY_FREE(array as *mut c_void, free_segment) }
236}
237
238pub type VCPUIndex = c_uint;
240#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
241pub type PluginU64 = qemu_plugin_u64;
245pub type CallbackFlags = qemu_plugin_cb_flags;
247pub type MemRW = qemu_plugin_mem_rw;
249#[cfg(any(feature = "plugin-api-v3"))]
250pub type PluginCondition = qemu_plugin_cond;
252pub type PluginOp = qemu_plugin_op;
254pub type PluginId = qemu_plugin_id_t;
256
257pub type VCPUInitCallback = qemu_plugin_vcpu_simple_cb_t;
264
265pub type VCPUExitCallback = qemu_plugin_vcpu_simple_cb_t;
272
273pub type VCPUIdleCallback = qemu_plugin_vcpu_simple_cb_t;
280
281pub type VCPUResumeCallback = qemu_plugin_vcpu_simple_cb_t;
288
289pub type VCPUTranslationBlockTranslationCallback = qemu_plugin_vcpu_tb_trans_cb_t;
300
301pub type FlushCallback = qemu_plugin_simple_cb_t;
307
308pub type SyscallCallback = qemu_plugin_vcpu_syscall_cb_t;
324
325pub type SyscallReturnCallback = qemu_plugin_vcpu_syscall_ret_cb_t;
334
335pub struct TranslationBlock<'a> {
342 translation_block: usize,
343 marker: PhantomData<&'a ()>,
344}
345
346impl<'a> From<*mut qemu_plugin_tb> for TranslationBlock<'a> {
347 fn from(tb: *mut qemu_plugin_tb) -> Self {
348 Self {
349 translation_block: tb as usize,
350 marker: PhantomData,
351 }
352 }
353}
354
355impl<'a> TranslationBlock<'a> {
356 pub fn size(&self) -> usize {
358 unsafe { crate::sys::qemu_plugin_tb_n_insns(self.translation_block as *mut qemu_plugin_tb) }
359 }
360
361 pub fn vaddr(&self) -> u64 {
363 unsafe { crate::sys::qemu_plugin_tb_vaddr(self.translation_block as *mut qemu_plugin_tb) }
364 }
365
366 pub fn instruction(&'a self, index: usize) -> Result<Instruction<'a>> {
373 let size = self.size();
374
375 if index >= size {
376 Err(Error::InvalidInstructionIndex { index, size })
377 } else {
378 Ok(Instruction::new(self, unsafe {
379 crate::sys::qemu_plugin_tb_get_insn(
380 self.translation_block as *mut qemu_plugin_tb,
381 index,
382 )
383 }))
384 }
385 }
386
387 pub fn instructions(&'a self) -> TranslationBlockIterator<'a> {
389 TranslationBlockIterator { tb: self, index: 0 }
390 }
391
392 pub fn register_execute_callback<F>(&self, cb: F)
394 where
395 F: FnMut(VCPUIndex) + Send + Sync + 'static,
396 {
397 self.register_execute_callback_flags(cb, CallbackFlags::QEMU_PLUGIN_CB_NO_REGS);
398 }
399
400 pub fn register_execute_callback_flags<F>(&self, cb: F, flags: CallbackFlags)
402 where
403 F: FnMut(VCPUIndex) + Send + Sync + 'static,
404 {
405 let callback = Box::new(cb);
406 let callback_box = Box::new(callback);
407 let userdata = Box::into_raw(callback_box) as *mut c_void;
408
409 unsafe {
410 crate::sys::qemu_plugin_register_vcpu_tb_exec_cb(
411 self.translation_block as *mut qemu_plugin_tb,
412 Some(handle_qemu_plugin_register_vcpu_tb_exec_cb::<F>),
413 flags,
414 userdata,
415 )
416 };
417 }
418
419 #[cfg(feature = "plugin-api-v3")]
420 pub fn register_conditional_execute_callback<F>(
423 &self,
424 cb: F,
425 cond: PluginCondition,
426 entry: PluginU64,
427 immediate: u64,
428 ) where
429 F: FnMut(VCPUIndex) + Send + Sync + 'static,
430 {
431 self.register_conditional_execute_callback_flags(
432 cb,
433 CallbackFlags::QEMU_PLUGIN_CB_NO_REGS,
434 cond,
435 entry,
436 immediate,
437 )
438 }
439
440 #[cfg(feature = "plugin-api-v3")]
441 pub fn register_conditional_execute_callback_flags<F>(
444 &self,
445 cb: F,
446 flags: CallbackFlags,
447 cond: PluginCondition,
448 entry: PluginU64,
449 immediate: u64,
450 ) where
451 F: FnMut(VCPUIndex) + Send + Sync + 'static,
452 {
453 let callback = Box::new(cb);
454 let callback_box = Box::new(callback);
455 let userdata = Box::into_raw(callback_box) as *mut c_void;
456
457 unsafe {
458 crate::sys::qemu_plugin_register_vcpu_tb_exec_cond_cb(
459 self.translation_block as *mut qemu_plugin_tb,
460 Some(handle_qemu_plugin_register_vcpu_tb_exec_cb::<F>),
461 flags,
462 cond,
463 entry,
464 immediate,
465 userdata,
466 )
467 };
468 }
469}
470
471pub struct TranslationBlockIterator<'a> {
473 tb: &'a TranslationBlock<'a>,
474 index: usize,
475}
476
477impl<'a> Iterator for TranslationBlockIterator<'a> {
478 type Item = Instruction<'a>;
479
480 fn next(&mut self) -> Option<Self::Item> {
481 let size = self.tb.size();
482
483 if self.index >= size {
484 None
485 } else {
486 let insn = self.tb.instruction(self.index).ok();
487 self.index += 1;
488 insn
489 }
490 }
491}
492
493pub struct Instruction<'a> {
500 #[allow(unused)]
501 translation_block: &'a TranslationBlock<'a>,
503 instruction: usize,
504 marker: PhantomData<&'a ()>,
505}
506
507impl<'a> Instruction<'a> {
508 fn new(translation_block: &'a TranslationBlock<'a>, insn: *mut qemu_plugin_insn) -> Self {
509 Self {
510 translation_block,
511 instruction: insn as usize,
512 marker: PhantomData,
513 }
514 }
515}
516
517impl<'a> Instruction<'a> {
518 #[cfg(any(feature = "plugin-api-v1", feature = "plugin-api-v2"))]
519 pub fn data(&self) -> Vec<u8> {
522 let size = self.size();
523 let mut data = Vec::with_capacity(size);
524
525 let insn_data =
527 unsafe { crate::sys::qemu_plugin_insn_data(self.instruction as *mut qemu_plugin_insn) }
528 as *mut u8;
529
530 unsafe {
531 data.set_len(size);
532 std::ptr::copy_nonoverlapping(insn_data, data.as_mut_ptr(), size);
533 }
534
535 data
536 }
537
538 #[cfg(feature = "plugin-api-v3")]
539 pub fn data(&self) -> Vec<u8> {
542 let size = self.size();
543 let mut data = vec![0; size];
544
545 let size = unsafe {
547 crate::sys::qemu_plugin_insn_data(
548 self.instruction as *mut qemu_plugin_insn,
549 data.as_mut_ptr() as *mut _,
550 data.len(),
551 )
552 };
553
554 data.truncate(size);
555
556 data
557 }
558
559 pub fn size(&self) -> usize {
561 unsafe { crate::sys::qemu_plugin_insn_size(self.instruction as *mut qemu_plugin_insn) }
562 }
563
564 pub fn vaddr(&self) -> u64 {
566 unsafe { crate::sys::qemu_plugin_insn_vaddr(self.instruction as *mut qemu_plugin_insn) }
567 }
568
569 pub fn haddr(&self) -> u64 {
571 (unsafe { crate::sys::qemu_plugin_insn_haddr(self.instruction as *mut qemu_plugin_insn) })
572 as usize as u64
573 }
574
575 pub fn disas(&self) -> Result<String> {
577 let disas = unsafe {
578 crate::sys::qemu_plugin_insn_disas(self.instruction as *mut qemu_plugin_insn)
579 };
580 if disas.is_null() {
581 Err(Error::NoDisassemblyString)
582 } else {
583 let disas_string = unsafe { CStr::from_ptr(disas) }.to_str()?.to_string();
584
585 unsafe { g_free(disas as *mut _) };
587
588 Ok(disas_string)
589 }
590 }
591
592 pub fn symbol(&self) -> Result<Option<String>> {
595 let symbol = unsafe {
596 crate::sys::qemu_plugin_insn_symbol(self.instruction as *mut qemu_plugin_insn)
597 };
598 if symbol.is_null() {
599 Ok(None)
600 } else {
601 let symbol_string = unsafe { CStr::from_ptr(symbol) }.to_str()?.to_string();
602 Ok(Some(symbol_string))
604 }
605 }
606
607 pub fn register_execute_callback<F>(&self, cb: F)
609 where
610 F: FnMut(VCPUIndex) + Send + Sync + 'static,
611 {
612 self.register_execute_callback_flags(cb, CallbackFlags::QEMU_PLUGIN_CB_NO_REGS)
613 }
614
615 pub fn register_execute_callback_flags<F>(&self, cb: F, flags: CallbackFlags)
617 where
618 F: FnMut(VCPUIndex) + Send + Sync + 'static,
619 {
620 let callback = Box::new(cb);
621 let callback_box = Box::new(callback);
622 let userdata = Box::into_raw(callback_box) as *mut c_void;
623
624 unsafe {
625 crate::sys::qemu_plugin_register_vcpu_insn_exec_cb(
626 self.instruction as *mut qemu_plugin_insn,
627 Some(handle_qemu_plugin_register_vcpu_insn_exec_cb::<F>),
628 flags,
629 userdata,
630 )
631 };
632 }
633
634 #[cfg(feature = "plugin-api-v3")]
636 pub fn register_conditional_execute_callback<F>(
637 &self,
638 cb: F,
639 cond: PluginCondition,
640 entry: PluginU64,
641 immediate: u64,
642 ) where
643 F: FnMut(VCPUIndex) + Send + Sync + 'static,
644 {
645 self.register_conditional_execute_callback_flags(
646 cb,
647 CallbackFlags::QEMU_PLUGIN_CB_NO_REGS,
648 cond,
649 entry,
650 immediate,
651 )
652 }
653
654 #[cfg(feature = "plugin-api-v3")]
656 pub fn register_conditional_execute_callback_flags<F>(
657 &self,
658 cb: F,
659 flags: CallbackFlags,
660 cond: PluginCondition,
661 entry: PluginU64,
662 immediate: u64,
663 ) where
664 F: FnMut(VCPUIndex) + Send + Sync + 'static,
665 {
666 let callback = Box::new(cb);
667 let callback_box = Box::new(callback);
668 let userdata = Box::into_raw(callback_box) as *mut c_void;
669
670 unsafe {
671 crate::sys::qemu_plugin_register_vcpu_insn_exec_cond_cb(
672 self.instruction as *mut qemu_plugin_insn,
673 Some(handle_qemu_plugin_register_vcpu_insn_exec_cb::<F>),
674 flags,
675 cond,
676 entry,
677 immediate,
678 userdata,
679 )
680 };
681 }
682
683 pub fn register_memory_access_callback<F>(&self, cb: F, rw: MemRW)
690 where
691 F: FnMut(VCPUIndex, MemoryInfo, u64) + Send + Sync + 'static,
692 {
693 self.register_memory_access_callback_flags(cb, rw, CallbackFlags::QEMU_PLUGIN_CB_NO_REGS)
694 }
695
696 pub fn register_memory_access_callback_flags<F>(&self, cb: F, rw: MemRW, flags: CallbackFlags)
703 where
704 F: FnMut(VCPUIndex, MemoryInfo, u64) + Send + Sync + 'static,
705 {
706 let callback = Box::new(cb);
707 let callback_box = Box::new(callback);
708 let userdata = Box::into_raw(callback_box) as *mut c_void;
709
710 unsafe {
711 crate::sys::qemu_plugin_register_vcpu_mem_cb(
712 self.instruction as *mut qemu_plugin_insn,
713 Some(handle_qemu_plugin_register_vcpu_mem_cb::<F>),
714 flags,
715 rw,
716 userdata,
717 )
718 };
719 }
720}
721
722pub struct MemoryInfo<'a> {
729 memory_info: qemu_plugin_meminfo_t,
730 marker: PhantomData<&'a ()>,
731}
732
733impl<'a> From<qemu_plugin_meminfo_t> for MemoryInfo<'a> {
734 fn from(info: qemu_plugin_meminfo_t) -> Self {
735 Self {
736 memory_info: info,
737 marker: PhantomData,
738 }
739 }
740}
741
742impl<'a> MemoryInfo<'a> {
743 pub fn size_shift(&self) -> usize {
746 (unsafe { crate::sys::qemu_plugin_mem_size_shift(self.memory_info) }) as usize
747 }
748
749 pub fn sign_extended(&self) -> bool {
751 unsafe { crate::sys::qemu_plugin_mem_is_sign_extended(self.memory_info) }
752 }
753
754 pub fn big_endian(&self) -> bool {
756 unsafe { crate::sys::qemu_plugin_mem_is_big_endian(self.memory_info) }
757 }
758
759 pub fn is_store(&self) -> bool {
761 unsafe { crate::sys::qemu_plugin_mem_is_store(self.memory_info) }
762 }
763
764 pub fn hwaddr(&self, vaddr: u64) -> Option<HwAddr> {
767 let hwaddr = unsafe { crate::sys::qemu_plugin_get_hwaddr(self.memory_info, vaddr) };
768 if hwaddr.is_null() {
769 None
770 } else {
771 Some(HwAddr::from(hwaddr))
772 }
773 }
774}
775
776#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
777#[derive(Clone)]
778pub struct RegisterDescriptor<'a> {
785 handle: usize,
788 pub name: String,
790 pub feature: Option<String>,
792 marker: PhantomData<&'a ()>,
793}
794
795#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
796impl<'a> From<qemu_plugin_reg_descriptor> for RegisterDescriptor<'a> {
797 fn from(descriptor: qemu_plugin_reg_descriptor) -> Self {
798 let name = unsafe { CStr::from_ptr(descriptor.name) }
799 .to_str()
800 .expect("Register name is not valid UTF-8")
801 .to_string();
802
803 let feature = if descriptor.feature.is_null() {
804 None
805 } else {
806 Some(
807 unsafe { CStr::from_ptr(descriptor.feature) }
808 .to_str()
809 .expect("Register feature is not valid UTF-8")
810 .to_string(),
811 )
812 };
813
814 Self {
815 handle: descriptor.handle as usize,
816 name,
817 feature,
818 marker: PhantomData,
819 }
820 }
821}
822
823#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
824impl<'a> Debug for RegisterDescriptor<'a> {
825 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
826 f.debug_struct("RegisterDescriptor")
827 .field("name", &self.name)
828 .field("feature", &self.feature)
829 .finish()
830 }
831}
832
833#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
834impl<'a> RegisterDescriptor<'a> {
835 pub fn read(&self) -> Result<Vec<u8>> {
841 let byte_array = unsafe { g_byte_array_new() };
842
843 let result = unsafe {
844 qemu_plugin_read_register(self.handle as *mut qemu_plugin_register, byte_array)
845 };
846
847 if result == -1 {
848 return Err(Error::RegisterReadError {
849 name: self.name.clone(),
850 });
851 }
852
853 let mut data = Vec::new();
854 data.extend_from_slice(unsafe {
855 std::slice::from_raw_parts((*byte_array).data, (*byte_array).len as usize)
856 });
857
858 assert_eq!(
859 unsafe { g_byte_array_free(byte_array, true) },
860 std::ptr::null_mut(),
861 "g_byte_array_free must return NULL"
862 );
863
864 Ok(data)
865 }
866
867 #[cfg(feature = "num-traits")]
868 pub fn read_be<T>(&self) -> Result<T>
874 where
875 T: PrimInt + FromBytes + Sized,
876 T: FromBytes<Bytes = [u8; std::mem::size_of::<T>()]>,
877 {
878 let data = self.read()?;
879 let mut bytes = [0; std::mem::size_of::<T>()];
880 bytes.copy_from_slice(&data);
881 Ok(T::from_be_bytes(&bytes))
882 }
883
884 #[cfg(feature = "num-traits")]
885 pub fn read_le<T>(&self) -> Result<T>
891 where
892 T: PrimInt + FromBytes + Sized,
893 T: FromBytes<Bytes = [u8; std::mem::size_of::<T>()]>,
894 {
895 let data = self.read()?;
896 let mut bytes = [0; std::mem::size_of::<T>()];
897 bytes.copy_from_slice(&data);
898 Ok(T::from_le_bytes(&bytes))
899 }
900}
901
902pub struct HwAddr<'a> {
909 hwaddr: usize,
910 marker: PhantomData<&'a ()>,
911}
912
913impl<'a> From<*mut qemu_plugin_hwaddr> for HwAddr<'a> {
914 fn from(hwaddr: *mut qemu_plugin_hwaddr) -> Self {
915 Self {
916 hwaddr: hwaddr as usize,
917 marker: PhantomData,
918 }
919 }
920}
921
922impl<'a> HwAddr<'a> {
923 pub fn is_io(&self) -> bool {
926 unsafe { crate::sys::qemu_plugin_hwaddr_is_io(self.hwaddr as *mut qemu_plugin_hwaddr) }
927 }
928
929 pub fn hwaddr(&self) -> u64 {
931 unsafe { crate::sys::qemu_plugin_hwaddr_phys_addr(self.hwaddr as *mut qemu_plugin_hwaddr) }
932 }
933
934 pub fn device_name(&self) -> Result<Option<String>> {
936 let device_name = unsafe {
937 crate::sys::qemu_plugin_hwaddr_device_name(self.hwaddr as *mut qemu_plugin_hwaddr)
938 };
939
940 if device_name.is_null() {
941 Ok(None)
942 } else {
943 let device_name_string = unsafe { CStr::from_ptr(device_name) }.to_str()?.to_string();
944 Ok(Some(device_name_string))
946 }
947 }
948}
949
950#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
951pub struct Scoreboard<'a, T>
957where
958 T: Sized,
959{
960 handle: usize,
961 marker: PhantomData<&'a T>,
962}
963
964#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
965impl<'a, T> Scoreboard<'a, T> {
966 pub fn new() -> Self {
969 let handle =
970 unsafe { crate::sys::qemu_plugin_scoreboard_new(std::mem::size_of::<T>()) as usize };
971
972 Self {
973 handle,
974 marker: PhantomData,
975 }
976 }
977
978 pub fn find<'b>(&mut self, vcpu_index: VCPUIndex) -> &'b mut MaybeUninit<T> {
981 unsafe {
982 &mut *(crate::sys::qemu_plugin_scoreboard_find(
983 self.handle as *mut qemu_plugin_scoreboard,
984 vcpu_index,
985 ) as *mut MaybeUninit<T>)
986 }
987 }
988}
989
990#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
991impl<'a, T> Default for Scoreboard<'a, T> {
992 fn default() -> Self {
993 Self::new()
994 }
995}
996
997#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
998impl<'a, T> Drop for Scoreboard<'a, T> {
999 fn drop(&mut self) {
1000 unsafe {
1001 crate::sys::qemu_plugin_scoreboard_free(self.handle as *mut qemu_plugin_scoreboard)
1002 }
1003 }
1004}
1005
1006#[allow(clippy::type_complexity)]
1011static UNINSTALL_CALLBACK: OnceLock<
1014 Mutex<Option<Box<Box<dyn FnOnce(qemu_plugin_id_t) + Send + Sync + 'static>>>>,
1015> = OnceLock::new();
1016#[allow(clippy::type_complexity)]
1017static RESET_CALLBACK: OnceLock<
1020 Mutex<Option<Box<Box<dyn FnOnce(qemu_plugin_id_t) + Send + Sync + 'static>>>>,
1021> = OnceLock::new();
1022
1023extern "C" fn handle_qemu_plugin_uninstall_callback(id: qemu_plugin_id_t) {
1026 if let Some(callback) = UNINSTALL_CALLBACK.get() {
1027 if let Ok(mut callback) = callback.lock() {
1028 if let Some(callback) = callback.take() {
1029 callback(id);
1030 }
1031 }
1032 }
1033 }
1035
1036extern "C" fn handle_qemu_plugin_reset_callback(id: qemu_plugin_id_t) {
1039 if let Some(callback) = UNINSTALL_CALLBACK.get() {
1040 if let Ok(mut callback) = callback.lock() {
1041 if let Some(callback) = callback.take() {
1042 callback(id);
1043 }
1044 }
1045 }
1046 }
1048
1049pub fn qemu_plugin_uninstall<F>(id: qemu_plugin_id_t, cb: F) -> Result<()>
1064where
1065 F: FnOnce(qemu_plugin_id_t) + Send + Sync + 'static,
1066{
1067 UNINSTALL_CALLBACK
1068 .set(Mutex::new(Some(Box::new(Box::new(cb)))))
1069 .map_err(|_| Error::ConcurrentPluginUninstallCallbackSet)?;
1070
1071 unsafe { crate::sys::qemu_plugin_uninstall(id, Some(handle_qemu_plugin_uninstall_callback)) };
1072
1073 Ok(())
1074}
1075
1076pub fn qemu_plugin_reset<F>(id: qemu_plugin_id_t, cb: F) -> Result<()>
1089where
1090 F: FnOnce(qemu_plugin_id_t) + Send + Sync + 'static,
1091{
1092 if let Some(callback) = RESET_CALLBACK.get() {
1093 let Ok(mut callback) = callback.lock() else {
1094 return Err(Error::PluginResetCallbackState);
1095 };
1096 let _ = callback.replace(Box::new(Box::new(cb)));
1097 } else {
1098 RESET_CALLBACK
1099 .set(Mutex::new(Some(Box::new(Box::new(cb)))))
1100 .map_err(|_| Error::ConcurrentPluginResetCallbackSet)?;
1101 }
1102
1103 unsafe { crate::sys::qemu_plugin_reset(id, Some(handle_qemu_plugin_reset_callback)) };
1104
1105 Ok(())
1106}
1107
1108pub fn qemu_plugin_register_vcpu_init_cb(id: qemu_plugin_id_t, cb: VCPUInitCallback) -> Result<()> {
1116 unsafe { crate::sys::qemu_plugin_register_vcpu_init_cb(id, cb) };
1117 Ok(())
1118}
1119
1120pub fn qemu_plugin_register_vcpu_exit_cb(id: qemu_plugin_id_t, cb: VCPUExitCallback) -> Result<()> {
1128 unsafe { crate::sys::qemu_plugin_register_vcpu_exit_cb(id, cb) };
1129 Ok(())
1130}
1131
1132pub fn qemu_plugin_register_vcpu_idle_cb(id: qemu_plugin_id_t, cb: VCPUIdleCallback) -> Result<()> {
1140 unsafe { crate::sys::qemu_plugin_register_vcpu_idle_cb(id, cb) };
1141 Ok(())
1142}
1143
1144pub fn qemu_plugin_register_vcpu_resume_cb(
1152 id: qemu_plugin_id_t,
1153 cb: VCPUResumeCallback,
1154) -> Result<()> {
1155 unsafe { crate::sys::qemu_plugin_register_vcpu_resume_cb(id, cb) };
1156 Ok(())
1157}
1158
1159pub fn qemu_plugin_register_vcpu_tb_trans_cb(
1169 id: qemu_plugin_id_t,
1170 cb: VCPUTranslationBlockTranslationCallback,
1171) -> Result<()> {
1172 unsafe { crate::sys::qemu_plugin_register_vcpu_tb_trans_cb(id, cb) };
1173 Ok(())
1174}
1175
1176extern "C" fn handle_qemu_plugin_register_vcpu_tb_exec_cb<F>(
1177 vcpu_index: VCPUIndex,
1178 userdata: *mut c_void,
1179) where
1180 F: FnMut(VCPUIndex) + Send + Sync + 'static,
1181{
1182 let mut cb: Box<Box<F>> = unsafe { Box::from_raw(userdata as *mut _) };
1183 cb(vcpu_index);
1184 Box::leak(cb);
1185}
1186
1187#[allow(clippy::not_unsafe_ptr_arg_deref)]
1188pub fn qemu_plugin_register_vcpu_tb_exec_cb<F>(tb: TranslationBlock, cb: F, flags: CallbackFlags)
1200where
1201 F: FnMut(VCPUIndex) + Send + Sync + 'static,
1202{
1203 tb.register_execute_callback_flags(cb, flags);
1204}
1205
1206#[cfg(feature = "plugin-api-v3")]
1207pub fn qemu_plugin_register_vcpu_tb_exec_cond_cb<F>(
1222 tb: TranslationBlock,
1223 cb: F,
1224 flags: CallbackFlags,
1225 cond: PluginCondition,
1226 entry: PluginU64,
1227 immediate: u64,
1228) where
1229 F: FnMut(VCPUIndex) + Send + Sync + 'static,
1230{
1231 tb.register_conditional_execute_callback_flags(cb, flags, cond, entry, immediate);
1232}
1233
1234#[cfg(feature = "plugin-api-v1")]
1235#[allow(clippy::not_unsafe_ptr_arg_deref)]
1236pub fn qemu_plugin_register_vcpu_tb_exec_inline(
1245 tb: TranslationBlock,
1246 op: PluginOp,
1247 ptr: *mut c_void,
1248 imm: u64,
1249) {
1250 unsafe {
1251 crate::sys::qemu_plugin_register_vcpu_tb_exec_inline(
1252 tb.translation_block as *mut qemu_plugin_tb,
1253 op,
1254 ptr,
1255 imm,
1256 );
1257 }
1258}
1259
1260#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
1261#[allow(clippy::not_unsafe_ptr_arg_deref)]
1262pub fn qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
1271 tb: TranslationBlock,
1272 op: PluginOp,
1273 entry: PluginU64,
1274 imm: u64,
1275) {
1276 unsafe {
1277 crate::sys::qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
1278 tb.translation_block as *mut qemu_plugin_tb,
1279 op,
1280 entry,
1281 imm,
1282 );
1283 }
1284}
1285
1286extern "C" fn handle_qemu_plugin_register_vcpu_insn_exec_cb<F>(
1287 vcpu_index: VCPUIndex,
1288 userdata: *mut c_void,
1289) where
1290 F: FnMut(VCPUIndex) + Send + Sync + 'static,
1291{
1292 let mut cb: Box<Box<F>> = unsafe { Box::from_raw(userdata as *mut _) };
1293 cb(vcpu_index);
1294 Box::leak(cb);
1296}
1297
1298#[allow(clippy::not_unsafe_ptr_arg_deref)]
1299pub fn qemu_plugin_register_vcpu_insn_exec_cb<F>(insn: Instruction, cb: F, flags: CallbackFlags)
1306where
1307 F: FnMut(VCPUIndex) + Send + Sync + 'static,
1308{
1309 insn.register_execute_callback_flags(cb, flags);
1310}
1311
1312#[cfg(feature = "plugin-api-v3")]
1313#[allow(clippy::not_unsafe_ptr_arg_deref)]
1314pub fn qemu_plugin_register_vcpu_insn_exec_cond_cb<F>(
1324 insn: Instruction,
1325 cb: F,
1326 flags: CallbackFlags,
1327 cond: PluginCondition,
1328 entry: PluginU64,
1329 immediate: u64,
1330) where
1331 F: FnMut(VCPUIndex) + Send + Sync + 'static,
1332{
1333 insn.register_conditional_execute_callback_flags(cb, flags, cond, entry, immediate);
1334}
1335
1336#[cfg(feature = "plugin-api-v1")]
1337#[allow(clippy::not_unsafe_ptr_arg_deref)]
1338pub fn qemu_plugin_register_vcpu_insn_exec_inline(
1347 insn: Instruction,
1348 op: PluginOp,
1349 ptr: *mut c_void,
1350 imm: u64,
1351) {
1352 unsafe {
1353 crate::sys::qemu_plugin_register_vcpu_insn_exec_inline(
1354 insn.instruction as *mut qemu_plugin_insn,
1355 op,
1356 ptr,
1357 imm,
1358 );
1359 }
1360}
1361
1362#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
1363#[allow(clippy::not_unsafe_ptr_arg_deref)]
1364pub fn qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
1373 insn: Instruction,
1374 op: PluginOp,
1375 entry: PluginU64,
1376 imm: u64,
1377) {
1378 unsafe {
1379 crate::sys::qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
1380 insn.instruction as *mut qemu_plugin_insn,
1381 op,
1382 entry,
1383 imm,
1384 );
1385 }
1386}
1387
1388extern "C" fn handle_qemu_plugin_register_vcpu_mem_cb<F>(
1389 vcpu_index: VCPUIndex,
1390 meminfo: qemu_plugin_meminfo_t,
1391 vaddr: u64,
1392 userdata: *mut c_void,
1393) where
1394 F: FnMut(VCPUIndex, MemoryInfo, u64) + Send + Sync + 'static,
1395{
1396 let mut cb: Box<Box<F>> = unsafe { Box::from_raw(userdata as *mut _) };
1397 let meminfo = MemoryInfo::from(meminfo);
1398 cb(vcpu_index, meminfo, vaddr);
1399 Box::leak(cb);
1401}
1402
1403pub fn qemu_plugin_register_vcpu_mem_cb<F>(
1412 insn: Instruction,
1413 cb: F,
1414 flags: CallbackFlags,
1415 rw: MemRW,
1416) where
1417 F: FnMut(VCPUIndex, MemoryInfo, u64) + Send + Sync + 'static,
1418{
1419 insn.register_memory_access_callback_flags(cb, rw, flags);
1420}
1421
1422#[cfg(feature = "plugin-api-v1")]
1423#[allow(clippy::not_unsafe_ptr_arg_deref)]
1424pub fn qemu_plugin_register_vcpu_mem_inline(
1434 insn: Instruction,
1435 rw: MemRW,
1436 op: PluginOp,
1437 ptr: *mut c_void,
1438 imm: u64,
1439) {
1440 unsafe {
1441 crate::sys::qemu_plugin_register_vcpu_mem_inline(
1442 insn.instruction as *mut qemu_plugin_insn,
1443 rw,
1444 op,
1445 ptr,
1446 imm,
1447 );
1448 }
1449}
1450
1451#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
1452#[allow(clippy::not_unsafe_ptr_arg_deref)]
1453pub fn qemu_plugin_register_vcpu_mem_inline_per_vcpu(
1463 insn: Instruction,
1464 rw: MemRW,
1465 op: PluginOp,
1466 entry: PluginU64,
1467 imm: u64,
1468) {
1469 unsafe {
1470 crate::sys::qemu_plugin_register_vcpu_mem_inline_per_vcpu(
1471 insn.instruction as *mut qemu_plugin_insn,
1472 rw,
1473 op,
1474 entry,
1475 imm,
1476 );
1477 }
1478}
1479
1480extern "C" fn handle_qemu_plugin_register_atexit_cb<F>(id: qemu_plugin_id_t, userdata: *mut c_void)
1481where
1482 F: FnOnce(qemu_plugin_id_t) + Send + Sync + 'static,
1483{
1484 let cb: Box<Box<F>> = unsafe { Box::from_raw(userdata as *mut _) };
1485 cb(id);
1486 }
1489
1490pub fn qemu_plugin_register_atexit_cb<F>(id: qemu_plugin_id_t, cb: F) -> Result<()>
1498where
1499 F: FnOnce(qemu_plugin_id_t) + Send + Sync + 'static,
1500{
1501 let callback = Box::new(cb);
1502 let callback_box = Box::new(callback);
1503 unsafe {
1504 crate::sys::qemu_plugin_register_atexit_cb(
1505 id,
1506 Some(handle_qemu_plugin_register_atexit_cb::<F>),
1507 Box::into_raw(callback_box) as *mut c_void,
1508 )
1509 };
1510 Ok(())
1511}
1512
1513pub fn qemu_plugin_register_flush_cb(id: qemu_plugin_id_t, cb: FlushCallback) {
1520 unsafe { crate::sys::qemu_plugin_register_flush_cb(id, cb) };
1521}
1522
1523pub fn qemu_plugin_register_vcpu_syscall_cb(id: qemu_plugin_id_t, cb: SyscallCallback) {
1530 unsafe { crate::sys::qemu_plugin_register_vcpu_syscall_cb(id, cb) };
1531}
1532
1533pub fn qemu_plugin_register_vcpu_syscall_ret_cb(id: qemu_plugin_id_t, cb: SyscallReturnCallback) {
1540 unsafe { crate::sys::qemu_plugin_register_vcpu_syscall_ret_cb(id, cb) };
1541}
1542
1543pub fn qemu_plugin_outs<S>(string: S) -> Result<()>
1545where
1546 S: AsRef<str>,
1547{
1548 unsafe {
1549 crate::sys::qemu_plugin_outs(CString::new(string.as_ref())?.as_ptr());
1550 }
1551
1552 Ok(())
1553}
1554
1555pub fn qemu_plugin_bool_parse<S>(name: S, val: S) -> Result<bool>
1566where
1567 S: AsRef<str>,
1568{
1569 let mut value = false;
1570 if unsafe {
1571 crate::sys::qemu_plugin_bool_parse(
1572 CString::new(name.as_ref())?.as_ptr(),
1573 CString::new(val.as_ref())?.as_ptr(),
1574 &mut value,
1575 )
1576 } {
1577 Ok(value)
1578 } else {
1579 Err(Error::InvalidBool {
1580 name: name.as_ref().to_string(),
1581 val: val.as_ref().to_string(),
1582 })
1583 }
1584}
1585
1586pub fn qemu_plugin_path_to_binary() -> Result<Option<PathBuf>> {
1590 let path_str = unsafe { crate::sys::qemu_plugin_path_to_binary() };
1591 if path_str.is_null() {
1592 Ok(None)
1593 } else {
1594 let path = unsafe { PathBuf::from(CStr::from_ptr(path_str).to_str()?) };
1595 unsafe { g_free(path_str as *mut _) };
1596 Ok(Some(path))
1597 }
1598}
1599
1600pub fn qemu_plugin_start_code() -> Option<u64> {
1605 let start = unsafe { crate::sys::qemu_plugin_start_code() };
1606
1607 if start == 0 {
1608 None
1609 } else {
1610 Some(start)
1611 }
1612}
1613
1614pub fn qemu_plugin_end_code() -> Option<u64> {
1619 let end = unsafe { crate::sys::qemu_plugin_end_code() };
1620
1621 if end == 0 {
1622 None
1623 } else {
1624 Some(end)
1625 }
1626}
1627
1628pub fn qemu_plugin_entry_code() -> Option<u64> {
1633 let entry = unsafe { crate::sys::qemu_plugin_entry_code() };
1634
1635 if entry == 0 {
1636 None
1637 } else {
1638 Some(entry)
1639 }
1640}
1641
1642#[cfg(feature = "plugin-api-v1")]
1643pub fn qemu_plugin_n_vcpus() -> Option<i32> {
1645 let vcpus = unsafe { crate::sys::qemu_plugin_n_vcpus() };
1646
1647 if vcpus == -1 {
1648 None
1649 } else {
1650 Some(vcpus)
1651 }
1652}
1653
1654#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
1655pub fn qemu_plugin_num_vcpus() -> Option<i32> {
1657 let vcpus = unsafe { crate::sys::qemu_plugin_num_vcpus() };
1658
1659 if vcpus == -1 {
1660 None
1661 } else {
1662 Some(vcpus)
1663 }
1664}
1665
1666#[cfg(feature = "plugin-api-v1")]
1667pub fn qemu_plugin_n_max_vcpus() -> Option<i32> {
1669 let max_cpus = unsafe { crate::sys::qemu_plugin_n_max_vcpus() };
1670
1671 if max_cpus == -1 {
1672 None
1673 } else {
1674 Some(max_cpus)
1675 }
1676}
1677
1678#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
1679pub fn qemu_plugin_get_registers<'a>() -> Result<Vec<RegisterDescriptor<'a>>> {
1682 use std::slice::from_raw_parts;
1683
1684 let array = unsafe { crate::sys::qemu_plugin_get_registers() };
1685
1686 let registers = unsafe {
1687 from_raw_parts(
1688 (*array).data as *mut qemu_plugin_reg_descriptor,
1689 (*array).len as usize,
1690 )
1691 }
1692 .iter()
1693 .map(|desc| RegisterDescriptor::from(*desc))
1694 .collect::<Vec<_>>();
1695
1696 assert_eq!(
1698 unsafe { g_array_free(array, true) },
1699 std::ptr::null_mut(),
1700 "g_array_free return value must be NULL"
1701 );
1702
1703 Ok(registers)
1704}
1705
1706#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
1707pub fn qemu_plugin_u64_add(entry: PluginU64, vcpu_index: VCPUIndex, added: u64) -> Result<()> {
1709 unsafe { crate::sys::qemu_plugin_u64_add(entry, vcpu_index, added) };
1710 Ok(())
1711}
1712
1713#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
1714pub fn qemu_plugin_u64_get(entry: PluginU64, vcpu_index: VCPUIndex) -> u64 {
1716 unsafe { crate::sys::qemu_plugin_u64_get(entry, vcpu_index) }
1717}
1718
1719#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
1720pub fn qemu_plugin_u64_set(entry: PluginU64, vcpu_index: VCPUIndex, value: u64) {
1722 unsafe { crate::sys::qemu_plugin_u64_set(entry, vcpu_index, value) }
1723}
1724
1725#[cfg(any(feature = "plugin-api-v2", feature = "plugin-api-v3"))]
1726pub fn qemu_plugin_scoreboard_sum(entry: PluginU64) -> u64 {
1728 unsafe { crate::sys::qemu_plugin_u64_sum(entry) }
1729}