vbox: Implement hwaccl support for Muen

* Implement VMMR0_DO_VMMR0_INIT operation for Muen

   - Indicate VT-x support
   - Enable unrestricted guest mode
   - Set CR[0|4] mask to enable masking of guest CR0.[NE,NW,CD] and
     CR4.VMXE bits.

 * Implement VMMR0_DO_GVMM_CREATE_VM on Muen

   Return error if trying to create SMP VM as VMs with multiple CPUs are
   currently not supported on hw_x86_64_muen.

 * Add Muen-specific Mem_region type

   On hw_x86_64_muen the guest memory layout is static, thus regions are
   handed out from an array of memory regions.

   Use sinfo API to calculate the base address of the VM RAM physical
   0x0 region. This allows to dynamically modify the VM RAM size by
   adjusting the Muen policy and Genode vbox files accordingly.

   Zeroize all memory regions apart from VM Ram since Virtualbox expects
   these regions to be cleared.

 * Add Muen subject state struct

   The subject state encompasses the guest VM machine state that is
   transfered between Virtualbox and hardware accelerated execution on
   Muen.

 * Add Muen-specific Vm_handler class
 * Use Vm_handler to run VM
 * Instruct recompiler to flush its code cache
 * Copy the Muen subject state to/from the Vbox PCPUMCTX.
 * Use the VM interruptibility state to inform the recompiler whether
   interrupts are currently inhibited.
 * Explicitly handle control register access

   If a VM-exit occurs due to a control register access, handle it and
   directly continue hardware accelerated execution of guest VM.

   Note: On NOVA control register accesses are handled by the kernel [1].

   [1] - https://github.com/alex-ab/NOVA/blob/master/src/ec_vmx.cpp#L106

 * Reset guest interruptibility state

   Assert that interrupts are not inhibited in the Virtualbox machine
   state and clear Blocking-by-[STI|MOV to SS] guest interruptibility
   flags prior to running a guest VM in hwaccel mode.

 * Set return code depending on exit reason

   Do not unconditionally emulate the next instruction on VM exit. This
   makes sharing the VM FPU state with Virtualbox unnecessary, as FPU
   instructions are not emulated by the recompiler any longer.
   Also, assert that the FPU has not been used by the recompiler

 * Inject pending guest VM interrupts on Muen

   Use mapped subject pending interrupts page of guest VM to perform
   interrupt injection. IRQs are transferred from the Virtualbox trap
   manager state to the pending interrupts region for injection. If an
   IRQ remains pending upon returning to the recompiler, it is copied
   back to the trap manager state and cleared in the subject interrupts
   region.

 * Inform recompiler about changed SYSENTER_[CS|EIP|ESP] values,
   otherwise values set while running the guest VM hardware accelerated
   may get lost.

 * Implement genode_cpu_hz() on Muen

   Determine the CPU frequency dynamically using the sinfo API.

Issue #2016
This commit is contained in:
Adrian-Ken Rueegsegger 2015-06-02 19:46:50 +02:00 committed by Norman Feske
parent 738b01d37a
commit e3fbeeb25e
10 changed files with 1232 additions and 3 deletions

View File

@ -0,0 +1,12 @@
include $(REP_DIR)/lib/mk/virtualbox-common.inc
SRC_CC = pgm.cc sup.cc
INC_DIR += $(call select_from_repositories,src/lib/libc)
INC_DIR += $(VBOX_DIR)/VMM/include
INC_DIR += $(REP_DIR)/src/virtualbox
INC_DIR += $(REP_DIR)/src/virtualbox/spec/muen
vpath pgm.cc $(REP_DIR)/src/virtualbox/
vpath sup.cc $(REP_DIR)/src/virtualbox/spec/muen/

View File

@ -7,6 +7,7 @@ INC_DIR += $(call select_from_repositories,src/lib/pthread)
INC_DIR += $(VBOX_DIR)/VMM/include
INC_DIR += $(REP_DIR)/src/virtualbox
INC_DIR += $(REP_DIR)/src/virtualbox/spec/nova
vpath pgm.cc $(REP_DIR)/src/virtualbox/
vpath sup.cc $(REP_DIR)/src/virtualbox/spec/nova/

View File

@ -5,6 +5,7 @@ SRC_CC = pgm.cc sup.cc
INC_DIR += $(call select_from_repositories,src/lib/libc)
INC_DIR += $(VBOX_DIR)/VMM/include
INC_DIR += $(REP_DIR)/src/virtualbox
INC_DIR += $(REP_DIR)/src/virtualbox/accloff
vpath pgm.cc $(REP_DIR)/src/virtualbox/
vpath sup.cc $(REP_DIR)/src/virtualbox/accloff/

View File

@ -13,8 +13,8 @@
* version 2.
*/
#ifndef _MEM_REGION_H_
#define _MEM_REGION_H_
#ifndef _VIRTUALBOX__ACCLOFF__MEM_REGION_H_
#define _VIRTUALBOX__ACCLOFF__MEM_REGION_H_
/* Genode includes */
#include <util/list.h>
@ -72,4 +72,4 @@ struct Mem_region : public Genode::List<Mem_region>::Element,
T * local_addr() { return reinterpret_cast<T *>(_base); }
};
#endif /* _MEM_REGION_H_ */
#endif /* _VIRTUALBOX__ACCLOFF__MEM_REGION_H_ */

View File

