blob: 2bab43e070752831b9d006f163acdc4476b0ab9d [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* 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(DATA61_GPL)
*/
#pragma once
#include <stdlib.h>
#include <sel4/sel4.h>
#include "vmm/vmcs.h"
#include "vmm/platform/vmcs.h"
typedef struct guest_cr_virt_state {
/* mask represents bits that are owned by us, the host */
unsigned int cr0_mask;
unsigned int cr4_mask;
/* the shadow represents what the values of our owned bits should be seen as by the guest.
* i.e. the value they set it to */
unsigned int cr0_shadow;
unsigned int cr4_shadow;
/* for any bits owned by us, this represents what those bits should actually be set to */
unsigned int cr0_host_bits;
unsigned int cr4_host_bits;
/* the raw cr3 is only valid if we're trapping guest accesses to cr3, which we
* only do if the guest has not yet enabled paging for itself. If the guest has
* enabled paging then the value should be retrieved from the guest machine state */
uint32_t cr3_guest;
} guest_cr_virt_state_t;
typedef struct guest_exit_information {
bool in_exit;
unsigned int reason;
unsigned int qualification;
unsigned int instruction_length;
unsigned int guest_physical;
} guest_exit_information_t;
typedef enum machine_state_status {
/* Unknown means guest has run and we have no idea if
* our value is up to date */
machine_state_unknown = 0,
/* Valid means the value we have is up to date and we
* have not modified it with respect to what the guest
* believes */
machine_state_valid,
/* As part of the virtualization we have modified the value
* and this needs to be sycned back to the guest */
machine_state_modified
} machine_state_status_t;
#define MACHINE_STATE(type, name) machine_state_status_t name##_status; type name
#define MACHINE_STATE_READ(name, value) do { assert(name##_status == machine_state_unknown); name##_status = machine_state_valid; name = (value);} while(0)
#define MACHINE_STATE_DIRTY(name) do { name##_status = machine_state_modified; } while(0)
#define MACHINE_STATE_SYNC(name) do { assert(name##_status == machine_state_modified); name##_status = machine_state_valid; } while(0)
#define MACHINE_STATE_INVAL(name) do { assert(name##_status != machine_state_modified); name##_status = machine_state_unknown; } while(0)
#define MACHINE_STATE_SYNC_INVAL(name) do { MACHINE_STATE_SYNC(name); MACHINE_STATE_INVAL(name); } while(0)
#define IS_MACHINE_STATE_VALID(name) (name##_status == machine_state_valid)
#define IS_MACHINE_STATE_UNKNOWN(name) (name##_status == machine_state_unknown)
#define IS_MACHINE_STATE_MODIFIED(name) (name##_status == machine_state_modified)
typedef struct guest_machine_state {
MACHINE_STATE(seL4_VCPUContext, context);
MACHINE_STATE(unsigned int, cr0);
MACHINE_STATE(unsigned int, cr3);
MACHINE_STATE(unsigned int, cr4);
MACHINE_STATE(unsigned int, rflags);
MACHINE_STATE(unsigned int, guest_interruptibility);
MACHINE_STATE(unsigned int, idt_base);
MACHINE_STATE(unsigned int, idt_limit);
MACHINE_STATE(unsigned int, gdt_base);
MACHINE_STATE(unsigned int, gdt_limit);
MACHINE_STATE(unsigned int, cs_selector);
MACHINE_STATE(unsigned int, entry_exception_error_code);
/* This is state that we set on VMentry and get back on
* a vmexit, therefore it is always valid and correct */
unsigned int eip;
unsigned int control_entry;
unsigned int control_ppc;
} guest_machine_state_t;
/* Define the seL4_UserContext layout so we can treat it as an array */
#define USER_CONTEXT_EAX 0
#define USER_CONTEXT_EBX 1
#define USER_CONTEXT_ECX 2
#define USER_CONTEXT_EDX 3
#define USER_CONTEXT_ESI 4
#define USER_CONTEXT_EDI 5
#define USER_CONTEXT_EBP 6
typedef struct guest_virt_state {
guest_cr_virt_state_t cr;
/* are we hlt'ed waiting for an interrupted */
int interrupt_halt;
} guest_virt_state_t;
typedef struct guest_state {
/* Guest machine state. Bits of this may or may not be valid.
* This is all state that the guest itself observes and modifies.
* Either explicitly (registers) or implicitly (interruptability state) */
guest_machine_state_t machine;
/* Internal state that we use for handling guest state virtualization. */
guest_virt_state_t virt;
/* Information relating specifically to a guest exist, that is generated
* as a result of the exit */
guest_exit_information_t exit;
} guest_state_t;
static inline bool vmm_guest_state_no_modified(guest_state_t *gs) {
return !(
IS_MACHINE_STATE_MODIFIED(gs->machine.context) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.cr0) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.cr3) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.cr4) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.rflags) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.guest_interruptibility) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.idt_base) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.idt_limit) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.gdt_base) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.gdt_limit) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.cs_selector) ||
IS_MACHINE_STATE_MODIFIED(gs->machine.entry_exception_error_code)
);
}
static inline void vmm_guest_state_invalidate_all(guest_state_t *gs) {
MACHINE_STATE_INVAL(gs->machine.context);
MACHINE_STATE_INVAL(gs->machine.cr0);
MACHINE_STATE_INVAL(gs->machine.cr3);
MACHINE_STATE_INVAL(gs->machine.cr4);
MACHINE_STATE_INVAL(gs->machine.rflags);
MACHINE_STATE_INVAL(gs->machine.guest_interruptibility);
MACHINE_STATE_INVAL(gs->machine.idt_base);
MACHINE_STATE_INVAL(gs->machine.idt_limit);
MACHINE_STATE_INVAL(gs->machine.gdt_base);
MACHINE_STATE_INVAL(gs->machine.gdt_limit);
MACHINE_STATE_INVAL(gs->machine.cs_selector);
MACHINE_STATE_INVAL(gs->machine.entry_exception_error_code);
}
/* get */
static inline uint32_t vmm_read_user_context(guest_state_t *gs, int reg) {
assert(!IS_MACHINE_STATE_UNKNOWN(gs->machine.context));
assert(reg >= 0 && reg <= USER_CONTEXT_EBP);
return (&gs->machine.context.eax)[reg];
}
static inline unsigned int vmm_guest_state_get_eip(guest_state_t *gs) {
return gs->machine.eip;
}
static inline unsigned int vmm_guest_state_get_cr0(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_UNKNOWN(gs->machine.cr0)) {
MACHINE_STATE_READ(gs->machine.cr0, vmm_vmcs_read(vcpu, VMX_GUEST_CR0));
}
return gs->machine.cr0;
}
static inline unsigned int vmm_guest_state_get_cr3(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_UNKNOWN(gs->machine.cr3)) {
MACHINE_STATE_READ(gs->machine.cr3, vmm_vmcs_read(vcpu, VMX_GUEST_CR3));
}
return gs->machine.cr3;
}
static inline unsigned int vmm_guest_state_get_cr4(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_UNKNOWN(gs->machine.cr4)) {
MACHINE_STATE_READ(gs->machine.cr4, vmm_vmcs_read(vcpu, VMX_GUEST_CR4));
}
return gs->machine.cr4;
}
static inline unsigned int vmm_guest_state_get_rflags(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_UNKNOWN(gs->machine.rflags)) {
MACHINE_STATE_READ(gs->machine.rflags, vmm_vmcs_read(vcpu, VMX_GUEST_RFLAGS));
}
return gs->machine.rflags;
}
static inline unsigned int vmm_guest_state_get_interruptibility(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_UNKNOWN(gs->machine.guest_interruptibility)) {
MACHINE_STATE_READ(gs->machine.guest_interruptibility, vmm_vmcs_read(vcpu, VMX_GUEST_INTERRUPTABILITY));
}
return gs->machine.guest_interruptibility;
}
static inline unsigned int vmm_guest_state_get_control_entry(guest_state_t *gs) {
return gs->machine.control_entry;
}
static inline unsigned int vmm_guest_state_get_control_ppc(guest_state_t *gs) {
return gs->machine.control_ppc;
}
static inline unsigned int vmm_guest_state_get_idt_base(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_UNKNOWN(gs->machine.idt_base)) {
MACHINE_STATE_READ(gs->machine.idt_base, vmm_vmcs_read(vcpu, VMX_GUEST_IDTR_BASE));
}
return gs->machine.idt_base;
}
static inline unsigned int vmm_guest_state_get_idt_limit(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_UNKNOWN(gs->machine.idt_limit)) {
MACHINE_STATE_READ(gs->machine.idt_limit, vmm_vmcs_read(vcpu, VMX_GUEST_IDTR_LIMIT));
}
return gs->machine.idt_limit;
}
static inline unsigned int vmm_guest_state_get_gdt_base(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_UNKNOWN(gs->machine.gdt_base)) {
MACHINE_STATE_READ(gs->machine.gdt_base, vmm_vmcs_read(vcpu, VMX_GUEST_GDTR_BASE));
}
return gs->machine.gdt_base;
}
static inline unsigned int vmm_guest_state_get_gdt_limit(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_UNKNOWN(gs->machine.gdt_limit)) {
MACHINE_STATE_READ(gs->machine.gdt_limit, vmm_vmcs_read(vcpu, VMX_GUEST_GDTR_LIMIT));
}
return gs->machine.gdt_limit;
}
static inline unsigned int vmm_guest_state_get_cs_selector(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_UNKNOWN(gs->machine.cs_selector)) {
MACHINE_STATE_READ(gs->machine.cs_selector, vmm_vmcs_read(vcpu, VMX_GUEST_CS_SELECTOR));
}
return gs->machine.cs_selector;
}
/* set */
static inline void vmm_set_user_context(guest_state_t *gs, int reg, uint32_t val) {
MACHINE_STATE_DIRTY(gs->machine.context);
assert(reg >= 0 && reg <= USER_CONTEXT_EBP);
(&gs->machine.context.eax)[reg] = val;
}
static inline void vmm_guest_state_set_eip(guest_state_t *gs, unsigned int val) {
gs->machine.eip = val;
}
static inline void vmm_guest_state_set_cr0(guest_state_t *gs, unsigned int val) {
MACHINE_STATE_DIRTY(gs->machine.cr0);
gs->machine.cr0 = val;
}
static inline void vmm_guest_state_set_cr3(guest_state_t *gs, unsigned int val) {
MACHINE_STATE_DIRTY(gs->machine.cr3);
gs->machine.cr3 = val;
}
static inline void vmm_guest_state_set_cr4(guest_state_t *gs, unsigned int val) {
MACHINE_STATE_DIRTY(gs->machine.cr4);
gs->machine.cr4 = val;
}
static inline void vmm_guest_state_set_control_entry(guest_state_t *gs, unsigned int val) {
gs->machine.control_entry = val;
}
static inline void vmm_guest_state_set_control_ppc(guest_state_t *gs, unsigned int val) {
gs->machine.control_ppc = val;
}
static inline void vmm_guest_state_set_idt_base(guest_state_t *gs, unsigned int val) {
MACHINE_STATE_DIRTY(gs->machine.idt_base);
gs->machine.idt_base = val;
}
static inline void vmm_guest_state_set_idt_limit(guest_state_t *gs, unsigned int val) {
MACHINE_STATE_DIRTY(gs->machine.idt_limit);
gs->machine.idt_limit = val;
}
static inline void vmm_guest_state_set_gdt_base(guest_state_t *gs, unsigned int val) {
MACHINE_STATE_DIRTY(gs->machine.gdt_base);
gs->machine.gdt_base = val;
}
static inline void vmm_guest_state_set_gdt_limit(guest_state_t *gs, unsigned int val) {
MACHINE_STATE_DIRTY(gs->machine.gdt_limit);
gs->machine.gdt_limit = val;
}
static inline void vmm_guest_state_set_cs_selector(guest_state_t *gs, unsigned int val) {
MACHINE_STATE_DIRTY(gs->machine.cs_selector);
gs->machine.cs_selector = val;
}
static inline void vmm_guest_state_set_entry_exception_error_code(guest_state_t *gs, unsigned int val) {
MACHINE_STATE_DIRTY(gs->machine.entry_exception_error_code);
gs->machine.entry_exception_error_code = val;
}
/* sync */
static inline void vmm_guest_state_sync_cr0(guest_state_t *gs, seL4_CPtr vcpu) {
if(IS_MACHINE_STATE_MODIFIED(gs->machine.cr0)) {
vmm_vmcs_write(vcpu, VMX_GUEST_CR0, gs->machine.cr0);
MACHINE_STATE_SYNC(gs->machine.cr0);
}
}
static inline void vmm_guest_state_sync_cr3(guest_state_t *gs, seL4_CPtr vcpu) {
if(IS_MACHINE_STATE_MODIFIED(gs->machine.cr3)) {
vmm_vmcs_write(vcpu, VMX_GUEST_CR3, gs->machine.cr3);
MACHINE_STATE_SYNC(gs->machine.cr3);
}
}
static inline void vmm_guest_state_sync_cr4(guest_state_t *gs, seL4_CPtr vcpu) {
if(IS_MACHINE_STATE_MODIFIED(gs->machine.cr4)) {
vmm_vmcs_write(vcpu, VMX_GUEST_CR4, gs->machine.cr4);
MACHINE_STATE_SYNC(gs->machine.cr4);
}
}
static inline void vmm_guest_state_sync_idt_base(guest_state_t *gs, seL4_CPtr vcpu) {
if(IS_MACHINE_STATE_MODIFIED(gs->machine.idt_base)) {
vmm_vmcs_write(vcpu, VMX_GUEST_IDTR_BASE, gs->machine.idt_base);
MACHINE_STATE_SYNC(gs->machine.idt_base);
}
}
static inline void vmm_guest_state_sync_idt_limit(guest_state_t *gs, seL4_CPtr vcpu) {
if(IS_MACHINE_STATE_MODIFIED(gs->machine.idt_limit)) {
vmm_vmcs_write(vcpu, VMX_GUEST_IDTR_LIMIT, gs->machine.idt_limit);
MACHINE_STATE_SYNC(gs->machine.idt_limit);
}
}
static inline void vmm_guest_state_sync_gdt_base(guest_state_t *gs, seL4_CPtr vcpu) {
if(IS_MACHINE_STATE_MODIFIED(gs->machine.gdt_base)) {
vmm_vmcs_write(vcpu, VMX_GUEST_GDTR_BASE, gs->machine.gdt_base);
MACHINE_STATE_SYNC(gs->machine.gdt_base);
}
}
static inline void vmm_guest_state_sync_gdt_limit(guest_state_t *gs, seL4_CPtr vcpu) {
if(IS_MACHINE_STATE_MODIFIED(gs->machine.gdt_limit)) {
vmm_vmcs_write(vcpu, VMX_GUEST_GDTR_LIMIT, gs->machine.gdt_limit);
MACHINE_STATE_SYNC(gs->machine.gdt_limit);
}
}
static inline void vmm_guest_state_sync_cs_selector(guest_state_t *gs, seL4_CPtr vcpu) {
if(IS_MACHINE_STATE_MODIFIED(gs->machine.cs_selector)) {
vmm_vmcs_write(vcpu, VMX_GUEST_CS_SELECTOR, gs->machine.cs_selector);
MACHINE_STATE_SYNC(gs->machine.cs_selector);
}
}
static inline void vmm_guest_state_sync_entry_exception_error_code(guest_state_t *gs, seL4_CPtr vcpu) {
if (IS_MACHINE_STATE_MODIFIED(gs->machine.entry_exception_error_code)) {
vmm_vmcs_write(vcpu, VMX_CONTROL_ENTRY_EXCEPTION_ERROR_CODE, gs->machine.entry_exception_error_code);
MACHINE_STATE_SYNC(gs->machine.entry_exception_error_code);
}
}
/* Exit */
static inline unsigned int vmm_guest_exit_get_reason(guest_state_t *gs) {
assert(gs->exit.in_exit);
return gs->exit.reason;
}
static inline unsigned int vmm_guest_exit_get_physical(guest_state_t *gs) {
assert(gs->exit.in_exit);
return gs->exit.guest_physical;
}
static inline unsigned int vmm_guest_exit_get_int_len(guest_state_t *gs) {
assert(gs->exit.in_exit);
return gs->exit.instruction_length;
}
static inline unsigned int vmm_guest_exit_get_qualification(guest_state_t *gs) {
assert(gs->exit.in_exit);
return gs->exit.qualification;
}
static inline void vmm_guest_exit_next_instruction(guest_state_t *gs, seL4_CPtr vcpu) {
vmm_guest_state_set_eip(gs, vmm_guest_state_get_eip(gs) + vmm_guest_exit_get_int_len(gs));
}