vbox: join emt and vcpu handler thread

Fixes #1129
This commit is contained in:
Alexander Boettcher 2014-04-24 10:55:09 +02:00 committed by Norman Feske
parent 4782fd34f6
commit 0ddc69d370
7 changed files with 282 additions and 394 deletions

View File

@ -3,6 +3,7 @@ include $(REP_DIR)/lib/mk/virtualbox-common.inc
SRC_CC = sup.cc SRC_CC = sup.cc
INC_DIR += $(call select_from_repositories,src/lib/libc) INC_DIR += $(call select_from_repositories,src/lib/libc)
INC_DIR += $(call select_from_repositories,src/lib/pthread)
INC_DIR += $(VBOX_DIR)/VMM/include INC_DIR += $(VBOX_DIR)/VMM/include
INC_DIR += $(REP_DIR)/src/virtualbox INC_DIR += $(REP_DIR)/src/virtualbox

View File

@ -25,6 +25,9 @@
#include "sup.h" #include "sup.h"
#include "vmm_memory.h" #include "vmm_memory.h"
/* Libc include */
#include <pthread.h>
/* VirtualBox SUPLib interface */ /* VirtualBox SUPLib interface */
int SUPR3QueryVTxSupported(void) int SUPR3QueryVTxSupported(void)
@ -77,6 +80,16 @@ int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned
} }
extern "C"
bool create_emt_vcpu(pthread_t * thread, size_t stack_size,
const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg)
{
/* no hardware acceleration support */
return false;
}
/** /**
* Dummies and unimplemented stuff. * Dummies and unimplemented stuff.
*/ */
@ -85,13 +98,12 @@ uint64_t genode_cpu_hz() {
return 1000000000ULL; /* XXX fixed 1GHz return value */ return 1000000000ULL; /* XXX fixed 1GHz return value */
} }
bool Vmm_memory::unmap_from_vm(RTGCPHYS GCPhys) bool Vmm_memory::unmap_from_vm(RTGCPHYS GCPhys)
{ {
PWRN("%s unimplemented", __func__); PWRN("%s unimplemented", __func__);
return false; return false;
} }
extern "C" int pthread_yield() {
PWRN("%s unimplemented", __func__); extern "C" void pthread_yield() { PWRN("%s unimplemented", __func__); }
return 0;
}

View File