@ -0,0 +1,78 @@
/*
* \brief Muen subject pending interrupt handling
* \author Adrian-Ken Rueegsegger
* \author Reto Buerki
* \date 2016-04-27
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _VIRTUALBOX__SPEC__MUEN__INTERRUPTS_H_
#define _VIRTUALBOX__SPEC__MUEN__INTERRUPTS_H_
/* Genode includes */
#include <base/stdint.h>
#include <os/attached_io_mem_dataspace.h>
namespace Genode
{
/**
* Vm execution handler.
*/
class Guest_interrupts;
}
class Genode::Guest_interrupts
{
private:
addr_t _base;
public:
Guest_interrupts (addr_t base) : _base(base) { }
/**
* Returns true if the bit corresponding to the specified IRQ is set.
*/
bool is_pending_interrupt(uint8_t irq)
{
bool result;
asm volatile ("bt %1, %2;"
"sbb %0, %0;"
: "=r" (result)
: "Ir" ((uint32_t)irq), "m" (*(char *)_base)
: "memory");
return result;
}
/**
* Set bit corresponding to given IRQ in pending interrupts region.
*/
void set_pending_interrupt(uint8_t irq)
{
asm volatile ("lock bts %1, %0"
: "+m" (*(char *)_base)
: "Ir" ((uint32_t)irq)
: "memory");
}
/**
* Clear bit corresponding to given IRQ in pending interrupts region.
*/
void clear_pending_interrupt(uint8_t irq)
{
asm volatile ("lock btr %1, %0"
: "+m" (*(char *)_base)
: "Ir" ((uint32_t)irq)
: "memory");
}
};
#endif /* _VIRTUALBOX__SPEC__MUEN__INTERRUPTS_H_ */

View File

@ -0,0 +1,135 @@
/*
* \brief Memory region types
* \author Norman Feske
* \author Adrian-Ken Rueegsegger
* \author Reto Buerki
* \date 2015-06-23
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _VIRTUALBOX__SPEC__MUEN__MEM_REGION_H_
#define _VIRTUALBOX__SPEC__MUEN__MEM_REGION_H_
/* Genode includes */
#include <base/env.h>
#include <base/lock.h>
#include <util/list.h>
#include <os/attached_io_mem_dataspace.h>
#include <rom_session/connection.h>
#include <muen/sinfo.h>
/* VirtualBox includes */
#include <VBox/vmm/pgm.h>
#include <VBox/vmm/pdmdev.h>
struct Mem_region;
struct Mem_region : Genode::List<Mem_region>::Element,
Genode::Attached_io_mem_dataspace
{
typedef Genode::Ram_session Ram_session;
typedef Genode::size_t size_t;
typedef Genode::addr_t addr_t;
PPDMDEVINS pDevIns;
unsigned const iRegion;
RTGCPHYS vm_phys;
PFNPGMR3PHYSHANDLER pfnHandlerR3;
void *pvUserR3;
PGMPHYSHANDLERTYPE enmType;
size_t region_size;
bool _clear;
addr_t _phys_base(size_t size)
{
struct Region_info
{
addr_t base;
size_t size;
};
static unsigned counter;
static Region_info regions[] = {
/* pcbios phys 0xe1000 */
{ 0x810000000, 0x1000 },
/* pcbios phys 0xf0000*/
{ 0x820000000, 0x10000 },
/* pcbios 0xffff0000 */
{ 0x830000000, 0x10000 },
/* VMMDev */
{ 0x840000000, 0x400000 },
/* VMMDev */
{ 0x850000000, 0x4000 },
/* vga */
{ 0x860000000, 0x8000000 },
/* vga phys 0xc0000 */
{ 0x870000000, 0x9000 },
/* acpi phys e0000 */
{ 0x880000000, 0x1000 },
{ 0x0, 0x0 },
};
Region_info cur_region;
if (!counter) {
Genode::Rom_connection sinfo_rom("subject_info_page");
Genode::Sinfo sinfo
((addr_t)Genode::env()->rm_session()->attach
(sinfo_rom.dataspace()));
struct Genode::Sinfo::Memregion_info region1, region4;
if (!sinfo.get_memregion_info("vm_ram_1", &region1)) {
PERR("Unable to retrieve vm_ram_1 region");
return 0;
}
if (!sinfo.get_memregion_info("vm_ram_4", &region4)) {
PERR("Unable to retrieve vm_ram_4 region");
return 0;
}
cur_region.base = region1.address;
cur_region.size = region4.address + region4.size - region1.address;
counter++;
_clear = false;
} else {
cur_region = regions[counter - 1];
if (cur_region.size == 0)
{
PERR("Region size is zero!!!");
return 0;
}
counter++;
_clear = true;
}
if (size > cur_region.size)
PERR("Size: 0x%zx, cur_region.size: 0x%zx", size, cur_region.size);
Assert(size <= cur_region.size);
return cur_region.base;
}
size_t size() const { return region_size; }
Mem_region(Ram_session &ram, size_t size, PPDMDEVINS pDevIns,
unsigned iRegion)
:
Attached_io_mem_dataspace(_phys_base(size), size),
pDevIns(pDevIns),
iRegion(iRegion),
vm_phys(0), pfnHandlerR3(0), pvUserR3(0),
region_size(size)
{
if (_clear)
Genode::memset(local_addr<char>(), 0, size);
}
};
#endif /* _VIRTUALBOX__SPEC__MUEN__MEM_REGION_H_ */

View File

