kvmi-v6: Context emulation is specific to memory events
In the KVMi-v6 API, the ability to emulate new data or new instructions is tied to the reply of a memory event: https://github.com/KVM-VMI/kvm/blob/5205f803008a2ee5788ba0a9cc7a475a546889ba/include/uapi/linux/kvmi.h#L251
struct kvmi_event_pf_reply {
__u64 ctx_addr;
__u32 ctx_size;
__u8 singlestep;
__u8 rep_complete;
__u16 padding;
__u8 ctx_data[256];
};
It might be beneficial to have the same fields in a breakpoint response too. In fact, upon a breakpoint event, you can do a couple of things:
- recoiling
- replace the original instruction
- trigger singlestep
- replace breakpoint
-
emulate the original instruction in breakpoint even handler (a.k.a
RIP++), but this is not very reliable, unless you can embed a decompiler and can implement a specific behavior for each instruction. -
configure the original instruction to be emulated, via the breakpoint event response. In LibVMI, this behavior is already implemented in
breakpoint-emulate-example.c: https://github.com/libvmi/libvmi/blob/master/examples/breakpoint-emulate-example.c#L87
if (data->vaddr == event->interrupt_event.gla) {
// our breakpoint !
printf("We hit our breakpoint on %s, setting emulation buffer to 0x%"PRIx64"\n",
data->symbol, *(uint64_t*)data->emul.data);
// don't reinject
event->interrupt_event.reinject = 0;
// set previous opcode for emulation
event->emul_insn = &data->emul;
// set response to emulate instruction
rsp |= VMI_EVENT_RESPONSE_SET_EMUL_INSN;
}
Amond the benefits of simplifying recoiling on a breakpoint (which is tedius since you need to handle one event handler for the interrupt, and a second for the singlestep event), this solution has the advantage of being race-condition free in a multi-VCPU context.
Also, on a side note, this makes it more difficult to implement VMI_EVENT_RESPONSE_SET_EMUL_READ_DATA and VMI_EVENT_RESPONSE_SET_EMUL_INSN, as for kvmi-v6, they should be specific to a kvmi_event_pf.
So this type of generic process_cb_response() is not possible:
https://github.com/libvmi/libvmi/blob/master/libvmi/driver/xen/xen_events.c#L604
On Xen, I'm not sure, but LibVMI has an internal structure, with emulation context available for all events: https://github.com/libvmi/libvmi/blob/master/libvmi/driver/xen/xen_events_private.h#L97
typedef struct vm_event_compat {
uint32_t version;
uint32_t flags;
uint32_t reason;
uint32_t vcpu_id;
uint16_t altp2m_idx;
union {
struct vm_event_mem_access mem_access;
struct vm_event_write_ctrlreg write_ctrlreg;
struct vm_event_mov_to_msr_3 mov_to_msr;
struct vm_event_desc_access_3 desc_access;
struct vm_event_singlestep singlestep;
struct vm_event_debug_6 software_breakpoint;
struct vm_event_debug_6 debug_exception;
struct vm_event_cpuid cpuid;
struct vm_event_interrupt_x86 x86_interrupt;
};
union {
union {
x86_registers_t x86;
arm_registers_t arm;
} regs;
union {
struct vm_event_emul_read_data_4 read;
struct vm_event_emul_insn_data insn;
} emul;
} data;
} vm_event_compat_t;
@tklengyel , can you tell us if an emulation context is theoretically available for all VM events on Xen ?
Thanks.
It looks like read data is used for mem access and descriptor access events, while insn data for breakpoint events - https://github.com/hisilicon/Xen/blob/36e29dd9e580cb0f847f5ac1e72afdb5febe3e99/xen/arch/x86/vm_event.c#L184