@ -18,6 +18,7 @@
#include <base/flex_iterator.h> #include <base/flex_iterator.h>
#include <rom_session/connection.h> #include <rom_session/connection.h>
#include <timer_session/connection.h> #include <timer_session/connection.h>
#include <os/attached_rom_dataspace.h>
#include <vmm/vcpu_thread.h> #include <vmm/vcpu_thread.h>
#include <vmm/vcpu_dispatcher.h> #include <vmm/vcpu_dispatcher.h>
@ -51,129 +52,77 @@ static Genode::Semaphore *r0_halt_sem()
/* Genode specific function */ /* Genode specific function */
void SUPR3QueryHWACCLonGenodeSupport(VM * pVM) { static Genode::Attached_rom_dataspace hip_rom("hypervisor_info_page");
void SUPR3QueryHWACCLonGenodeSupport(VM * pVM)
{
try { try {
using namespace Genode; Nova::Hip * hip = hip_rom.local_addr<Nova::Hip>();
Rom_connection hip_rom("hypervisor_info_page");
Nova::Hip * const hip = env()->rm_session()->attach(hip_rom.dataspace());
if (!hip)
return;
pVM->hwaccm.s.svm.fSupported = hip->has_feature_svm(); pVM->hwaccm.s.svm.fSupported = hip->has_feature_svm();
pVM->hwaccm.s.vmx.fSupported = hip->has_feature_vmx(); pVM->hwaccm.s.vmx.fSupported = hip->has_feature_vmx();
PINF("support svm %u vmx %u", hip->has_feature_svm(), hip->has_feature_vmx());
} catch (...) { } catch (...) {
PWRN("No hardware acceleration available - execution will be slow!"); PWRN("No hardware acceleration available - execution will be slow!");
} /* if we get an exception let hardware support off */ } /* if we get an exception let hardware support off */
} }
void SUPR3QueryHWACCLonGenodeCreateVM(VM * pVM)
{
bool svm = pVM->hwaccm.s.svm.fSupported;
if (!svm && !pVM->hwaccm.s.vmx.fSupported) {
PERR("SVM nor VMX supported by hardware accelerated code called !");
return;
}
Assert(!vcpu_handler);
if (svm)
vcpu_handler = new Vcpu_handler_svm();
else
vcpu_handler = new Vcpu_handler_vmx();
}
/* VirtualBox SUPLib interface */ /* VirtualBox SUPLib interface */
int SUPR3QueryVTxSupported(void) int SUPR3QueryVTxSupported(void) { return VINF_SUCCESS; }
{
return VINF_SUCCESS;
}
int SUPR3CallVMMR0Fast(PVMR0 pVMR0, unsigned uOperation, VMCPUID idCpu) int SUPR3CallVMMR0Fast(PVMR0 pVMR0, unsigned uOperation, VMCPUID idCpu)
{ {
switch (uOperation) switch (uOperation) {
{ case SUP_VMMR0_DO_HWACC_RUN:
case SUP_VMMR0_DO_HWACC_RUN: return vcpu_handler->run_hw(pVMR0, idCpu);
{
VM * pVM = reinterpret_cast<VM *>(pVMR0);
PVMCPU pVCpu = &pVM->aCpus[idCpu];
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
return vcpu_handler->run_hw(pVMR0, idCpu);
}
default:
break;
} }
return VERR_INTERNAL_ERROR; return VERR_INTERNAL_ERROR;
} }
int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned
uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr) uOperation, uint64_t u64Arg, PSUPVMMR0REQHDR pReqHdr)
{ {
static unsigned counter = 0; switch (uOperation) {
switch(uOperation) case VMMR0_DO_GVMM_CREATE_VM:
{ genode_VMMR0_DO_GVMM_CREATE_VM(pReqHdr);
case VMMR0_DO_GVMM_CREATE_VM: return VINF_SUCCESS;
genode_VMMR0_DO_GVMM_CREATE_VM(pReqHdr);
return VINF_SUCCESS;
case VMMR0_DO_GVMM_SCHED_HALT: case VMMR0_DO_GVMM_SCHED_HALT:
// counter ++; r0_halt_sem()->down();
// PERR("halt %u", counter); return VINF_SUCCESS;
r0_halt_sem()->down();
// PERR("halt - done");
return VINF_SUCCESS;
case VMMR0_DO_GVMM_SCHED_WAKE_UP: case VMMR0_DO_GVMM_SCHED_WAKE_UP:
// counter ++; r0_halt_sem()->up();
// PERR("sched wake up %u", counter); return VINF_SUCCESS;
r0_halt_sem()->up();
return VINF_SUCCESS;
case VMMR0_DO_GVMM_SCHED_POLL: /* called by 'vmR3HaltGlobal1Halt' */
/* called by 'vmR3HaltGlobal1Halt' */ case VMMR0_DO_GVMM_SCHED_POLL:
// PDBG("SUPR3CallVMMR0Ex: VMMR0_DO_GVMM_SCHED_POLL"); return VINF_SUCCESS;
return VINF_SUCCESS;
case VMMR0_DO_VMMR0_INIT: case VMMR0_DO_VMMR0_INIT:
{ SUPR3QueryHWACCLonGenodeSupport(reinterpret_cast<VM *>(pVMR0));
VM * pVM = reinterpret_cast<VM *>(pVMR0); return VINF_SUCCESS;
SUPR3QueryHWACCLonGenodeSupport(pVM);
return VINF_SUCCESS;
}
case VMMR0_DO_HWACC_SETUP_VM:
{
VM * pVM = reinterpret_cast<VM *>(pVMR0);
SUPR3QueryHWACCLonGenodeCreateVM(pVM);
return VINF_SUCCESS;
}
case VMMR0_DO_HWACC_ENABLE:
return VINF_SUCCESS;
case VMMR0_DO_GVMM_SCHED_POKE: case VMMR0_DO_HWACC_SETUP_VM:
{ return VINF_SUCCESS;
/* XXX only do one of it - either recall or up - not both XXX */
vcpu_handler->recall();
r0_halt_sem()->up();
return VINF_SUCCESS;
}
default: case VMMR0_DO_HWACC_ENABLE:
PERR("SUPR3CallVMMR0Ex: unhandled uOperation %d", uOperation); return VINF_SUCCESS;
return VERR_GENERAL_FAILURE;
/* XXX only do one of it - either recall or up - not both XXX */
case VMMR0_DO_GVMM_SCHED_POKE:
vcpu_handler->recall();
r0_halt_sem()->up();
return VINF_SUCCESS;
default:
PERR("SUPR3CallVMMR0Ex: unhandled uOperation %d", uOperation);
return VERR_GENERAL_FAILURE;
} }
} }
@ -181,7 +130,8 @@ int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned
/** /**
* Various support stuff - base-nova specific. * Various support stuff - base-nova specific.
*/ */
uint64_t genode_cpu_hz() { uint64_t genode_cpu_hz()
{
static uint64_t cpu_freq = 0; static uint64_t cpu_freq = 0;
if (!cpu_freq) { if (!cpu_freq) {
@ -205,12 +155,6 @@ uint64_t genode_cpu_hz() {
} }
extern "C" int pthread_yield() {
Nova::ec_ctrl(Nova::EC_YIELD);
return 0;
}
bool Vmm_memory::unmap_from_vm(RTGCPHYS GCPhys) bool Vmm_memory::unmap_from_vm(RTGCPHYS GCPhys)
{ {
size_t const size = 1; size_t const size = 1;
@ -232,3 +176,27 @@ bool Vmm_memory::unmap_from_vm(RTGCPHYS GCPhys)
return true; return true;
} }
extern "C" void pthread_yield(void) { Nova::ec_ctrl(Nova::EC_YIELD); }
extern "C"
bool create_emt_vcpu(pthread_t * pthread, size_t stack,
const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg)
{
Nova::Hip * hip = hip_rom.local_addr<Nova::Hip>();
if (!hip->has_feature_vmx() && !hip->has_feature_svm())
return false;
if (hip->has_feature_vmx())
vcpu_handler = new Vcpu_handler_vmx(stack, attr, start_routine, arg);
if (hip->has_feature_svm())
vcpu_handler = new Vcpu_handler_svm(stack, attr, start_routine, arg);
*pthread = vcpu_handler;
return true;
}

View File

@ -39,6 +39,12 @@
#include "guest_memory.h" #include "guest_memory.h"
#include "vmm_memory.h" #include "vmm_memory.h"
/* Genode libc pthread binding */
#include "thread.h"
/* LibC includes */
#include <setjmp.h>
/* /*
* VirtualBox stores segment attributes in Intel format using a 32-bit * VirtualBox stores segment attributes in Intel format using a 32-bit
* value. NOVA represents the attributes in packet format using a 16-bit * value. NOVA represents the attributes in packet format using a 16-bit
@ -55,21 +61,22 @@ static inline Genode::uint32_t sel_ar_conv_from_nova(Genode::uint16_t v)
return (v & 0xff) | (((uint32_t )v << 4) & 0x1f000); return (v & 0xff) | (((uint32_t )v << 4) & 0x1f000);
} }
/*
* Used to map memory for virtual framebuffer to VM
*/
extern "C" int MMIO2_MAPPED_SYNC(PVM pVM, RTGCPHYS GCPhys, size_t cbWrite); extern "C" int MMIO2_MAPPED_SYNC(PVM pVM, RTGCPHYS GCPhys, size_t cbWrite);
class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base> class Vcpu_handler : public Vmm::Vcpu_dispatcher<pthread>
{ {
private: private:
enum { STACK_SIZE = 4096 };
Genode::Cap_connection _cap_connection; Genode::Cap_connection _cap_connection;
Vmm::Vcpu_other_pd _vcpu; Vmm::Vcpu_other_pd _vcpu;
Genode::addr_t _ec_sel = 0; Genode::addr_t _ec_sel = 0;
void fpu_save(char * data) { void fpu_save(char * data) {
Assert(!(reinterpret_cast<Genode::addr_t>(data) & 0xF)); Assert(!(reinterpret_cast<Genode::addr_t>(data) & 0xF));
asm volatile ("fxsave %0" : "=m" (*data)); asm volatile ("fxsave %0" : "=m" (*data));
@ -82,41 +89,48 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
protected: protected:
/* unlocked by first startup exception */ struct {
Genode::Lock _lock_startup; Nova::mword_t mtd;
Genode::Lock _signal_vcpu; unsigned intr_state;
Genode::Lock _signal_emt; unsigned ctrl[2];
} next_utcb;
PVM _current_vm; PVM _current_vm;
PVMCPU _current_vcpu; PVMCPU _current_vcpu;
unsigned _current_exit_cond; void * _stack_reply;
jmp_buf _env;
__attribute__((noreturn)) void _default_handler(unsigned cond) void switch_to_hw(PCPUMCTX pCtx) {
unsigned long value;
if (!setjmp(_env)) {
_stack_reply = reinterpret_cast<void *>(&value - 1);
Nova::reply(_stack_reply);
}
}
__attribute__((noreturn)) void _irq_window(unsigned cond)
{ {
using namespace Nova; Nova::Utcb * utcb = reinterpret_cast<Nova::Utcb *>(Thread_base::utcb());
using namespace Genode;
Thread_base *myself = Thread_base::myself(); Assert(!(utcb->intr_state & 3));
Utcb *utcb = reinterpret_cast<Utcb *>(myself->utcb()); Assert(utcb->flags & X86_EFL_IF);
/* tell caller what happened */ if (irq_win(utcb)) {
_current_exit_cond = cond; /* reset mtd to not transfer anything back by accident */
utcb->mtd = 0;
/* inject IRQ */
if (inj_event(utcb, _current_vcpu))
Nova::reply(_stack_reply);
}
PVMCPU pVCpu = _current_vcpu; /* go back to re-compiler */
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu); longjmp(_env, 1);
}
fpu_save(reinterpret_cast<char *>(&pCtx->fpu)); __attribute__((noreturn)) void _default_handler()
{
/* unblock caller */ longjmp(_env, 1);
_signal_emt.unlock();
/* block myself */
_signal_vcpu.lock();
fpu_load(reinterpret_cast<char *>(&pCtx->fpu));
utcb->mtd |= Mtd::FPU;
Nova::reply(myself->stack_top());
} }
@ -128,13 +142,16 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
using namespace Nova; using namespace Nova;
using namespace Genode; using namespace Genode;
addr_t stack_top;
Assert(utcb->actv_state == 0);
Assert(!(utcb->intr_state & 3));
Assert(!(utcb->inj_info & 0x80000000));
if (unmap) { if (unmap) {
PERR("unmap not implemented\n"); PERR("unmap not implemented\n");
Nova::reply(reinterpret_cast<void *>(&stack_top));
/* deadlock until implemented */
_signal_vcpu.lock();
Nova::reply(myself->stack_top());
} }
Flexpage_iterator fli; Flexpage_iterator fli;
@ -159,26 +176,8 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
} }
/* emulator has to take over if fault region is not ram */ /* emulator has to take over if fault region is not ram */
if (!pv) { if (!pv)
/* tell caller what happened */ longjmp(_env, 1);
_current_exit_cond = NPT_EPT;
PVMCPU pVCpu = _current_vcpu;
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
fpu_save(reinterpret_cast<char *>(&pCtx->fpu));
/* unblock caller */
_signal_emt.unlock();
/* block myself */
_signal_vcpu.lock();
fpu_load(reinterpret_cast<char *>(&pCtx->fpu));
utcb->mtd |= Mtd::FPU;
Nova::reply(myself->stack_top());
}
/* fault region is ram - so map it */ /* fault region is ram - so map it */
enum { enum {
@ -210,7 +209,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
res = utcb->append_item(crd, flexpage.hotspot, USER_PD, GUEST_PGT); res = utcb->append_item(crd, flexpage.hotspot, USER_PD, GUEST_PGT);
} while (res); } while (res);
Nova::reply(myself->stack_top()); Nova::reply(reinterpret_cast<void *>(&stack_top));
} }
/** /**
@ -396,7 +395,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
} }
inline void inj_event(Nova::Utcb * utcb, PVMCPU pVCpu) inline bool inj_event(Nova::Utcb * utcb, PVMCPU pVCpu)
{ {
PCPUMCTX const pCtx = CPUMQueryGuestCtxPtr(pVCpu); PCPUMCTX const pCtx = CPUMQueryGuestCtxPtr(pVCpu);
@ -409,7 +408,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
if (VMCPU_FF_ISPENDING(pVCpu, (VMCPU_FF_INTERRUPT_APIC|VMCPU_FF_INTERRUPT_PIC))) { if (VMCPU_FF_ISPENDING(pVCpu, (VMCPU_FF_INTERRUPT_APIC|VMCPU_FF_INTERRUPT_PIC))) {
if (!(utcb->flags & X86_EFL_IF)) { if (!(pCtx->rflags.u & X86_EFL_IF)) {
unsigned vector = 0; unsigned vector = 0;
utcb->inj_info = 0x1000 | vector; utcb->inj_info = 0x1000 | vector;
@ -430,9 +429,9 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
} }
/* can an interrupt be dispatched ? */ /* can an interrupt be dispatched ? */
if (!TRPMHasTrap(pVCpu) || !(utcb->flags & X86_EFL_IF) || if (!TRPMHasTrap(pVCpu) || !(pCtx->rflags.u & X86_EFL_IF) ||
VMCPU_FF_ISSET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)) VMCPU_FF_ISSET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
return; return false;
#ifdef VBOX_STRICT #ifdef VBOX_STRICT
if (TRPMHasTrap(pVCpu)) { if (TRPMHasTrap(pVCpu)) {
@ -472,20 +471,44 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
utcb->inj_error = Event.n.u32ErrorCode; utcb->inj_error = Event.n.u32ErrorCode;
utcb->mtd |= Nova::Mtd::INJ; utcb->mtd |= Nova::Mtd::INJ;
/* /*
PDBG("type:info:vector %x:%x:%x", Vmm::printf("type:info:vector %x:%x:%x\n",
Event.n.u3Type, utcb->inj_info, u8Vector); Event.n.u3Type, utcb->inj_info, u8Vector);
*/ */
return true;
} }
inline void irq_win(Nova::Utcb * utcb, PVMCPU pVCpu) inline bool irq_win(Nova::Utcb * utcb)
{ {
Assert(utcb->flags & X86_EFL_IF); Assert(!(VMCPU_FF_ISSET(_current_vcpu, VMCPU_FF_INHIBIT_INTERRUPTS)));
Nova::mword_t const mtd = Nova::Mtd::INJ; uint32_t check_vm = VM_FF_HWACCM_TO_R3_MASK | VM_FF_REQUEST
utcb->mtd = ~mtd; | VM_FF_PGM_POOL_FLUSH_PENDING
| VM_FF_PDM_DMA;
uint32_t check_vcpu = VMCPU_FF_HWACCM_TO_R3_MASK
| VMCPU_FF_PGM_SYNC_CR3
| VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL
| VMCPU_FF_REQUEST;
if (VM_FF_ISPENDING(_current_vm, check_vm)
|| VMCPU_FF_ISPENDING(_current_vcpu, check_vcpu))
{
Assert(VM_FF_ISPENDING(_current_vm, VM_FF_HWACCM_TO_R3_MASK) ||
VMCPU_FF_ISPENDING(_current_vcpu,
VMCPU_FF_HWACCM_TO_R3_MASK));
Assert(!(RT_UNLIKELY(VM_FF_ISPENDING(_current_vm,
VM_FF_PGM_NO_MEMORY))));
return false;
}
/* Is in Realmode ? */
if (!(utcb->cr0 & X86_CR0_PE))
return false;
return true;
} }
virtual bool hw_load_state(Nova::Utcb *, VM *, PVMCPU) = 0; virtual bool hw_load_state(Nova::Utcb *, VM *, PVMCPU) = 0;
@ -505,21 +528,16 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
}; };
Vcpu_handler() Vcpu_handler(size_t stack_size, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg)
: :
Vmm::Vcpu_dispatcher<Genode::Thread_base>(STACK_SIZE, Vmm::Vcpu_dispatcher<pthread>(stack_size, _cap_connection,
_cap_connection), attr ? *attr : 0, start_routine, arg),
_ec_sel(Genode::cap_map()->insert()), _ec_sel(Genode::cap_map()->insert())
_lock_startup(Genode::Lock::LOCKED),
_signal_emt(Genode::Lock::LOCKED),
_signal_vcpu(Genode::Lock::LOCKED)
{ } { }
void start() { void start() {
_vcpu.start(_ec_sel); _vcpu.start(_ec_sel);
/* wait until vCPU thread is up */
_lock_startup.lock();
} }
void recall() void recall()
@ -623,23 +641,28 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(Thread_base::utcb()); Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(Thread_base::utcb());
Assert(Thread_base::utcb() == Thread_base::myself()->utcb());
/* take the utcb state prepared during the last exit */
utcb->mtd = next_utcb.mtd;
utcb->intr_state = next_utcb.intr_state;
utcb->actv_state = 0; /* XXX */
utcb->ctrl[0] = next_utcb.ctrl[0];
utcb->ctrl[1] = next_utcb.ctrl[1];
using namespace Nova; using namespace Nova;
Genode::Thread_base *myself = Genode::Thread_base::myself();
/* check whether to inject interrupts */
inj_event(utcb, pVCpu);
/* Transfer vCPU state from vBox to Nova format */ /* Transfer vCPU state from vBox to Nova format */
if (!vbox_to_utcb(utcb, pVM, pVCpu) || if (!vbox_to_utcb(utcb, pVM, pVCpu) ||
!hw_load_state(utcb, pVM, pVCpu)) { !hw_load_state(utcb, pVM, pVCpu)) {
PERR("loading vCPU state failed"); PERR("loading vCPU state failed");
/* deadlock here */ return VERR_INTERNAL_ERROR;
_signal_emt.lock();
} }
/* check whether to inject interrupts */
inj_event(utcb, pVCpu);
ResumeExecution:
/* /*
* Flag vCPU to be "pokeable" by external events such as interrupts * Flag vCPU to be "pokeable" by external events such as interrupts
* from virtual devices. Only if this flag is set, the * from virtual devices. Only if this flag is set, the
@ -649,17 +672,24 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
*/ */
VMCPU_SET_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC); VMCPU_SET_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC);
this->_current_vm = pVM; /* write FPU state from pCtx to vCPU */
this->_current_vcpu = pVCpu; fpu_load(reinterpret_cast<char *>(&pCtx->fpu));
/* let vCPU run */ utcb->mtd |= Mtd::FPU;
_signal_vcpu.unlock();
/* waiting to be woken up */ _current_vm = pVM;
_signal_emt.lock(); _current_vcpu = pVCpu;
this->_current_vm = 0; /* switch to hardware accelerated mode */
this->_current_vcpu = 0; switch_to_hw(pCtx);
Assert(utcb->actv_state == 0);
_current_vm = 0;
_current_vcpu = 0;
/* write FPU state of vCPU to pCtx */
fpu_save(reinterpret_cast<char *>(&pCtx->fpu));
// CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_GLOBAL_TLB_FLUSH); // CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_GLOBAL_TLB_FLUSH);
@ -668,89 +698,23 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread_base>
/* Transfer vCPU state from Nova to vBox format */ /* Transfer vCPU state from Nova to vBox format */
if (!utcb_to_vbox(utcb, pVM, pVCpu) || if (!utcb_to_vbox(utcb, pVM, pVCpu) ||
!hw_save_state(utcb, pVM, pVCpu)) { !hw_save_state(utcb, pVM, pVCpu)) {
PERR("saving vCPU state failed"); PERR("saving vCPU state failed");
/* deadlock here */ return VERR_INTERNAL_ERROR;
_signal_emt.lock();
} }
/* reset message transfer descriptor for next invocation */ /* reset message transfer descriptor for next invocation */
utcb->mtd = 0; next_utcb.mtd = 0;
next_utcb.intr_state = utcb->intr_state;
next_utcb.ctrl[0] = utcb->ctrl[0];
next_utcb.ctrl[1] = utcb->ctrl[1];
if (utcb->intr_state & 3) { if (next_utcb.intr_state & 3) {
/* next_utcb.intr_state &= ~3U;
PDBG("reset intr_state - exit reason %u", _current_exit_cond); next_utcb.mtd |= Mtd::STA;
*/
utcb->intr_state &= ~3;
utcb->mtd |= Mtd::STA;
} }
switch (_current_exit_cond) return VINF_EM_RAW_EMULATE_INSTR;
{
case RECALL:
case VMX_EXIT_EPT_VIOLATION:
case VMX_EXIT_PORT_IO:
case VMX_EXIT_ERR_INVALID_GUEST_STATE:
case VMX_EXIT_HLT:
case SVM_EXIT_IOIO:
case SVM_NPT:
case SVM_EXIT_HLT:
case SVM_INVALID:
case SVM_EXIT_MSR:
case EMULATE_INSTR:
return VINF_EM_RAW_EMULATE_INSTR;
case SVM_EXIT_VINTR:
case VMX_EXIT_IRQ_WINDOW:
{
if (VMCPU_FF_ISSET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS)) {
if (pCtx->rip != EMGetInhibitInterruptsPC(pVCpu))
PERR("inhibit interrupts %x %x", pCtx->rip, EMGetInhibitInterruptsPC(pVCpu));
}
uint32_t check_vm = VM_FF_HWACCM_TO_R3_MASK | VM_FF_REQUEST
| VM_FF_PGM_POOL_FLUSH_PENDING
| VM_FF_PDM_DMA;
uint32_t check_vcpu = VMCPU_FF_HWACCM_TO_R3_MASK
| VMCPU_FF_PGM_SYNC_CR3
| VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL
| VMCPU_FF_REQUEST;
if (VM_FF_ISPENDING(pVM, check_vm)
|| VMCPU_FF_ISPENDING(pVCpu, check_vcpu))
{
Assert(VM_FF_ISPENDING(pVM, VM_FF_HWACCM_TO_R3_MASK) ||
VMCPU_FF_ISPENDING(pVCpu, VMCPU_FF_HWACCM_TO_R3_MASK));
if (RT_UNLIKELY(VM_FF_ISPENDING(pVM, VM_FF_PGM_NO_MEMORY)))
{
PERR(" no memory");
while (1) {}
}
// PERR(" em raw to r3");
return VINF_EM_RAW_TO_R3;
}
if ((utcb->intr_state & 3))
PERR("irq window with intr_state %x", utcb->intr_state);
irq_win(utcb, pVCpu);
goto ResumeExecution;
}
default:
PERR("unknown exit cond:ip:qual[0],[1] %lx:%lx:%llx:%llx",
_current_exit_cond, utcb->ip, utcb->qual[0], utcb->qual[1]);
while (1) {}
}
return VERR_INTERNAL_ERROR;
} }
}; };