@ -0,0 +1,765 @@
/*
* \brief Genode specific VirtualBox SUPLib supplements.
* \author Alexander Boettcher
* \author Adrian-Ken Rueegsegger
* \author Reto Buerki
* \date 2013-11-18
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/printf.h>
#include <timer_session/connection.h>
#include <os/attached_io_mem_dataspace.h>
#include <rom_session/connection.h>
#include <muen/sinfo.h>
/* VirtualBox includes */
#include "HMInternal.h" /* enable access to hm.s.* */
#include "CPUMInternal.h" /* enable access to cpum.s.* */
#include <VBox/vmm/rem.h>
#include <VBox/vmm/pdmapi.h>
#include <VBox/vmm/hm_vmx.h>
/* Genode's VirtualBox includes */
#include "sup.h"
#include "vcpu.h"
#include "vm_handler.h"
#include "vmm_memory.h"
#include "guest_interrupts.h"
/* Libc include */
#include <pthread.h>
enum {
VMCS_SEG_UNUSABLE = 0x10000,
INTERRUPT_STATE_NONE = 0U,
BLOCKING_BY_STI = 1U << 0,
BLOCKING_BY_MOV_SS = 1U << 1,
};
#define GENODE_READ_SELREG_REQUIRED(REG) \
(pCtx->REG.Sel != cur_state->REG.sel) || \
(pCtx->REG.ValidSel != cur_state->REG.sel) || \
(pCtx->REG.fFlags != CPUMSELREG_FLAGS_VALID) || \
(pCtx->REG.u32Limit != cur_state->REG.limit) || \
(pCtx->REG.u64Base != cur_state->REG.base) || \
(pCtx->REG.Attr.u != cur_state->REG.access)
#define GENODE_READ_SELREG(REG) \
pCtx->REG.Sel = cur_state->REG.sel; \
pCtx->REG.ValidSel = cur_state->REG.sel; \
pCtx->REG.fFlags = CPUMSELREG_FLAGS_VALID; \
pCtx->REG.u32Limit = cur_state->REG.limit; \
pCtx->REG.u64Base = cur_state->REG.base; \
pCtx->REG.Attr.u = cur_state->REG.access;
#define GENODE_ASSERT_SELREG(REG) \
Assert(pCtx->REG.Sel == cur_state->REG.sel); \
Assert(pCtx->REG.ValidSel == cur_state->REG.sel); \
Assert(pCtx->REG.fFlags == CPUMSELREG_FLAGS_VALID); \
Assert(pCtx->REG.u32Limit == cur_state->REG.limit); \
Assert(pCtx->REG.u64Base == cur_state->REG.base);
#define GENODE_WRITE_SELREG(REG) \
Assert(pCtx->REG.fFlags & CPUMSELREG_FLAGS_VALID); \
Assert(pCtx->REG.ValidSel == pCtx->REG.Sel); \
cur_state->REG.sel = pCtx->REG.Sel; \
cur_state->REG.limit = pCtx->REG.u32Limit; \
cur_state->REG.base = pCtx->REG.u64Base; \
cur_state->REG.access = pCtx->REG.Attr.u ? : VMCS_SEG_UNUSABLE
static Genode::Vm_handler vm_handler;
struct Subject_state *cur_state;
Genode::Guest_interrupts *guest_interrupts;
/**
* Return pointer to sinfo.
*/
static Genode::Sinfo * sinfo()
{
using namespace Genode;
static Sinfo *ptr;
if (!ptr) {
try {
static Rom_connection sinfo_rom("subject_info_page");
static Sinfo sinfo(
(addr_t)env()->rm_session()->attach(sinfo_rom.dataspace()));
ptr = &sinfo;
} catch (...) {
PERR("Unable to attach Sinfo ROM");
Assert(false);
}
}
return ptr;
}
/**
* Setup guest subject state.
*/
bool setup_subject_state()
{
using namespace Genode;
if (cur_state)
return true;
struct Sinfo::Memregion_info region;
if (!sinfo()->get_memregion_info("monitor_state", &region)) {
PERR("Unable to retrieve monitor state region");
return false;
}
try {
static Attached_io_mem_dataspace subject_ds(region.address,
region.size);
cur_state = subject_ds.local_addr<struct Subject_state>();
return true;
} catch (...) {
PERR("Unable to attach subject state I/O mem dataspace");
}
return false;
}
/**
* Setup guest interrupts page.
*/
bool setup_subject_interrupts()
{
using namespace Genode;
if (guest_interrupts)
return true;
struct Sinfo::Memregion_info region;
if (!sinfo()->get_memregion_info("monitor_interrupts", &region)) {
PERR("Unable to retrieve monitor interrupts region");
return false;
}
try {
static Attached_io_mem_dataspace subject_intrs(region.address,
region.size);
static Guest_interrupts g((addr_t)subject_intrs.local_addr<addr_t>());
guest_interrupts = &g;
return true;
} catch (...) {
PERR("Unable to attach subject interrupts I/O mem dataspace");
}
return false;
}
/**
* Returns the value of the register identified by reg.
* The register mapping is specified by Intel SDM Vol. 3C, table 27-3.
*/
inline uint64_t get_reg_val (struct Subject_state *cur_state, unsigned reg)
{
switch (reg) {
case 0:
return cur_state->Regs.Rax;
break;
case 1:
return cur_state->Regs.Rcx;
break;
case 2:
return cur_state->Regs.Rdx;
break;
case 3:
return cur_state->Regs.Rbx;
break;
case 4:
return cur_state->Rsp;
break;
case 5:
return cur_state->Regs.Rbp;
break;
case 6:
return cur_state->Regs.Rsi;
break;
case 7:
return cur_state->Regs.Rdi;
break;
default:
PERR("Invalid register %u", reg);
return 0;
}
}
/**
* Sets the control register identified by cr to the given value.
*/
inline bool set_cr(struct Subject_state *cur_state, unsigned cr, uint64_t value)
{
bool res = false;
switch (cr) {
case 0:
cur_state->Shadow_cr0 = value;
cur_state->Cr0 = value | 1 << 5;
cur_state->Cr0 &= ~(1 << 30 | 1 << 29);
res = true;
break;
case 2:
cur_state->Regs.Cr2 = value;
res = true;
break;
case 3:
cur_state->Cr3 = value;
res = true;
break;
case 4:
cur_state->Shadow_cr4 = value;
cur_state->Cr4 = value | 1 << 13;
res = true;
break;
default:
PERR("Invalid control register %u", cr);
res = false;
}
return res;
}
/**
* Handle control register access by evaluating the VM-exit qualification
* according to Intel SDM Vol. 3C, table 27-3.
*/
inline bool handle_cr(struct Subject_state *cur_state)
{
uint64_t qual = cur_state->Exit_qualification;
unsigned cr = qual & 0xf;
unsigned acc = (qual & 0x30) >> 4;
unsigned reg = (qual & 0xf00) >> 8;
bool res;
switch (acc) {
case 0: // MOV to CR
res = set_cr(cur_state, cr, get_reg_val(cur_state, reg));
break;
default:
PERR("Invalid control register %u access %u, reg %u", cr, acc, reg);
return false;
}
if (res)
cur_state->Rip += cur_state->Instruction_len;
return res;
}
inline void check_vm_state(PVMCPU pVCpu, struct Subject_state *cur_state)
{
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
Assert(cur_state->Rip == pCtx->rip);
Assert(cur_state->Rsp == pCtx->rsp);
Assert(cur_state->Regs.Rax == pCtx->rax);
Assert(cur_state->Regs.Rbx == pCtx->rbx);
Assert(cur_state->Regs.Rcx == pCtx->rcx);
Assert(cur_state->Regs.Rdx == pCtx->rdx);
Assert(cur_state->Regs.Rbp == pCtx->rbp);
Assert(cur_state->Regs.Rsi == pCtx->rsi);
Assert(cur_state->Regs.Rdi == pCtx->rdi);
Assert(cur_state->Regs.R08 == pCtx->r8);
Assert(cur_state->Regs.R09 == pCtx->r9);
Assert(cur_state->Regs.R10 == pCtx->r10);
Assert(cur_state->Regs.R11 == pCtx->r11);
Assert(cur_state->Regs.R12 == pCtx->r12);
Assert(cur_state->Regs.R13 == pCtx->r13);
Assert(cur_state->Regs.R14 == pCtx->r14);
Assert(cur_state->Regs.R15 == pCtx->r15);
Assert(cur_state->Rflags == pCtx->rflags.u);
Assert(cur_state->Sysenter_cs == pCtx->SysEnter.cs);
Assert(cur_state->Sysenter_eip == pCtx->SysEnter.eip);
Assert(cur_state->Sysenter_esp == pCtx->SysEnter.esp);
{
uint32_t val;
val = (cur_state->Shadow_cr0 & pVCpu->hm.s.vmx.u32CR0Mask);
val |= (cur_state->Cr0 & ~pVCpu->hm.s.vmx.u32CR0Mask);
Assert(pCtx->cr0 == val);
}
Assert(cur_state->Regs.Cr2 == pCtx->cr2);
Assert(cur_state->Cr3 == pCtx->cr3);
{
uint32_t val;
val = (cur_state->Shadow_cr4 & pVCpu->hm.s.vmx.u32CR4Mask);
val |= (cur_state->Cr4 & ~pVCpu->hm.s.vmx.u32CR4Mask);
Assert(pCtx->cr4 == val);
}
GENODE_ASSERT_SELREG(cs);
GENODE_ASSERT_SELREG(ss);
GENODE_ASSERT_SELREG(ds);
GENODE_ASSERT_SELREG(es);
GENODE_ASSERT_SELREG(fs);
GENODE_ASSERT_SELREG(gs);
Assert(cur_state->ldtr.sel == pCtx->ldtr.Sel);
Assert(cur_state->ldtr.limit == pCtx->ldtr.u32Limit);
Assert(cur_state->ldtr.base == pCtx->ldtr.u64Base);
if(cur_state->ldtr.sel != 0)
Assert(cur_state->ldtr.access == pCtx->ldtr.Attr.u);
Assert(pCtx->tr.Attr.u & X86_SEL_TYPE_SYS_TSS_BUSY_MASK);
{
Assert(cur_state->tr.sel == pCtx->tr.Sel);
Assert(cur_state->tr.limit == pCtx->tr.u32Limit);
Assert(cur_state->tr.base == pCtx->tr.u64Base);
Assert(cur_state->tr.access == pCtx->tr.Attr.u);
}
Assert(cur_state->idtr.limit == pCtx->idtr.cbIdt);
Assert(cur_state->idtr.base == pCtx->idtr.pIdt);
Assert(cur_state->gdtr.limit == pCtx->gdtr.cbGdt);
Assert(cur_state->gdtr.base == pCtx->gdtr.pGdt);
Assert(cur_state->Ia32_efer == CPUMGetGuestEFER(pVCpu));
}
inline bool has_pending_irq(PVMCPU pVCpu)
{
return TRPMHasTrap(pVCpu) ||
VMCPU_FF_IS_PENDING(pVCpu, (VMCPU_FF_INTERRUPT_APIC |
VMCPU_FF_INTERRUPT_PIC));
}
/**
* Return vector of currently pending IRQ.
*/
inline uint8_t get_irq(PVMCPU pVCpu)
{
int rc;
if (!TRPMHasTrap(pVCpu)) {
bool res = VMCPU_FF_TEST_AND_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_NMI);
Assert(!res);
if (VMCPU_FF_IS_PENDING(pVCpu, (VMCPU_FF_INTERRUPT_APIC |
VMCPU_FF_INTERRUPT_PIC))) {
uint8_t irq;
rc = PDMGetInterrupt(pVCpu, &irq);
Assert(RT_SUCCESS(rc));
rc = TRPMAssertTrap(pVCpu, irq, TRPM_HARDWARE_INT);
Assert(RT_SUCCESS(rc));
}
}
Assert(TRPMHasTrap(pVCpu));
uint8_t u8Vector;
TRPMEVENT enmType;
RTGCUINT u32ErrorCode;
rc = TRPMQueryTrapAll(pVCpu, &u8Vector, &enmType, 0, 0, 0);
AssertRC(rc);
Assert(enmType == TRPM_HARDWARE_INT);
Assert(u8Vector != X86_XCPT_NMI);
/* Clear the pending trap. */
rc = TRPMResetTrap(pVCpu);
AssertRC(rc);
return u8Vector;
}
int SUPR3QueryVTxSupported(void) { return VINF_SUCCESS; }
int SUPR3CallVMMR0Fast(PVMR0 pVMR0, unsigned uOperation, VMCPUID idCpu)
{
switch (uOperation) {
case SUP_VMMR0_DO_HM_RUN:
VM * pVM = reinterpret_cast<VM *>(pVMR0);
PVMCPU pVCpu = &pVM->aCpus[idCpu];
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
int rc;
Assert(!(pVCpu->cpum.s.fUseFlags & (CPUM_USED_FPU | CPUM_USED_FPU_SINCE_REM | CPUM_SYNC_FPU_STATE)));
if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS) && (cur_state->Intr_state & 3))
cur_state->Intr_state &= ~3U;
cur_state->Rip = pCtx->rip;
cur_state->Rsp = pCtx->rsp;
cur_state->Regs.Rax = pCtx->rax;
cur_state->Regs.Rbx = pCtx->rbx;
cur_state->Regs.Rcx = pCtx->rcx;
cur_state->Regs.Rdx = pCtx->rdx;
cur_state->Regs.Rbp = pCtx->rbp;
cur_state->Regs.Rsi = pCtx->rsi;
cur_state->Regs.Rdi = pCtx->rdi;
cur_state->Regs.R08 = pCtx->r8;
cur_state->Regs.R09 = pCtx->r9;
cur_state->Regs.R10 = pCtx->r10;
cur_state->Regs.R11 = pCtx->r11;
cur_state->Regs.R12 = pCtx->r12;
cur_state->Regs.R13 = pCtx->r13;
cur_state->Regs.R14 = pCtx->r14;
cur_state->Regs.R15 = pCtx->r15;
cur_state->Rflags = pCtx->rflags.u;
cur_state->Sysenter_cs = pCtx->SysEnter.cs;
cur_state->Sysenter_eip = pCtx->SysEnter.eip;
cur_state->Sysenter_esp = pCtx->SysEnter.esp;
set_cr(cur_state, 0, pCtx->cr0);
set_cr(cur_state, 2, pCtx->cr2);
set_cr(cur_state, 3, pCtx->cr3);
set_cr(cur_state, 4, pCtx->cr4);
GENODE_WRITE_SELREG(cs);
GENODE_WRITE_SELREG(ss);
GENODE_WRITE_SELREG(ds);
GENODE_WRITE_SELREG(es);
GENODE_WRITE_SELREG(fs);
GENODE_WRITE_SELREG(gs);
if (pCtx->ldtr.Sel == 0) {
cur_state->ldtr.sel = 0;
cur_state->ldtr.limit = 0;
cur_state->ldtr.base = 0;
cur_state->ldtr.access = 0x82;
} else {
cur_state->ldtr.sel = pCtx->ldtr.Sel;
cur_state->ldtr.limit = pCtx->ldtr.u32Limit;
cur_state->ldtr.base = pCtx->ldtr.u64Base;
cur_state->ldtr.access = pCtx->ldtr.Attr.u;
}
Assert(pCtx->tr.Attr.u & X86_SEL_TYPE_SYS_TSS_BUSY_MASK);
{
cur_state->tr.sel = pCtx->tr.Sel;
cur_state->tr.limit = pCtx->tr.u32Limit;
cur_state->tr.base = pCtx->tr.u64Base;
cur_state->tr.access = pCtx->tr.Attr.u;
}
cur_state->idtr.limit = pCtx->idtr.cbIdt;
cur_state->idtr.base = pCtx->idtr.pIdt;
cur_state->gdtr.limit = pCtx->gdtr.cbGdt;
cur_state->gdtr.base = pCtx->gdtr.pGdt;
cur_state->Ia32_efer = CPUMGetGuestEFER(pVCpu);
VMCPU_SET_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC);
int irq = -1;
if (has_pending_irq(pVCpu) &&
cur_state->Intr_state == INTERRUPT_STATE_NONE &&
cur_state->Rflags & X86_EFL_IF)
{
TRPMSaveTrap(pVCpu);
irq = get_irq(pVCpu);
guest_interrupts->set_pending_interrupt(irq);
}
vm_handler.run_vm();
const uint64_t reason = cur_state->Exit_reason;
uint32_t changed_state = CPUM_CHANGED_GLOBAL_TLB_FLUSH
| CPUM_CHANGED_HIDDEN_SEL_REGS;
switch(reason)
{
case VMX_EXIT_MOV_CRX:
if (handle_cr(cur_state))
rc = VINF_SUCCESS;
else
rc = VINF_EM_RAW_EMULATE_INSTR;
break;
case VMX_EXIT_EXT_INT:
case VMX_EXIT_TASK_SWITCH:
case VMX_EXIT_PREEMPT_TIMER:
rc = VINF_SUCCESS;
break;
case VMX_EXIT_TRIPLE_FAULT:
rc = VINF_EM_TRIPLE_FAULT;
break;
case VMX_EXIT_CPUID:
case VMX_EXIT_HLT:
case VMX_EXIT_INVLPG:
case VMX_EXIT_RDTSC:
case VMX_EXIT_MOV_DRX:
case VMX_EXIT_IO_INSTR:
case VMX_EXIT_RDMSR:
case VMX_EXIT_WRMSR:
case VMX_EXIT_PAUSE:
case VMX_EXIT_EPT_VIOLATION:
case VMX_EXIT_WBINVD:
rc = VINF_EM_RAW_EMULATE_INSTR;
break;
default:
rc = VINF_EM_RAW_EMULATE_INSTR;
}
VMCPU_SET_STATE(pVCpu, VMCPUSTATE_STARTED);
pCtx->rip = cur_state->Rip;
pCtx->rsp = cur_state->Rsp;
pCtx->rax = cur_state->Regs.Rax;
pCtx->rbx = cur_state->Regs.Rbx;
pCtx->rcx = cur_state->Regs.Rcx;
pCtx->rdx = cur_state->Regs.Rdx;
pCtx->rbp = cur_state->Regs.Rbp;
pCtx->rsi = cur_state->Regs.Rsi;
pCtx->rdi = cur_state->Regs.Rdi;
pCtx->r8 = cur_state->Regs.R08;
pCtx->r9 = cur_state->Regs.R09;
pCtx->r10 = cur_state->Regs.R10;
pCtx->r11 = cur_state->Regs.R11;
pCtx->r12 = cur_state->Regs.R12;
pCtx->r13 = cur_state->Regs.R13;
pCtx->r14 = cur_state->Regs.R14;
pCtx->r15 = cur_state->Regs.R15;
pCtx->rflags.u = cur_state->Rflags;
if (pCtx->SysEnter.cs != cur_state->Sysenter_cs) {
pCtx->SysEnter.cs = cur_state->Sysenter_cs;
changed_state |= CPUM_CHANGED_SYSENTER_MSR;
}
if (pCtx->SysEnter.esp != cur_state->Sysenter_esp) {
pCtx->SysEnter.esp = cur_state->Sysenter_esp;
changed_state |= CPUM_CHANGED_SYSENTER_MSR;
}
if (pCtx->SysEnter.eip != cur_state->Sysenter_eip) {
pCtx->SysEnter.eip = cur_state->Sysenter_eip;
changed_state |= CPUM_CHANGED_SYSENTER_MSR;
}
if (pCtx->idtr.cbIdt != cur_state->idtr.limit ||
pCtx->idtr.pIdt != cur_state->idtr.base)
CPUMSetGuestIDTR(pVCpu, cur_state->idtr.base, cur_state->idtr.limit);
if (pCtx->gdtr.cbGdt != cur_state->gdtr.limit ||
pCtx->gdtr.pGdt != cur_state->gdtr.base)
CPUMSetGuestGDTR(pVCpu, cur_state->gdtr.base, cur_state->gdtr.limit);
{
uint32_t val;
val = (cur_state->Shadow_cr0 & pVCpu->hm.s.vmx.u32CR0Mask);
val |= (cur_state->Cr0 & ~pVCpu->hm.s.vmx.u32CR0Mask);
if (pCtx->cr0 != val)
CPUMSetGuestCR0(pVCpu, val);
}
if (pCtx->cr2 != cur_state->Regs.Cr2)
CPUMSetGuestCR2(pVCpu, cur_state->Regs.Cr2);
{
uint32_t val;
val = (cur_state->Shadow_cr4 & pVCpu->hm.s.vmx.u32CR4Mask);
val |= (cur_state->Cr4 & ~pVCpu->hm.s.vmx.u32CR4Mask);
if (pCtx->cr4 != val)
CPUMSetGuestCR4(pVCpu, val);
}
/*
* Guest CR3 must be handled after saving CR0 & CR4.
* See HMVMXR0.cpp, function hmR0VmxSaveGuestControlRegs
*/
if (pCtx->cr3 != cur_state->Cr3) {
CPUMSetGuestCR3(pVCpu, cur_state->Cr3);
}
GENODE_READ_SELREG(cs);
GENODE_READ_SELREG(ss);
GENODE_READ_SELREG(ds);
GENODE_READ_SELREG(es);
GENODE_READ_SELREG(fs);
GENODE_READ_SELREG(gs);
if (GENODE_READ_SELREG_REQUIRED(ldtr)) {
GENODE_READ_SELREG(ldtr);
CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_LDTR);
}
if (GENODE_READ_SELREG_REQUIRED(tr)) {
GENODE_READ_SELREG(tr);
CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_TR);
}
CPUMSetGuestEFER(pVCpu, cur_state->Ia32_efer);
VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_TO_R3);
CPUMSetChangedFlags(pVCpu, changed_state);
if (cur_state->Intr_state != 0) {
Assert(cur_state->Intr_state == BLOCKING_BY_STI ||
cur_state->Intr_state == BLOCKING_BY_MOV_SS);
EMSetInhibitInterruptsPC(pVCpu, pCtx->rip);
} else
VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
if ((irq != -1) && guest_interrupts->is_pending_interrupt(irq))
{
TRPMRestoreTrap(pVCpu);
guest_interrupts->clear_pending_interrupt(irq);
}
#ifdef VBOX_WITH_REM
/* XXX see VMM/VMMR0/HMVMXR0.cpp - not necessary every time ! XXX */
REMFlushTBs(pVM);
#endif
return rc;
}
PERR("SUPR3CallVMMR0Fast: unhandled uOperation %d", uOperation);
return VERR_INTERNAL_ERROR;
}
static Genode::Semaphore *r0_halt_sem()
{
static Genode::Semaphore sem;
return &sem;
}
int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned
uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr)
{
switch(uOperation)
{
case VMMR0_DO_GVMM_CREATE_VM:
{
GVMMCREATEVMREQ &req = reinterpret_cast<GVMMCREATEVMREQ &>(*pReqHdr);
AssertMsgReturn(req.cCpus == 1,
("VM with multiple CPUs not supported\n"),
VERR_INVALID_PARAMETER);
AssertMsgReturn(setup_subject_state(),
("Unable to map guest subject state\n"),
VERR_INVALID_PARAMETER);
AssertMsgReturn(setup_subject_interrupts(),
("Unable to map guest interrupts page\n"),
VERR_INVALID_PARAMETER);
genode_VMMR0_DO_GVMM_CREATE_VM(pReqHdr);
return VINF_SUCCESS;
}
case VMMR0_DO_GVMM_SCHED_HALT:
r0_halt_sem()->down();
return VINF_SUCCESS;
case VMMR0_DO_GVMM_SCHED_WAKE_UP:
r0_halt_sem()->up();
return VINF_SUCCESS;
case VMMR0_DO_VMMR0_INIT:
{
VM * pVM = reinterpret_cast<VM *>(pVMR0);
PVMCPU pVCpu = &pVM->aCpus[idCpu];
pVM->hm.s.svm.fSupported = false;
pVM->hm.s.vmx.fSupported = true;
pVM->hm.s.vmx.fAllowUnrestricted = true;
pVCpu->hm.s.vmx.u32CR0Mask = 0x60000020;
pVCpu->hm.s.vmx.u32CR4Mask = 0x2000;
return VINF_SUCCESS;
}
case VMMR0_DO_GVMM_SCHED_POLL:
case VMMR0_DO_GVMM_DESTROY_VM:
case VMMR0_DO_VMMR0_TERM:
case VMMR0_DO_HM_SETUP_VM:
case VMMR0_DO_HM_ENABLE:
return VINF_SUCCESS;
case VMMR0_DO_GVMM_SCHED_POKE:
return VINF_SUCCESS;
default:
PERR("SUPR3CallVMMR0Ex: unhandled uOperation %d", uOperation);
return VERR_GENERAL_FAILURE;
}
}
bool create_emt_vcpu(pthread_t * thread, size_t stack_size,
const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg,
Genode::Cpu_session * cpu_session,
Genode::Affinity::Location location,
unsigned int cpu_id)
{
/* No support for multiple vcpus */
return false;
}
void genode_update_tsc(void (*update_func)(void), unsigned long update_us)
{
using namespace Genode;
Timer::Connection timer;
Signal_context sig_ctx;
Signal_receiver sig_rec;
Signal_context_capability sig_cap = sig_rec.manage(&sig_ctx);
timer.sigh(sig_cap);
timer.trigger_once(update_us);
for (;;) {
Signal s = sig_rec.wait_for_signal();
update_func();
timer.trigger_once(update_us);
}
}
uint64_t genode_cpu_hz()
{
static uint64_t cpu_freq = 0;
if (!cpu_freq) {
cpu_freq = sinfo()->get_tsc_khz() * 1000;
if (!cpu_freq)
PERR("Unable to determine CPU frequency");
}
return cpu_freq;
}
/**
* Dummies and unimplemented stuff.
*/
/**
* VM memory layout on Muen is static. Always report success for revocation
* operation.
*/
bool Vmm_memory::revoke_from_vm(Mem_region *r)
{
return true;
}
extern "C" void pthread_yield() { PWRN("%s unimplemented", __func__); }

