Skip to content
This repository has been archived by the owner on Jan 28, 2023. It is now read-only.

Optimizing VMCS reads/writes with a new cache implementation #130

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
100 changes: 6 additions & 94 deletions core/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,29 +186,6 @@ void cpu_pmu_init(void *arg)
pmu_info->cpuid_edx = cpuid_args.edx;
}

static void vmread_cr(struct vcpu_t *vcpu)
{
struct vcpu_state_t *state = vcpu->state;
mword cr4, cr4_mask;

// Update only the bits the guest is allowed to change
// This must use the actual cr0 mask, not _cr0_mask.
mword cr0 = vmread(vcpu, GUEST_CR0);
mword cr0_mask = vmread(vcpu, VMX_CR0_MASK); // should cache this
hax_debug("vmread_cr cr0 %lx, cr0_mask %lx, state->_cr0 %llx\n", cr0,
cr0_mask, state->_cr0);
state->_cr0 = (cr0 & ~cr0_mask) | (state->_cr0 & cr0_mask);
hax_debug("vmread_cr, state->_cr0 %llx\n", state->_cr0);

// update CR3 only if guest is allowed to change it
if (!(vmx(vcpu, pcpu_ctls) & CR3_LOAD_EXITING))
state->_cr3 = vmread(vcpu, GUEST_CR3);

cr4 = vmread(vcpu, GUEST_CR4);
cr4_mask = vmread(vcpu, VMX_CR4_MASK); // should cache this
state->_cr4 = (cr4 & ~cr4_mask) | (state->_cr4 & cr4_mask);
}

