Skip to content

Commit f87ee79

Browse files
baciumarandreeaflorescu
authored andcommitted
Add support for KVM_SET_GUEST_DEBUG ioctl call
This enables us to handle software breakpoints, perform single-stepping and possibly, implement hardware breakpoints Signed-off-by: Marius-Cristian Baciu <[email protected]>
1 parent aed8f65 commit f87ee79

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

coverage_config_x86_64.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 91.1,
2+
"coverage_score": 91.2,
33
"exclude_path": "",
44
"crate_features": ""
55
}

src/ioctls/vcpu.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,59 @@ impl VcpuFd {
10451045
Ok(())
10461046
}
10471047

1048+
/// Sets processor-specific debug registers and configures the vcpu for handling
1049+
/// certain guest debug events using the `KVM_SET_GUEST_DEBUG` ioctl.
1050+
///
1051+
/// # Arguments
1052+
///
1053+
/// * `debug_struct` - control bitfields and debug registers, depending on the specific architecture.
1054+
/// For details check the `kvm_guest_debug` structure in the
1055+
/// [KVM API doc](https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt).
1056+
///
1057+
/// # Example
1058+
///
1059+
/// ```rust
1060+
/// # extern crate kvm_ioctls;
1061+
/// # extern crate kvm_bindings;
1062+
/// # use kvm_ioctls::Kvm;
1063+
/// # use kvm_bindings::{
1064+
/// # KVM_GUESTDBG_ENABLE, KVM_GUESTDBG_USE_SW_BP, kvm_guest_debug_arch, kvm_guest_debug
1065+
/// # };
1066+
/// let kvm = Kvm::new().unwrap();
1067+
/// let vm = kvm.create_vm().unwrap();
1068+
/// let vcpu = vm.create_vcpu(0).unwrap();
1069+
///
1070+
/// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
1071+
/// let debug_struct = kvm_guest_debug {
1072+
/// // Configure the vcpu so that a KVM_DEBUG_EXIT would be generated
1073+
/// // when encountering a software breakpoint during execution
1074+
/// control: KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP,
1075+
/// pad: 0,
1076+
/// // Reset all x86-specific debug registers
1077+
/// arch: kvm_guest_debug_arch {
1078+
/// debugreg: [0, 0, 0, 0, 0, 0, 0, 0],
1079+
/// },
1080+
/// };
1081+
///
1082+
/// vcpu.set_guest_debug(&debug_struct).unwrap();
1083+
/// }
1084+
/// ```
1085+
///
1086+
#[cfg(any(
1087+
target_arch = "x86",
1088+
target_arch = "x86_64",
1089+
target_arch = "arm64",
1090+
target_arch = "s390",
1091+
target_arch = "ppc"
1092+
))]
1093+
pub fn set_guest_debug(&self, debug_struct: &kvm_guest_debug) -> Result<()> {
1094+
let ret = unsafe { ioctl_with_ref(self, KVM_SET_GUEST_DEBUG(), debug_struct) };
1095+
if ret < 0 {
1096+
return Err(errno::Error::last());
1097+
}
1098+
Ok(())
1099+
}
1100+
10481101
/// Sets the value of one register for this vCPU.
10491102
///
10501103
/// The id of the register is encoded as specified in the kernel documentation
@@ -1732,6 +1785,7 @@ mod tests {
17321785
0xc6, 0x06, 0x00, 0x20, 0x00, /* movl $0, (0x2000); Dirty one page in guest mem. */
17331786
0xf4, /* hlt */
17341787
];
1788+
let expected_rips: [u64; 3] = [0x1003, 0x1005, 0x1007];
17351789

17361790
let mem_size = 0x4000;
17371791
let load_addr = mmap_anonymous(mem_size);
@@ -1772,6 +1826,16 @@ mod tests {
17721826
vcpu_regs.rflags = 2;
17731827
vcpu_fd.set_regs(&vcpu_regs).unwrap();
17741828

1829+
let mut debug_struct = kvm_guest_debug {
1830+
control: KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP,
1831+
pad: 0,
1832+
arch: kvm_guest_debug_arch {
1833+
debugreg: [0, 0, 0, 0, 0, 0, 0, 0],
1834+
},
1835+
};
1836+
vcpu_fd.set_guest_debug(&debug_struct).unwrap();
1837+
1838+
let mut instr_idx = 0;
17751839
loop {
17761840
match vcpu_fd.run().expect("run failed") {
17771841
VcpuExit::IoIn(addr, data) => {
@@ -1792,6 +1856,20 @@ mod tests {
17921856
assert_eq!(data.len(), 1);
17931857
assert_eq!(data[0], 0);
17941858
}
1859+
VcpuExit::Debug => {
1860+
if instr_idx == expected_rips.len() - 1 {
1861+
// Disabling debugging/single-stepping
1862+
debug_struct.control = 0;
1863+
vcpu_fd.set_guest_debug(&debug_struct).unwrap();
1864+
} else {
1865+
if instr_idx >= expected_rips.len() {
1866+
assert!(false);
1867+
}
1868+
}
1869+
let vcpu_regs = vcpu_fd.get_regs().unwrap();
1870+
assert_eq!(vcpu_regs.rip, expected_rips[instr_idx]);
1871+
instr_idx += 1;
1872+
}
17951873
VcpuExit::Hlt => {
17961874
// The code snippet dirties 2 pages:
17971875
// * one when the code itself is loaded in memory;

src/kvm_ioctls.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ ioctl_ior_nr!(KVM_ARM_PREFERRED_TARGET, KVMIO, 0xaf, kvm_vcpu_init);
217217
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
218218
ioctl_iowr_nr!(KVM_GET_REG_LIST, KVMIO, 0xb0, kvm_reg_list);
219219

220+
/* Available with KVM_CAP_SET_GUEST_DEBUG */
221+
ioctl_iow_nr!(KVM_SET_GUEST_DEBUG, KVMIO, 0x9b, kvm_guest_debug);
222+
220223
// Device ioctls.
221224

222225
/* Available with KVM_CAP_DEVICE_CTRL */

0 commit comments

Comments
 (0)