View File

@ -19,25 +19,10 @@ class Vcpu_handler_svm : public Vcpu_handler
{ {
private: private:
__attribute__((noreturn)) void _svm_default() { _default_handler(); }
__attribute__((noreturn)) void _svm_vintr() { __attribute__((noreturn)) void _svm_vintr() {
_default_handler(SVM_EXIT_VINTR); _irq_window(SVM_EXIT_VINTR);
}
__attribute__((noreturn)) void _svm_rdtsc() {
_default_handler(SVM_EXIT_RDTSC);
}
__attribute__((noreturn)) void _svm_msr() {
_default_handler(SVM_EXIT_MSR);
}
__attribute__((noreturn)) void _svm_recall()
{
_default_handler(SVM_INVALID);
}
__attribute__((noreturn)) void _svm_halt()
{
_default_handler(SVM_EXIT_HLT);
} }
__attribute__((noreturn)) void _svm_ioio() __attribute__((noreturn)) void _svm_ioio()
@ -53,18 +38,14 @@ class Vcpu_handler_svm : public Vcpu_handler
PERR("invalid gueststate"); PERR("invalid gueststate");
/* deadlock here */
_signal_vcpu.lock();
utcb->ctrl[0] = ctrl0; utcb->ctrl[0] = ctrl0;
utcb->ctrl[1] = 0; utcb->ctrl[1] = 0;
utcb->mtd = Mtd::CTRL; utcb->mtd = Mtd::CTRL;
Nova::reply(myself->stack_top()); Nova::reply(_stack_reply);
} }
_default_handler(SVM_EXIT_IOIO); _default_handler();
} }
template <unsigned X> template <unsigned X>
@ -84,40 +65,43 @@ class Vcpu_handler_svm : public Vcpu_handler
{ {
using namespace Nova; using namespace Nova;
Genode::Thread_base *myself = Genode::Thread_base::myself(); /* enable VM exits for CPUID */
Utcb *utcb = reinterpret_cast<Utcb *>(myself->utcb()); next_utcb.mtd = Nova::Mtd::CTRL;
next_utcb.ctrl[0] = SVM_CTRL1_INTERCEPT_CPUID;
next_utcb.ctrl[1] = 0;
/* we are ready, unlock our creator */ void *exit_status = _start_routine(_arg);
_lock_startup.unlock(); pthread_exit(exit_status);
/* wait until EMT thread say so */ Nova::reply(nullptr);
_signal_vcpu.lock();
Nova::reply(myself->stack_top());
} }
public: public:
Vcpu_handler_svm() Vcpu_handler_svm(size_t stack_size, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg)
: Vcpu_handler(stack_size, attr, start_routine, arg)
{ {
using namespace Nova; using namespace Nova;
typedef Vcpu_handler_svm This; typedef Vcpu_handler_svm This;
register_handler<RECALL, This, register_handler<RECALL, This,
&This::_svm_recall>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU)); &This::_svm_default>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
register_handler<SVM_EXIT_IOIO, This, register_handler<SVM_EXIT_IOIO, This,
&This::_svm_ioio> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU)); &This::_svm_ioio> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
register_handler<SVM_EXIT_VINTR, This, register_handler<SVM_EXIT_VINTR, This,
&This::_svm_vintr> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU)); &This::_svm_vintr> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
register_handler<SVM_EXIT_RDTSC, This, register_handler<SVM_EXIT_RDTSC, This,
&This::_svm_rdtsc> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU)); &This::_svm_default> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
register_handler<SVM_EXIT_MSR, This, register_handler<SVM_EXIT_MSR, This,
&This::_svm_msr> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU)); &This::_svm_default> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
register_handler<SVM_NPT, This, register_handler<SVM_NPT, This,
&This::_svm_npt<SVM_NPT>>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU)); &This::_svm_npt<SVM_NPT>>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
register_handler<SVM_EXIT_HLT, This, register_handler<SVM_EXIT_HLT, This,
&This::_svm_halt>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU)); &This::_svm_default>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
register_handler<SVM_EXIT_CPUID, This,
&This::_svm_default> (vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));
register_handler<VCPU_STARTUP, This, register_handler<VCPU_STARTUP, This,
&This::_svm_startup>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU)); &This::_svm_startup>(vcpu().exc_base(), Mtd(Mtd::ALL | Mtd::FPU));