View File

@ -0,0 +1,122 @@
/*
* \brief Muen subject state
* \author Adrian-Ken Rueegsegger
* \author Reto Buerki
*/
/*
* Copyright (C) 2015-2016 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _VIRTUALBOX__SPEC__MUEN__VCPU_H_
#define _VIRTUALBOX__SPEC__MUEN__VCPU_H_
#include <base/stdint.h>
struct Cpu_registers
{
Genode::uint64_t Cr2;
Genode::uint64_t Rax;
Genode::uint64_t Rbx;
Genode::uint64_t Rcx;
Genode::uint64_t Rdx;
Genode::uint64_t Rdi;
Genode::uint64_t Rsi;
Genode::uint64_t Rbp;
Genode::uint64_t R08;
Genode::uint64_t R09;
Genode::uint64_t R10;
Genode::uint64_t R11;
Genode::uint64_t R12;
Genode::uint64_t R13;
Genode::uint64_t R14;
Genode::uint64_t R15;
} __attribute__((packed));
struct Segment
{
Genode::uint64_t sel;
Genode::uint64_t base;
Genode::uint32_t limit;
Genode::uint32_t access;
} __attribute__((packed));
struct Subject_state
{
struct Cpu_registers Regs;
Genode::uint64_t Exit_reason;
Genode::uint64_t Exit_qualification;
Genode::uint64_t Guest_phys_addr;
Genode::uint64_t Intr_state;
Genode::uint64_t Interrupt_info;
Genode::uint64_t Instruction_len;
Genode::uint64_t Rip;
Genode::uint64_t Rsp;
Genode::uint64_t Cr0;
Genode::uint64_t Shadow_cr0;
Genode::uint64_t Cr3;
Genode::uint64_t Cr4;
Genode::uint64_t Shadow_cr4;
Genode::uint64_t Rflags;
Genode::uint64_t Ia32_efer;
Genode::uint64_t Sysenter_cs;
Genode::uint64_t Sysenter_esp;
Genode::uint64_t Sysenter_eip;
Segment cs;
Segment ss;
Segment ds;
Segment es;
Segment fs;
Segment gs;
Segment tr;
Segment ldtr;
Segment gdtr;
Segment idtr;
} __attribute__((packed));
/**
* Print subject state information
*/
inline void dump_register_state(Subject_state * state)
{
PINF("subject state");
PLOG("ip:sp:efl ax:bx:cx:dx:si:di %lx:%lx:%lx %lx:%lx:%lx:%lx:%lx:%lx",
state->Rip, state->Rsp, state->Rflags,
state->Regs.Rax, state->Regs.Rbx, state->Regs.Rcx, state->Regs.Rdx,
state->Regs.Rsi, state->Regs.Rdi);
PLOG("cs base:limit:sel:ar %lx:%x:%x:%x", state->cs.base,
state->cs.limit, state->cs.sel, state->cs.access);
PLOG("ds base:limit:sel:ar %lx:%x:%x:%x", state->ds.base,
state->ds.limit, state->ds.sel, state->ds.access);
PLOG("es base:limit:sel:ar %lx:%x:%x:%x", state->es.base,
state->es.limit, state->es.sel, state->es.access);
PLOG("fs base:limit:sel:ar %lx:%x:%x:%x", state->fs.base,
state->fs.limit, state->fs.sel, state->fs.access);
PLOG("gs base:limit:sel:ar %lx:%x:%x:%x", state->gs.base,
state->gs.limit, state->gs.sel, state->gs.access);
PLOG("ss base:limit:sel:ar %lx:%x:%x:%x", state->ss.base,
state->ss.limit, state->ss.sel, state->ss.access);
PLOG("cr0:cr2:cr3:cr4 %lx:%lx:%lx:%lx",
state->Cr0, state->Regs.Cr2, state->Cr3, state->Cr4);
PLOG("ldtr base:limit:sel:ar %lx:%x:%x:%x", state->ldtr.base,
state->ldtr.limit, state->ldtr.sel, state->ldtr.access);
PLOG("tr base:limit:sel:ar %lx:%x:%x:%x", state->tr.base,
state->tr.limit, state->tr.sel, state->tr.access);
PLOG("gdtr base:limit %lx:%x", state->gdtr.base, state->gdtr.limit);
PLOG("idtr base:limit %lx:%x", state->idtr.base, state->idtr.limit);
PLOG("sysenter cs:eip:esp %lx %lx %lx", state->Sysenter_cs,
state->Sysenter_eip, state->Sysenter_esp);
PLOG("%x", state->Intr_state);
}
#endif /* _VIRTUALBOX__SPEC__MUEN__VCPU_H_ */

