#[cfg(target_arch = "x86_64")]
pub mod x86_64;
pub(crate) mod fs;
pub(crate) mod gdb;
pub(crate) type DebugExitInfo = kvm_bindings::kvm_debug_exit_arch;
pub(crate) type Breakpoints = gdb::breakpoints::AllBreakpoints;
use std::sync::LazyLock;
use gdbstub::{common::Tid, stub::MultiThreadStopReason};
use kvm_ioctls::Kvm;
use libc::{SIGRTMAX, SIGRTMIN};
use nix::sys::pthread::Pthread;
use crate::{os::x86_64::kvm_cpu::KvmVm, vm::KickSignal};
static KVM: LazyLock<Kvm> = LazyLock::new(|| Kvm::new().unwrap());
impl KickSignal {
const RTSIG_OFFSET: libc::c_int = 0;
fn get() -> libc::c_int {
let kick_signal: libc::c_int = SIGRTMIN() + Self::RTSIG_OFFSET;
assert!(kick_signal <= SIGRTMAX());
kick_signal
}
pub(crate) fn register_handler() -> nix::Result<()> {
extern "C" fn handle_signal(_signal: libc::c_int) {}
let res = unsafe {
libc::signal(
Self::get(),
handle_signal as *const () as libc::sighandler_t,
)
};
nix::errno::Errno::result(res).map(drop)
}
pub(crate) fn pthread_kill(pthread: Pthread) -> nix::Result<()> {
let res = unsafe { libc::pthread_kill(pthread, Self::get()) };
nix::errno::Errno::result(res).map(drop)
}
pub(crate) fn block_in_current_thread() -> nix::Result<()> {
let mut set: libc::sigset_t = unsafe { std::mem::zeroed() };
let res = unsafe {
libc::sigemptyset(&mut set);
libc::sigaddset(&mut set, Self::get());
libc::pthread_sigmask(libc::SIG_BLOCK, &set, std::ptr::null_mut())
};
nix::errno::Errno::result(res).map(drop)
}
pub(crate) fn drain_pending_in_current_thread() {
let mut set: libc::sigset_t = unsafe { std::mem::zeroed() };
let zero_timeout = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe {
libc::sigemptyset(&mut set);
libc::sigaddset(&mut set, Self::get());
loop {
let mut info: libc::siginfo_t = std::mem::zeroed();
if libc::sigtimedwait(&set, &mut info, &zero_timeout) < 0 {
break;
}
}
}
}
}
pub(crate) fn debug_info_to_stop_reason(
debug: DebugExitInfo,
tid: Tid,
breakpoints: &Breakpoints,
) -> MultiThreadStopReason<u64> {
use kvm_bindings::{BP_VECTOR, DB_VECTOR};
match debug.exception {
DB_VECTOR => {
use ::x86_64::registers::debug::Dr6Flags;
let dr6 = Dr6Flags::from_bits_truncate(debug.dr6);
breakpoints.hard.stop_reason(tid, dr6)
}
BP_VECTOR => MultiThreadStopReason::SwBreak(tid),
vector => unreachable!("unknown KVM exception vector: {}", vector),
}
}