blob: 69e879a5d907718d9dede713096cd110a99c6875 [file] [log] [blame]
/*
* Copyright 2014, NICTA
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(NICTA_GPL)
*/
/*vm exit handler for control register access*/
#include <stdio.h>
#include <stdlib.h>
#include <sel4/sel4.h>
#include <utils/util.h>
#include "vmm/debug.h"
#include "vmm/processor/platfeature.h"
#include "vmm/platform/vmcs.h"
#include "vmm/vmm.h"
static int vmm_cr_set_cr0(vmm_vcpu_t *vcpu, unsigned int value) {
if (value & CR0_RESERVED_BITS)
return -1;
/*read the cr0 value*/
int set = vmm_guest_state_get_cr0(&vcpu->guest_state, vcpu->guest_vcpu);
DPRINTF(4, "cr0 val 0x%x guest set 0x%x\n", set, value);
/*set the cr0 value with the shadow value*/
value |= (vcpu->guest_state.virt.cr.cr0_mask & vcpu->guest_state.virt.cr.cr0_shadow);
if (value == set)
return 0;
vmm_guest_state_set_cr0(&vcpu->guest_state, value);
return 0;
}
static int vmm_cr_set_cr3(vmm_vcpu_t *vcpu, unsigned int value) {
assert(!"Should not get cr3 access");
return -1;
}
static int vmm_cr_get_cr3(vmm_vcpu_t *vcpu, unsigned int *value) {
*value = vmm_guest_state_get_cr3(&vcpu->guest_state, vcpu->guest_vcpu);
return 0;
}
static int vmm_cr_set_cr4(vmm_vcpu_t *vcpu, unsigned int value) {
if (value & CR4_RESERVED_BITS)
return -1;
int set = vmm_guest_state_get_cr4(&vcpu->guest_state, vcpu->guest_vcpu);
DPRINTF(4, "cr4 val 0x%x guest set 0x%x\n", set, value);
/*set the cr0 value with the shadow value*/
value |= (vcpu->guest_state.virt.cr.cr4_shadow & vcpu->guest_state.virt.cr.cr4_mask);
if (set == value)
return 0;
vmm_guest_state_set_cr4(&vcpu->guest_state, value);
return 0;
}
static int vmm_cr_clts(vmm_vcpu_t *vcpu) {
ZF_LOGI("Ignoring call of clts");
return -1;
}
static int vmm_cr_lmsw(vmm_vcpu_t *vcpu, unsigned int value) {
ZF_LOGI("Ignoring call of lmsw");
return -1;
}
/* Convert exit regs to seL4 user context */
static int crExitRegs[] = {
USER_CONTEXT_EAX,
USER_CONTEXT_ECX,
USER_CONTEXT_EDX,
USER_CONTEXT_EBX,
/*USER_CONTEXT_ESP*/-1,
USER_CONTEXT_EBP,
USER_CONTEXT_ESI,
USER_CONTEXT_EDI
};
int vmm_cr_access_handler(vmm_vcpu_t *vcpu) {
unsigned int exit_qualification, val;
int cr, reg, ret = -1;
exit_qualification = vmm_guest_exit_get_qualification(&vcpu->guest_state);
cr = exit_qualification & 15;
reg = (exit_qualification >> 8) & 15;
switch ((exit_qualification >> 4) & 3) {
case 0: /* mov to cr */
if (crExitRegs[reg] < 0) {
return -1;
}
val = vmm_read_user_context(&vcpu->guest_state, crExitRegs[reg]);
switch (cr) {
case 0:
ret = vmm_cr_set_cr0(vcpu, val);
break;
case 3:
ret = vmm_cr_set_cr3(vcpu, val);
break;
case 4:
ret = vmm_cr_set_cr4(vcpu, val);
break;
case 8:
#if 0
u8 cr8_prev = kvm_get_cr8(vcpu);
u8 cr8 = kvm_register_read(vcpu, reg);
err = kvm_set_cr8(vcpu, cr8);
kvm_complete_insn_gp(vcpu, err);
if (irqchip_in_kernel(vcpu->kvm))
return 1;
if (cr8_prev <= cr8)
return 1;
vcpu->run->exit_reason = KVM_EXIT_SET_TPR;
return 0;
#endif
default:
DPRINTF(4, "unhandled control register: op %d cr %d\n",
(int)(exit_qualification >> 4) & 3, cr);
break;
}
break;
case 1: /*mov from cr*/
switch (cr) {
case 3:
ret = vmm_cr_get_cr3(vcpu, &val);
if (!ret)
vmm_set_user_context(&vcpu->guest_state, crExitRegs[reg], val);
break;
case 8:
#if 0
val = kvm_get_cr8(vcpu);
kvm_register_write(vcpu, reg, val);
trace_kvm_cr_read(cr, val);
skip_emulated_instruction(vcpu);
return 1;
#endif
default:
DPRINTF(4, "unhandled control register: op %d cr %d\n",
(int)(exit_qualification >> 4) & 3, cr);
break;
}
break;
case 2: /* clts */
ret = vmm_cr_clts(vcpu);
break;
case 3: /* lmsw */
val = (exit_qualification >> LMSW_SOURCE_DATA_SHIFT) & 0x0f;
ret = vmm_cr_lmsw(vcpu, val);
break;
default:
DPRINTF(4, "unhandled control register: op %d cr %d\n",
(int)(exit_qualification >> 4) & 3, cr);
break;
}
if (!ret) {
vmm_guest_exit_next_instruction(&vcpu->guest_state, vcpu->guest_vcpu);
}
return ret;
}