View File

@ -0,0 +1,64 @@
/*
* \brief Genode/Muen specific VirtualBox SUPLib supplements
* \author Adrian-Ken Rueegsegger
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _VIRTUALBOX__SPEC__MUEN__VM_HANDLER_H_
#define _VIRTUALBOX__SPEC__MUEN__VM_HANDLER_H_
#include <base/env.h>
#include <base/signal.h>
#include <vm_session/vm_session.h>
#include <vm_session/connection.h>
namespace Genode
{
/**
* Vm execution handler.
*/
class Vm_handler;
}
class Genode::Vm_handler
{
private:
Vm_connection _vm_session;
Signal_context_capability _sig_cap;
Signal_receiver *_sig_rcv;
Signal_transmitter _sig_xmit;
Signal_context _sig_ctx;
public:
Vm_handler()
{
_sig_rcv = new (env()->heap())Signal_receiver();
_sig_cap = _sig_rcv->manage(&_sig_ctx);
_sig_xmit.context(_sig_cap);
_vm_session.exception_handler(_sig_cap);
}
~Vm_handler() { _sig_rcv->dissolve(&_sig_ctx); }
/**
* Starts execution of the Vm and blocks until the Vm returns or the
* execution handler gets poked.
*/
void run_vm()
{
_vm_session.run();
_sig_rcv->wait_for_signal();
}
};
#endif /* _VIRTUALBOX__SPEC__MUEN__VM_HANDLER_H_ */