vmx_result_t cpu_vmx_vmptrld(struct per_cpu_data *cpu_data, hax_paddr_t vmcs,
struct vcpu_t *vcpu)
{
Expand Down Expand Up @@ -271,19 +248,12 @@ __attribute__ ((__noinline__))
vmx_result_t cpu_vmx_run(struct vcpu_t *vcpu, struct hax_tunnel *htun)
{
vmx_result_t result = 0;
mword host_rip;

/* prepare the RIP */
hax_debug("vm entry!\n");
vcpu_save_host_state(vcpu);
hax_disable_irq();

/*
* put the vmwrite before is_running, so that the vcpu->cpu_id is set
* when we check vcpu->is_running in vcpu_pause
*/
host_rip = vmx_get_rip();
vmwrite(vcpu, HOST_RIP, (mword)host_rip);
vcpu->is_running = 1;
#ifdef DEBUG_HOST_STATE
vcpu_get_host_state(vcpu, 1);
Expand All @@ -304,6 +274,8 @@ vmx_result_t cpu_vmx_run(struct vcpu_t *vcpu, struct hax_tunnel *htun)
compare_host_state(vcpu);
#endif

vcpu_vmcs_flush_cache_r(vcpu);

if (result != VMX_SUCCEED) {
cpu_vmentry_failed(vcpu, result);
htun->_exit_reason = 0;
Expand All @@ -312,43 +284,12 @@ vmx_result_t cpu_vmx_run(struct vcpu_t *vcpu, struct hax_tunnel *htun)
return result;
}

void vcpu_handle_vmcs_pending(struct vcpu_t *vcpu)
{
if (!vcpu || !vcpu->vmcs_pending)
return;
if (vcpu->vmcs_pending_entry_error_code) {
vmwrite(vcpu, VMX_ENTRY_EXCEPTION_ERROR_CODE,
vmx(vcpu, entry_exception_error_code));
vcpu->vmcs_pending_entry_error_code = 0;
}

if (vcpu->vmcs_pending_entry_instr_length) {
vmwrite(vcpu, VMX_ENTRY_INSTRUCTION_LENGTH,
vmx(vcpu, entry_instr_length));
vcpu->vmcs_pending_entry_instr_length = 0;
}

if (vcpu->vmcs_pending_entry_intr_info) {
vmwrite(vcpu, VMX_ENTRY_INTERRUPT_INFO,
vmx(vcpu, entry_intr_info).raw);
vcpu->vmcs_pending_entry_intr_info = 0;
}

if (vcpu->vmcs_pending_guest_cr3) {
vmwrite(vcpu, GUEST_CR3, vtlb_get_cr3(vcpu));
vcpu->vmcs_pending_guest_cr3 = 0;
}
vcpu->vmcs_pending = 0;
return;
}

/* Return the value same as ioctl value */
int cpu_vmx_execute(struct vcpu_t *vcpu, struct hax_tunnel *htun)
{
vmx_result_t res = 0;
int ret;
preempt_flag flags;
struct vcpu_state_t *state = vcpu->state;
uint32_t vmcs_err = 0;

while (1) {
Expand All @@ -366,7 +307,7 @@ int cpu_vmx_execute(struct vcpu_t *vcpu, struct hax_tunnel *htun)
hax_panic_log(vcpu);
return 0;
}
vcpu_handle_vmcs_pending(vcpu);
vcpu_vmcs_flush_cache_w(vcpu);
vcpu_inject_intr(vcpu, htun);

/* sometimes, the code segment type from qemu can be 10 (code segment),
Expand Down Expand Up @@ -402,45 +343,16 @@ int cpu_vmx_execute(struct vcpu_t *vcpu, struct hax_tunnel *htun)
return -EINVAL;
}

exit_reason.raw = vmread(vcpu, VM_EXIT_INFO_REASON);
exit_reason.raw = vmcs_read(vcpu, VM_EXIT_INFO_REASON);
hax_debug("....exit_reason.raw %x, cpu %d %d\n", exit_reason.raw,
vcpu->cpu_id, hax_cpuid());

/* XXX Currently we take active save/restore for MSR and FPU, the main
* reason is, we have no schedule hook to get notified of preemption
* This should be changed later after get better idea
*/
vcpu->state->_rip = vmread(vcpu, GUEST_RIP);

hax_handle_idt_vectoring(vcpu);

vmx(vcpu, exit_qualification).raw = vmread(
vcpu, VM_EXIT_INFO_QUALIFICATION);
vmx(vcpu, exit_intr_info).raw = vmread(
vcpu, VM_EXIT_INFO_INTERRUPT_INFO);
vmx(vcpu, exit_exception_error_code) = vmread(
vcpu, VM_EXIT_INFO_EXCEPTION_ERROR_CODE);
vmx(vcpu, exit_idt_vectoring) = vmread(
vcpu, VM_EXIT_INFO_IDT_VECTORING);
vmx(vcpu, exit_instr_length) = vmread(
vcpu, VM_EXIT_INFO_INSTRUCTION_LENGTH);
vmx(vcpu, exit_gpa) = vmread(
vcpu, VM_EXIT_INFO_GUEST_PHYSICAL_ADDRESS);
vmx(vcpu, interruptibility_state).raw = vmread(
vcpu, GUEST_INTERRUPTIBILITY);

state->_rflags = vmread(vcpu, GUEST_RFLAGS);
state->_rsp = vmread(vcpu, GUEST_RSP);
VMREAD_SEG(vcpu, CS, state->_cs);
VMREAD_SEG(vcpu, DS, state->_ds);
VMREAD_SEG(vcpu, ES, state->_es);
vmread_cr(vcpu);

if (vcpu->nr_pending_intrs > 0 || hax_intr_is_blocked(vcpu))
htun->ready_for_interrupt_injection = 0;
else
htun->ready_for_interrupt_injection = 1;

vcpu->cur_state = GS_STALE;
vmcs_err = put_vmcs(vcpu, &flags);
if (vmcs_err) {
Expand Down Expand Up @@ -647,11 +559,11 @@ static void cpu_vmentry_failed(struct vcpu_t *vcpu, vmx_result_t result)
uint64_t error, reason;

hax_error("VM entry failed: RIP=%08lx\n",
(mword)vmread(vcpu, GUEST_RIP));
(mword)vmcs_read(vcpu, GUEST_RIP));

//dump_vmcs();

reason = vmread(vcpu, VM_EXIT_INFO_REASON);
reason = vmcs_read(vcpu, VM_EXIT_INFO_REASON);
if (result == VMX_FAIL_VALID) {
error = vmread(vcpu, VMX_INSTRUCTION_ERROR_CODE);
hax_error("VMfailValid. Prev exit: %llx. Error code: %llu (%s)\n",
Expand Down
52 changes: 13 additions & 39 deletions core/include/vcpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,40 +68,6 @@ struct cvtlb {
struct hax_mmu;
struct per_cpu_data;

struct vcpu_vmx_data {
uint32_t pin_ctls_base;
uint32_t pcpu_ctls_base;
uint32_t scpu_ctls_base;
uint32_t entry_ctls_base;
uint32_t exc_bitmap_base;
uint32_t exit_ctls_base;

uint32_t pin_ctls;
uint32_t pcpu_ctls;
uint32_t scpu_ctls;
uint32_t entry_ctls;
uint32_t exc_bitmap;
uint32_t exit_ctls;

uint64_t cr0_mask, cr0_shadow;
uint64_t cr4_mask, cr4_shadow;
uint32_t entry_exception_vector;
uint32_t entry_exception_error_code;

uint32_t exit_exception_error_code;
interruption_info_t exit_intr_info;
interruption_info_t entry_intr_info;
uint32_t exit_idt_vectoring;
uint32_t exit_instr_length;
uint32_t entry_instr_length;

exit_reason_t exit_reason;
exit_qualification_t exit_qualification;
interruptibility_state_t interruptibility_state;

uint64_t exit_gpa;
};

/* Information saved by instruction decoder and used by post-MMIO handler */
struct vcpu_post_mmio {
enum {
Expand Down Expand Up @@ -188,10 +154,6 @@ struct vcpu_t {
#define GS_STALE 0
#define GS_VALID 1
uint64_t cur_state : 1;
uint64_t vmcs_pending : 1;
uint64_t vmcs_pending_entry_error_code : 1;
uint64_t vmcs_pending_entry_instr_length : 1;
uint64_t vmcs_pending_entry_intr_info : 1;
AlexAltea marked this conversation as resolved.
Show resolved Hide resolved
uint64_t vmcs_pending_guest_cr3 : 1;
uint64_t debug_control_dirty : 1;
uint64_t dr_dirty : 1;
Expand All @@ -200,7 +162,6 @@ struct vcpu_t {
uint64_t fs_base_dirty : 1;
uint64_t interruptibility_dirty : 1;
uint64_t pcpu_ctls_dirty : 1;
uint64_t padding : 46;
};

/* For TSC offseting feature*/
Expand Down Expand Up @@ -293,4 +254,17 @@ bool vcpu_is_panic(struct vcpu_t *vcpu);
void hax_panic_vcpu(struct vcpu_t *v, char *fmt, ...);
#endif

// Extension-specific operations

mword vcpu_get_cr0(struct vcpu_t *vcpu);
mword vcpu_get_cr3(struct vcpu_t *vcpu);
mword vcpu_get_cr4(struct vcpu_t *vcpu);
mword vcpu_get_rflags(struct vcpu_t *vcpu);
mword vcpu_get_rsp(struct vcpu_t *vcpu);
mword vcpu_get_rip(struct vcpu_t *vcpu);
uint16_t vcpu_get_seg_selector(struct vcpu_t *vcpu, int seg);
mword vcpu_get_seg_base(struct vcpu_t *vcpu, int seg);
uint32_t vcpu_get_seg_limit(struct vcpu_t *vcpu, int seg);
uint32_t vcpu_get_seg_ar(struct vcpu_t *vcpu, int seg);

#endif // HAX_CORE_VCPU_H_
Loading