View File

@ -40,36 +40,24 @@ class Vcpu_handler_vmx : public Vcpu_handler
utcb->qual[1] & ~((1UL << 12) - 1)); utcb->qual[1] & ~((1UL << 12) - 1));
} }
__attribute__((noreturn)) void _vmx_default() { _default_handler(); }
__attribute__((noreturn)) void _vmx_startup() __attribute__((noreturn)) void _vmx_startup()
{ {
Vmm::printf("%s\n", __func__);
using namespace Nova; using namespace Nova;
Genode::Thread_base *myself = Genode::Thread_base::myself(); Genode::Thread_base *myself = Genode::Thread_base::myself();
Utcb *utcb = reinterpret_cast<Utcb *>(myself->utcb()); Utcb *utcb = reinterpret_cast<Utcb *>(myself->utcb());
/* we are ready, unlock our creator */
_lock_startup.unlock();
/* wait until EMT thread say so */
_signal_vcpu.lock();
/* avoid as many as possible VM exits */ /* avoid as many as possible VM exits */
utcb->mtd |= Mtd::CTRL; next_utcb.mtd = Nova::Mtd::CTRL;
utcb->ctrl[0] = 0; next_utcb.ctrl[0] = 0;
utcb->ctrl[1] = 0; next_utcb.ctrl[1] = 0;
Nova::reply(myself->stack_top()); void *exit_status = _start_routine(_arg);
} pthread_exit(exit_status);
__attribute__((noreturn)) void _vmx_recall() Nova::reply(nullptr);
{
_default_handler(RECALL);
}
__attribute__((noreturn)) void _vmx_pause()
{
_default_handler(EMULATE_INSTR);
} }
__attribute__((noreturn)) void _vmx_triple() __attribute__((noreturn)) void _vmx_triple()
@ -79,64 +67,19 @@ class Vcpu_handler_vmx : public Vcpu_handler
Vmm::printf("triple fault - dead\n"); Vmm::printf("triple fault - dead\n");
_signal_vcpu.lock(); _default_handler();
_default_handler(EMULATE_INSTR);
}
__attribute__((noreturn)) void _vmx_msr_write()
{
_default_handler(EMULATE_INSTR);
}
__attribute__((noreturn)) void _vmx_msr_read()
{
_default_handler(EMULATE_INSTR);
}
__attribute__((noreturn)) void _vmx_ioio()
{
_default_handler(VMX_EXIT_PORT_IO);
}
__attribute__((noreturn)) void _vmx_invalid()
{
_default_handler(VMX_EXIT_ERR_INVALID_GUEST_STATE);
}
__attribute__((noreturn)) void _vmx_init()
{
_default_handler(EMULATE_INSTR);
} }
__attribute__((noreturn)) void _vmx_irqwin() __attribute__((noreturn)) void _vmx_irqwin()
{ {
_default_handler(VMX_EXIT_IRQ_WINDOW); _irq_window(VMX_EXIT_IRQ_WINDOW);
}
__attribute__((noreturn)) void _vmx_hlt()
{
_default_handler(VMX_EXIT_HLT);
}
__attribute__((noreturn)) void _vmx_cpuid()
{
_default_handler(EMULATE_INSTR);
}
__attribute__((noreturn)) void _vmx_rdtsc()
{
_default_handler(EMULATE_INSTR);
}
__attribute__((noreturn)) void _vmx_vmcall()
{
_default_handler(EMULATE_INSTR);
} }
public: public:
Vcpu_handler_vmx() Vcpu_handler_vmx(size_t stack_size, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg)
: Vcpu_handler(stack_size, attr, start_routine, arg)
{ {
using namespace Nova; using namespace Nova;
@ -147,32 +90,33 @@ class Vcpu_handler_vmx : public Vcpu_handler
register_handler<VMX_EXIT_TRIPLE_FAULT, This, register_handler<VMX_EXIT_TRIPLE_FAULT, This,
&This::_vmx_triple> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_triple> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_INIT_SIGNAL, This, register_handler<VMX_EXIT_INIT_SIGNAL, This,
&This::_vmx_init> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_IRQ_WINDOW, This, register_handler<VMX_EXIT_IRQ_WINDOW, This,
&This::_vmx_irqwin> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_irqwin> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_CPUID, This, register_handler<VMX_EXIT_CPUID, This,
&This::_vmx_cpuid> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_HLT, This, register_handler<VMX_EXIT_HLT, This,
&This::_vmx_hlt> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_RDTSC, This, register_handler<VMX_EXIT_RDTSC, This,
&This::_vmx_rdtsc> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_VMCALL, This, register_handler<VMX_EXIT_VMCALL, This,
&This::_vmx_vmcall> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_PORT_IO, This, register_handler<VMX_EXIT_PORT_IO, This,
&This::_vmx_ioio> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_RDMSR, This, register_handler<VMX_EXIT_RDMSR, This,
&This::_vmx_msr_read> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_WRMSR, This, register_handler<VMX_EXIT_WRMSR, This,
&This::_vmx_msr_write> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_ERR_INVALID_GUEST_STATE, This, register_handler<VMX_EXIT_ERR_INVALID_GUEST_STATE, This,
&This::_vmx_invalid> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_PAUSE, This, register_handler<VMX_EXIT_PAUSE, This,
&This::_vmx_pause> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VMX_EXIT_EPT_VIOLATION, This, register_handler<VMX_EXIT_EPT_VIOLATION, This,
&This::_vmx_ept<VMX_EXIT_EPT_VIOLATION>> (exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_ept<VMX_EXIT_EPT_VIOLATION>> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<VCPU_STARTUP, This, &This::_vmx_startup> register_handler<VCPU_STARTUP, This,
(exc_base, Mtd::ALL | Mtd::FPU); &This::_vmx_startup> (exc_base, Mtd::ALL | Mtd::FPU);
register_handler<RECALL, This, &This::_vmx_recall> (exc_base, Mtd::ALL | Mtd::FPU); register_handler<RECALL, This,
&This::_vmx_default> (exc_base, Mtd::ALL | Mtd::FPU);
start(); start();
} }

View File

@ -28,6 +28,14 @@
extern "C" { extern "C" {
/**
* Returns true if a vCPU could be started. If false we run without
* hardware acceleration support.
*/
bool create_emt_vcpu(pthread_t * pthread, size_t stack,
const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg) void *(*start_routine) (void *), void *arg)
{ {
@ -45,6 +53,13 @@ extern "C" {
"limit to %zu Bytes", rtthread->szName, rtthread->cbStack, "limit to %zu Bytes", rtthread->szName, rtthread->cbStack,
stack_size); stack_size);
if (rtthread->enmType == RTTHREADTYPE_EMULATION) {
if (create_emt_vcpu(thread, stack_size, attr, start_routine, arg))
return 0;
/* no haredware support, create normal pthread thread */
}
pthread_t thread_obj = new (Genode::env()->heap()) pthread_t thread_obj = new (Genode::env()->heap())
pthread(attr ? *attr : 0, start_routine, pthread(attr ? *attr : 0, start_routine,
arg, stack_size, rtthread->szName, nullptr); arg, stack_size, rtthread->szName, nullptr);