View File

@ -0,0 +1,51 @@
/*
* \brief Memory region types
* \author Norman Feske
* \author Adrian-Ken Rueegsegger
* \author Reto Buerki
* \date 2013-09-02
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _VIRTUALBOX__SPEC__NOVA__MEM_REGION_H_
#define _VIRTUALBOX__SPEC__NOVA__MEM_REGION_H_
/* Genode includes */
#include <util/list.h>
#include <os/attached_ram_dataspace.h>
/* VirtualBox includes */
#include <VBox/vmm/pgm.h>
struct Mem_region;
struct Mem_region : Genode::List<Mem_region>::Element,
Genode::Attached_ram_dataspace
{
typedef Genode::Ram_session Ram_session;
PPDMDEVINS pDevIns;
unsigned const iRegion;
RTGCPHYS vm_phys;
PFNPGMR3PHYSHANDLER pfnHandlerR3;
void *pvUserR3;
PGMPHYSHANDLERTYPE enmType;
Mem_region(Ram_session &ram, size_t size, PPDMDEVINS pDevIns,
unsigned iRegion)
:
Attached_ram_dataspace(&ram, size),
pDevIns(pDevIns),
iRegion(iRegion),
vm_phys(0), pfnHandlerR3(0), pvUserR3(0)
{ }
};
#endif /* _VIRTUALBOX__SPEC__NOVA__MEM_REGION_H_ */