aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--examples/long_mode.rs26
-rw-r--r--examples/real_mode.rs1
-rw-r--r--guest/Makefile17
-rw-r--r--guest/guest64.S8
-rw-r--r--src/fmt.rs3
-rw-r--r--src/kvm_sys.rs73
-rw-r--r--src/vcpu.rs66
-rw-r--r--src/x86_64.rs5
-rw-r--r--sysdeps/kvm.c22
9 files changed, 201 insertions, 20 deletions
diff --git a/examples/long_mode.rs b/examples/long_mode.rs
index 0696ae4..5969a48 100644
--- a/examples/long_mode.rs
+++ b/examples/long_mode.rs
@@ -6,6 +6,7 @@ use kvm_rs::{PhysAddr, UserMem};
fn setup_long_mode_segments(sregs: &mut kvm_sys::kvm_sregs) {
let code_seg = |seg: &mut kvm_sys::kvm_segment| {
+ // Segment base address (unused in 64bit).
seg.base = 0x0;
// Limit (unused in 64bit).
seg.limit = 0x0;
@@ -28,6 +29,7 @@ fn setup_long_mode_segments(sregs: &mut kvm_sys::kvm_sregs) {
};
let data_seg = |seg: &mut kvm_sys::kvm_segment| {
+ // Segment base address (unused in 64bit).
seg.base = 0x0;
// Limit (unused in 64bit).
seg.limit = 0x0;
@@ -88,20 +90,21 @@ fn setup_long_mode_4level_paging(mem: &mut UserMem) -> PhysAddr {
let mut w = |addr: PhysAddr, val: u64| mem.load(addr, &val.to_le_bytes());
// PML4E[0] refers to PDPE[0:4095].
- w(PhysAddr(0x0000), PAGE_ENTRY_PRESENT | PAGE_RENTRY_RW | 0x1000);
+ w(PhysAddr(0x0000), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x1000);
// PDPE[0] refers to PDE[0:4095].
- w(PhysAddr(0x1000), PAGE_ENTRY_PRESENT | PAGE_RENTRY_RW | 0x2000);
+ w(PhysAddr(0x1000), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x2000);
// PDE[0] refers to PTE[0:4095].
- w(PhysAddr(0x2000), PAGE_ENTRY_PRESENT | PAGE_RENTRY_RW | 0x3000);
+ w(PhysAddr(0x2000), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x3000);
// PTE[0] maps Virt [0x0000:0x0fff] -> Phys [0x4000:0x4fff].
- w(PhysAddr(0x3000), PAGE_ENTRY_PRESENT | PAGE_RENTRY_RW | 0x4000);
+ // Just because we can, map this page readonly, as we loaded our guest sw here.
+ w(PhysAddr(0x3000), PAGE_ENTRY_PRESENT | 0x4000);
// PTE[1] maps Virt [0x1000:0x1fff] -> Phys [0x5000:0x5fff].
- w(PhysAddr(0x3008), PAGE_ENTRY_PRESENT | PAGE_RENTRY_RW | 0x5000);
+ w(PhysAddr(0x3008), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x5000);
// PTE[2] maps Virt [0x2000:0x2fff] -> Phys [0x6000:0x6fff].
- w(PhysAddr(0x3010), PAGE_ENTRY_PRESENT | PAGE_RENTRY_RW | 0x6000);
+ w(PhysAddr(0x3010), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x6000);
// PTE[3] maps Virt [0x3000:0x3fff] -> Phys [0x7000:0x7fff].
- w(PhysAddr(0x3018), PAGE_ENTRY_PRESENT | PAGE_RENTRY_RW | 0x7000);
+ w(PhysAddr(0x3018), PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW | 0x7000);
// Return address of PML4.
PhysAddr(0x0000)
@@ -178,8 +181,17 @@ fn main() -> std::io::Result<()> {
data
);
}
+ KvmExit::Debug(_pc) => {}
};
}
+ // The guest writes at virtual address [0x2000 - 0x2003] which will be visible in physical
+ // memory at [0x6000 - 0x6003] due to the paging structure we setup.
+ // See `setup_long_mode_4level_paging` above for details.
+ assert_eq!(
+ &mem.as_ref()[0x4000 + 0x2000..][..4],
+ &[0xaa, 0xbb, 0xcc, 0xdd]
+ );
+
Ok(())
}
diff --git a/examples/real_mode.rs b/examples/real_mode.rs
index f37d45c..fa5fb19 100644
--- a/examples/real_mode.rs
+++ b/examples/real_mode.rs
@@ -51,6 +51,7 @@ fn main() -> std::io::Result<()> {
data
);
}
+ KvmExit::Debug(_pc) => {}
};
}
diff --git a/guest/Makefile b/guest/Makefile
index 5dca740..635759a 100644
--- a/guest/Makefile
+++ b/guest/Makefile
@@ -1,17 +1,14 @@
+ARCH16 := i8086
+ARCH64 := i386:x86-64
+
guest: guest16 guest64
disasm: disasm16 disasm64
-guest16: guest.ld guest16.S
- $(CC) $(CFLAGS) -m16 -o $@ -nostdlib -ffreestanding -Wpedantic -Wall -Wextra -Werror -T guest.ld guest16.S
-
-guest64: guest.ld guest64.S
- $(CC) $(CFLAGS) -m64 -o $@ -nostdlib -ffreestanding -Wpedantic -Wall -Wextra -Werror -T guest.ld guest64.S
-
-disasm16: guest16
- objdump -D -b binary -m i8086 -M intel $^
+guest%: guest.ld guest%.S
+ $(CC) $(CFLAGS) -m$* -o $@ -nostdlib -ffreestanding -Wpedantic -Wall -Wextra -Werror -T guest.ld guest$*.S
-disasm64: guest64
- objdump -D -b binary -m i386:x86-64 -M intel $^
+disasm%: guest%
+ objdump -D -b binary -m $(ARCH$*) -M intel $^
clean:
$(RM) guest16 guest64
diff --git a/guest/guest64.S b/guest/guest64.S
index 6629273..533d186 100644
--- a/guest/guest64.S
+++ b/guest/guest64.S
@@ -9,9 +9,15 @@
// Trigger `KVM_EXIT_IO:KVM_EXIT_IO_IN` by reading byte to memory from input port.
mov dx, 0x1000 // Input port.
- lea di, [in_dest] // Destination address.
+ lea di, [rip + in_dest] // Destination address.
insb // Read byte from input port dx to ds:di.
+ // Write to allocated virtual addresses.
+ mov byte ptr ds:[0x2000], 0xaa
+ mov byte ptr ds:[0x2001], 0xbb
+ mov byte ptr ds:[0x2002], 0xcc
+ mov byte ptr ds:[0x2003], 0xdd
+
// Trigger `KVM_EXIT_HLT`.
hlt
diff --git a/src/fmt.rs b/src/fmt.rs
index 9ae8689..24971bf 100644
--- a/src/fmt.rs
+++ b/src/fmt.rs
@@ -12,7 +12,7 @@ impl fmt::Display for kvm_regs {
r8 : {:#018x} r9 : {:#018x} r10: {:#018x} r11: {:#018x}\n\
r12: {:#018x} r13: {:#018x} r14: {:#018x} r15: {:#018x}\n\
rsp: {:#018x} rbp: {:#018x}\n\
- rip: {:#018x} rfl: {:#018x} O({}) D({}) I({}) S({}) Z({}) P({}) C({})",
+ rip: {:#018x} rfl: {:#018x} O({}) D({}) I({}) T({}) S({}) Z({}) P({}) C({})",
self.rax,
self.rbx,
self.rcx,
@@ -34,6 +34,7 @@ impl fmt::Display for kvm_regs {
rflags_of(self.rflags),
rflags_df(self.rflags),
rflags_if(self.rflags),
+ rflags_tf(self.rflags),
rflags_sf(self.rflags),
rflags_zf(self.rflags),
rflags_pf(self.rflags),
diff --git a/src/kvm_sys.rs b/src/kvm_sys.rs
index d21da47..2ee97a1 100644
--- a/src/kvm_sys.rs
+++ b/src/kvm_sys.rs
@@ -91,6 +91,32 @@ pub(crate) struct kvm_userspace_memory_region {
pub userspace_addr: u64,
}
+#[cfg(target_arch = "x86_64")]
+#[repr(C)]
+#[derive(Default, Debug)]
+pub struct kvm_debugregs {
+ pub db: [u64; 4],
+ pub dr6: u64,
+ pub dr7: u64,
+ pub flags: u64,
+ pub reserved: [u64; 9],
+}
+
+#[repr(C)]
+#[derive(Default, Debug)]
+pub(crate) struct kvm_guest_debug {
+ pub control: u32,
+ pad: u32,
+ pub arch: kvm_guest_debug_arch,
+}
+
+#[cfg(target_arch = "x86_64")]
+#[repr(C)]
+#[derive(Default, Debug)]
+pub(crate) struct kvm_guest_debug_arch {
+ pub debugreg: [u64; 8],
+}
+
#[repr(C)]
pub(crate) struct kvm_run {
request_interrupt_window: u8,
@@ -127,11 +153,23 @@ pub(crate) struct kvm_run_mmio {
pub is_write: u8,
}
+#[cfg(target_arch = "x86_64")]
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct kvm_run_debug {
+ pub exception: u32,
+ pad: u32,
+ pub pc: u64,
+ pub dr6: u64,
+ pub dr7: u64,
+}
+
// Only add the union fields used here.
#[repr(C)]
pub(crate) union kvm_run_union {
pub io: kvm_run_io,
pub mmio: kvm_run_mmio,
+ pub debug: kvm_run_debug,
padding: [u8; 256],
}
@@ -194,4 +232,39 @@ mod tests {
assert_eq!(mem::size_of::<kvm_run_mmio>(), TEST_KVM_RUN_MMIO_SIZE);
assert_eq!(mem::size_of::<kvm_run_union_s>(), TEST_KVM_RUN_UNION_S_SIZE);
}
+
+ #[cfg(target_arch = "x86_64")]
+ #[test]
+ fn check_kvm_run_x86() {
+ assert_eq!(mem::size_of::<kvm_run_debug>(), TEST_KVM_RUN_DEBUG_SIZE);
+ }
+
+ #[cfg(target_arch = "x86_64")]
+ #[test]
+ fn check_kvm_debugregs() {
+ assert_eq!(mem::size_of::<kvm_debugregs>(), TEST_KVM_DEBUGREGS_SIZE);
+ assert_eq!(mem::align_of::<kvm_debugregs>(), TEST_KVM_DEBUGREGS_ALIGN);
+ }
+
+ #[test]
+ fn check_kvm_guest_dbg() {
+ assert_eq!(mem::size_of::<kvm_guest_debug>(), TEST_KVM_GUEST_DEBUG_SIZE);
+ assert_eq!(
+ mem::align_of::<kvm_guest_debug>(),
+ TEST_KVM_GUEST_DEBUG_ALIGN
+ );
+ }
+
+ #[cfg(target_arch = "x86_64")]
+ #[test]
+ fn check_kvm_guest_dbg_arch() {
+ assert_eq!(
+ mem::size_of::<kvm_guest_debug_arch>(),
+ TEST_KVM_GUEST_DEBUG_ARCH_SIZE
+ );
+ assert_eq!(
+ mem::align_of::<kvm_guest_debug_arch>(),
+ TEST_KVM_GUEST_DEBUG_ARCH_ALIGN
+ );
+ }
}
diff --git a/src/vcpu.rs b/src/vcpu.rs
index f0d311e..4b25736 100644
--- a/src/vcpu.rs
+++ b/src/vcpu.rs
@@ -18,6 +18,7 @@ pub enum KvmExit<'cpu> {
IoOut(u16, &'cpu [u8]),
MmioRead(u64, &'cpu mut [u8]),
MmioWrite(u64, &'cpu [u8]),
+ Debug(u64),
}
/// Wrapper for VCPU ioctls.
@@ -86,6 +87,65 @@ impl Vcpu {
.map(|_| ())
}
+ /// Get the debug registers with the [`KVM_GET_DEBUGREGS`][kvm-get-debugregs] ioctl in form of
+ /// [`kvm_debugregs`](crate::kvm_sys::kvm_debugregs).
+ ///
+ /// [kvm-get-debugregs]:
+ /// https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-get-debugregs
+ #[cfg(target_arch = "x86_64")]
+ pub fn get_debugregs(&self) -> io::Result<kvm_sys::kvm_debugregs> {
+ let mut dregs = kvm_sys::kvm_debugregs::default();
+ ioctl(
+ &self.vcpu,
+ kvm_sys::KVM_GET_DEBUGREGS,
+ &mut dregs as *mut _ as u64,
+ )?;
+ Ok(dregs)
+ }
+
+ /// Set the debug registers with the [`KVM_SET_DEBUGREGS`][kvm-set-debugregs] ioctl in form of
+ /// [`kvm_debugregs`](crate::kvm_sys::kvm_debugregs).
+ ///
+ /// [kvm-set-debugregs]:
+ /// https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-set-debugregs
+ #[cfg(target_arch = "x86_64")]
+ pub fn set_debugregs(&self, dregs: kvm_sys::kvm_debugregs) -> io::Result<()> {
+ ioctl(
+ &self.vcpu,
+ kvm_sys::KVM_SET_DEBUGREGS,
+ &dregs as *const _ as u64,
+ )
+ .map(|_| ())
+ }
+
+ /// Enable or disable guest single steppig (debug) with the
+ /// [`KVM_GUESTDBG_ENABLE`][kvm-guest-debug] ioctl.
+ ///
+ /// [kvm-guest-debug]: https://www.kernel.org/doc/html/latest/virt/kvm/api.html#kvm-set-guest-debug
+ #[cfg(target_arch = "x86_64")]
+ pub fn set_single_step(&self, enable: bool) -> io::Result<()> {
+ let mut dbg = kvm_sys::kvm_guest_debug::default();
+
+ if enable {
+ // Enable guest debugging and single stepping.
+ dbg.control = kvm_sys::KVM_GUESTDBG_ENABLE | kvm_sys::KVM_GUESTDBG_SINGLESTEP;
+ }
+
+ // Initialize debug registers based on current VCPUs debug register values.
+ let dregs = self.get_debugregs()?;
+ dbg.arch.debugreg[0..4].copy_from_slice(&dregs.db);
+ // DR4-DR5 are reserved.
+ dbg.arch.debugreg[6] = dregs.dr6;
+ dbg.arch.debugreg[7] = dregs.dr7;
+
+ ioctl(
+ &self.vcpu,
+ kvm_sys::KVM_SET_GUEST_DEBUG,
+ &dbg as *const _ as u64,
+ )
+ .map(|_| ())
+ }
+
/// Run the guest VCPU with the [`KVM_RUN`][kvm-run] ioctl until it exits with one of the exit
/// reasons described in [`KvmExit`](crate::vcpu::KvmExit).
///
@@ -128,6 +188,12 @@ impl Vcpu {
_ => unreachable!(),
}
}
+ kvm_sys::KVM_EXIT_DEBUG => {
+ // Safe to use union `debug` field, as Kernel instructed us to.
+ let debug = unsafe { kvm_run.inner.debug };
+
+ Ok(KvmExit::Debug(debug.pc))
+ }
r @ _ => {
todo!("KVM_EXIT_... (exit_reason={}) not implemented!", r)
}
diff --git a/src/x86_64.rs b/src/x86_64.rs
index ac153e9..d87319c 100644
--- a/src/x86_64.rs
+++ b/src/x86_64.rs
@@ -16,6 +16,8 @@ mod x86_64 {
pub const RFLAGS_ZF: u64 = 1 << 6;
/// Sign flag.
pub const RFLAGS_SF: u64 = 1 << 7;
+ /// Trap flag.
+ pub const RFLAGS_TF: u64 = 1 << 8;
/// Sign flag.
pub const RFLAGS_IF: u64 = 1 << 9;
/// Direction flag.
@@ -32,6 +34,7 @@ mod x86_64 {
pub const fn rflags_af(r: u64) -> u64 { (r & RFLAGS_AF) >> 4 }
pub const fn rflags_zf(r: u64) -> u64 { (r & RFLAGS_ZF) >> 6 }
pub const fn rflags_sf(r: u64) -> u64 { (r & RFLAGS_SF) >> 7 }
+ pub const fn rflags_tf(r: u64) -> u64 { (r & RFLAGS_TF) >> 8 }
pub const fn rflags_if(r: u64) -> u64 { (r & RFLAGS_IF) >> 9 }
pub const fn rflags_df(r: u64) -> u64 { (r & RFLAGS_DF) >> 10 }
pub const fn rflags_of(r: u64) -> u64 { (r & RFLAGS_OF) >> 11 }
@@ -155,5 +158,5 @@ mod x86_64 {
/// Page region read/write.
///
/// If set, region reference by paging entry is writeable.
- pub const PAGE_RENTRY_RW: u64 = 1 << 1;
+ pub const PAGE_ENTRY_RW: u64 = 1 << 1;
}
diff --git a/sysdeps/kvm.c b/sysdeps/kvm.c
index f7ff1a4..2be08a9 100644
--- a/sysdeps/kvm.c
+++ b/sysdeps/kvm.c
@@ -47,6 +47,20 @@ int main() {
// param: struct kvm_sregs
// ret : 0 success, -1 error
printf("pub(crate) const KVM_SET_SREGS : u64 = 0x%lx;\n", KVM_SET_SREGS);
+ // param: struct kvm_debugregs
+ // ret : 0 success, -1 error
+ printf("pub(crate) const KVM_GET_DEBUGREGS : u64 = 0x%lx;\n", KVM_GET_DEBUGREGS);
+ // param: struct kvm_debugregs
+ // ret : 0 success, -1 error
+ printf("pub(crate) const KVM_SET_DEBUGREGS : u64 = 0x%lx;\n", KVM_SET_DEBUGREGS);
+ // param: struct kvm_guest_debug
+ // ret : 0 success, -1 error
+ printf("pub(crate) const KVM_SET_GUEST_DEBUG : u64 = 0x%lx;\n", KVM_SET_GUEST_DEBUG);
+
+ /* struct kvm_guest_debug constants */
+
+ printf("pub(crate) const KVM_GUESTDBG_ENABLE : u32 = 0x%x;\n", KVM_GUESTDBG_ENABLE);
+ printf("pub(crate) const KVM_GUESTDBG_SINGLESTEP : u32 = 0x%x;\n", KVM_GUESTDBG_SINGLESTEP);
/* struct kvm_run constants */
@@ -55,6 +69,7 @@ int main() {
printf("pub(crate) const KVM_EXIT_IO_IN : u64 = 0x%x;\n", KVM_EXIT_IO_IN);
printf("pub(crate) const KVM_EXIT_IO_OUT : u64 = 0x%x;\n", KVM_EXIT_IO_OUT);
printf("pub(crate) const KVM_EXIT_MMIO : u64 = 0x%x;\n", KVM_EXIT_MMIO);
+ printf("pub(crate) const KVM_EXIT_DEBUG : u64 = 0x%x;\n", KVM_EXIT_DEBUG);
/* Capabilities */
@@ -99,7 +114,14 @@ int main() {
printf("#[cfg(test)] const TEST_KVM_RUN_ALIGN : usize = %ld;\n", alignof(struct kvm_run));
printf("#[cfg(test)] const TEST_KVM_RUN_IO_SIZE : usize = %ld;\n", sizeof(((struct kvm_run*)0)->io));
printf("#[cfg(test)] const TEST_KVM_RUN_MMIO_SIZE : usize = %ld;\n", sizeof(((struct kvm_run*)0)->mmio));
+ printf("#[cfg(test)] const TEST_KVM_RUN_DEBUG_SIZE : usize = %ld;\n", sizeof(((struct kvm_run*)0)->debug));
printf("#[cfg(test)] const TEST_KVM_RUN_UNION_S_SIZE : usize = %ld;\n", sizeof(((struct kvm_run*)0)->s));
+ printf("#[cfg(test)] const TEST_KVM_DEBUGREGS_SIZE: usize = %ld;\n", sizeof(struct kvm_debugregs));
+ printf("#[cfg(test)] const TEST_KVM_DEBUGREGS_ALIGN: usize = %ld;\n", alignof(struct kvm_debugregs));
+ printf("#[cfg(test)] const TEST_KVM_GUEST_DEBUG_SIZE: usize = %ld;\n", sizeof(struct kvm_guest_debug));
+ printf("#[cfg(test)] const TEST_KVM_GUEST_DEBUG_ALIGN: usize = %ld;\n", alignof(struct kvm_guest_debug));
+ printf("#[cfg(test)] const TEST_KVM_GUEST_DEBUG_ARCH_SIZE: usize = %ld;\n", sizeof(struct kvm_guest_debug_arch));
+ printf("#[cfg(test)] const TEST_KVM_GUEST_DEBUG_ARCH_ALIGN: usize = %ld;\n", alignof(struct kvm_guest_debug_arch));
return 0;
}