vbox5: use vm_session interface

Issue #3111
This commit is contained in:
Alexander Boettcher 2019-05-06 10:34:47 +02:00 committed by Christian Helmuth
parent 07ee9654e4
commit 5673c163fb
40 changed files with 3366 additions and 229 deletions

View File

@ -2,7 +2,7 @@ include $(REP_DIR)/lib/mk/virtualbox5-common.inc
LIBS += stdcxx
SRC_CC = sup.cc pgm.cc
SRC_CC = sup.cc pgm.cc sup_old.cc
INC_DIR += $(call select_from_repositories,src/lib/libc)
@ -16,5 +16,6 @@ INC_DIR += $(REP_DIR)/src/virtualbox5/frontend
vpath sup.cc $(REP_DIR)/src/virtualbox5/spec/nova/
vpath pgm.cc $(REP_DIR)/src/virtualbox5/spec/nova/
vpath sup_old.cc $(REP_DIR)/src/virtualbox5/spec/nova/
CC_CXX_WARN_STRICT =

View File

@ -2,7 +2,7 @@ include $(REP_DIR)/lib/mk/virtualbox5-common.inc
LIBS += stdcxx
SRC_CC = sup.cc
SRC_CC = sup.cc sup_vmm.cc
INC_DIR += $(call select_from_repositories,src/lib/libc)
@ -13,9 +13,8 @@ INC_DIR += $(VBOX_DIR)/Main/include
INC_DIR += $(VBOX_DIR)/VMM/include
INC_DIR += $(REP_DIR)/src/virtualbox5
INC_DIR += $(REP_DIR)/src/virtualbox5/frontend
INC_DIR += $(REP_DIR)/src/virtualbox5/accloff
#vpath pgm.cc $(REP_DIR)/src/virtualbox5/
vpath sup.cc $(REP_DIR)/src/virtualbox5/accloff/
vpath sup_vmm.cc $(REP_DIR)/src/virtualbox5/generic
vpath sup.cc $(REP_DIR)/src/virtualbox5/generic
CC_CXX_WARN_STRICT =

View File

@ -1 +1 @@
0db2b02901c5b62cd181d817ba71c3b0d417a40a
0ccf5764631d03e73d2a952391c96fe457f6f5ee

View File

@ -0,0 +1,84 @@
LIB_MK_FILES := $(notdir $(wildcard $(REP_DIR)/lib/mk/virtualbox5*))
MIRROR_FROM_REP_DIR := src/virtualbox5 \
src/virtualbox5/network.cpp \
src/virtualbox5/include \
include/vmm \
$(addprefix lib/mk/,$(LIB_MK_FILES))
content: $(MIRROR_FROM_REP_DIR)
$(MIRROR_FROM_REP_DIR):
$(mirror_from_rep_dir)
# omit virtualbox5-rem binary (12 MiB) from binary archive
content: disable_virtualbox_nova disable_assertions
disable_virtualbox_nova: $(MIRROR_FROM_REP_DIR)
rm src/virtualbox5/nova/target.mk
rmdir src/virtualbox5/nova
rm -r src/virtualbox5/spec
disable_assertions: $(MIRROR_FROM_REP_DIR)
rm lib/mk/virtualbox5-debug.inc
touch lib/mk/virtualbox5-debug.inc
PORT_DIR := $(call port_dir,$(REP_DIR)/ports/virtualbox5)
MIRROR_FROM_PORT_DIR := src/app/virtualbox src/app/virtualbox_sdk \
VBoxAPIWrap VirtualBox_stripped.xidl
content: $(MIRROR_FROM_PORT_DIR)
$(MIRROR_FROM_PORT_DIR):
mkdir -p $(dir $@)
cp -r $(PORT_DIR)/$@ $(dir $@)
MIRROR_FROM_LIBPORTS := lib/mk/libc_pipe.mk \
src/lib/libc_pipe \
lib/mk/libc_terminal.mk \
src/lib/libc_terminal \
lib/mk/libc-mem.mk \
lib/mk/libc-common.inc \
src/lib/libc/libc_mem_alloc.cc \
src/lib/libc/libc_mem_alloc.h \
src/lib/libc/libc_init.h \
src/lib/libc/thread_create.h \
src/lib/libc/thread.h \
include/libc-plugin \
lib/import/import-qemu-usb_include.mk \
lib/mk/qemu-usb_include.mk \
lib/mk/qemu-usb.mk \
include/qemu \
src/lib/qemu-usb
content: $(MIRROR_FROM_LIBPORTS)
$(MIRROR_FROM_LIBPORTS):
mkdir -p $(dir $@)
cp -r $(GENODE_DIR)/repos/libports/$@ $(dir $@)
QEMU_USB_PORT_DIR := $(call port_dir,$(GENODE_DIR)/repos/libports/ports/qemu-usb)
MIRROR_FROM_QEMU_USB_PORT_DIR := src/lib/qemu
content: $(MIRROR_FROM_QEMU_USB_PORT_DIR)
$(MIRROR_FROM_QEMU_USB_PORT_DIR):
mkdir -p $(dir $@)
cp -r $(QEMU_USB_PORT_DIR)/$@ $(dir $@)
MIRROR_FROM_OS := src/drivers/input/spec/ps2/scan_code_set_1.h \
include/pointer/shape_report.h \
content: $(MIRROR_FROM_OS)
$(MIRROR_FROM_OS):
mkdir -p $(dir $@)
cp -r $(GENODE_DIR)/repos/os/$@ $(dir $@)
content: LICENSE
LICENSE:
echo "GNU GPL version 2, see src/app/virtualbox/COPYING" > $@

View File

@ -0,0 +1 @@
2019-05-20-d d04357716c16c4a70e53bba190ef5a73c4e64b51

View File

@ -0,0 +1,19 @@
base
os
libc
so
vfs
libiconv
stdcxx
timer_session
usb_session
terminal_session
audio_in_session
audio_out_session
nic_session
input_session
framebuffer_session
report_session
nitpicker_session
rtc_session
vm_session

View File

@ -32,7 +32,7 @@
<Memory RAMSize="256" PageFusion="false"/>
<HID Pointing="PS2Mouse" Keyboard="PS2Keyboard"/>
<HPET enabled="false"/>
<Chipset type="PIIX3"/>
<Chipset type="ICH9"/>
<Boot>
<Order position="1" device="Floppy"/>
<Order position="2" device="DVD"/>

View File

@ -19,4 +19,7 @@ set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 1
# use non-generic vbox5 VMM version
set use_vbox5_nova 1
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -21,4 +21,7 @@ set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 1
# use non-generic vbox5 VMM version
set use_vbox5_nova 1
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -19,4 +19,7 @@ set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 1
# use non-generic vbox5 VMM version
set use_vbox5_nova 1
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -21,4 +21,7 @@ set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 1
# use non-generic vbox5 VMM version
set use_vbox5_nova 1
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -0,0 +1,25 @@
#
# Ubuntu 16.04 32bit in VBox 5
#
set flavor "ubuntu_16_04_32"
set vm_ram "1280M"
set use_vbox5 1
set use_rumpfs 1
# Write overlay only into ram
set use_ram_fs 1
# However read initial overlay from disk
set use_overlay_from_disk 1
set use_usb 1
set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 1
# use generic vbox5 VMM version
set use_vbox5_nova 0
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -0,0 +1,27 @@
#
# Ubuntu 16.04 64bit in VBox 5
#
assert_spec 64bit
set flavor "ubuntu_16_04_64"
set vm_ram "9460M"
set use_vbox5 1
set use_rumpfs 1
# Write overlay only into ram
set use_ram_fs 1
# However read initial overlay from disk
set use_overlay_from_disk 1
set use_usb 1
set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 1
# use generic vbox5 VMM version
set use_vbox5_nova 0
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -0,0 +1,27 @@
#
# Windows 7 in VirtualBox 5
#
assert_spec nova
set flavor "win7"
set vm_ram "1280M"
set use_vbox5 1
set use_rumpfs 1
# Write overlay only into ram
set use_ram_fs 1
# However read initial overlay from disk
set use_overlay_from_disk 1
set use_usb 1
set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 0
# use generic vbox5 VMM version
set use_vbox5_nova 0
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -0,0 +1,27 @@
#
# Windows 7 64bit in VirtualBox
#
assert_spec 64bit
set flavor "win7_64"
set vm_ram "9480M"
set use_vbox5 1
set use_rumpfs 1
# Write overlay only into ram
set use_ram_fs 1
# However read initial overlay from disk
set use_overlay_from_disk 1
set use_usb 1
set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 0
# use generic vbox5 VMM version
set use_vbox5_nova 0
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -22,4 +22,7 @@ set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 0
# use non-generic vbox5 VMM version
set use_vbox5_nova 1
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -21,4 +21,7 @@ set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 0
# use non-generic vbox5 VMM version
set use_vbox5_nova 1
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -21,4 +21,7 @@ set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 0
# use non-generic vbox5 VMM version
set use_vbox5_nova 1
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -33,4 +33,7 @@ set use_vms 6
set affinity_space_width 8
set use_cpu_load 0
# use non-generic vbox5 VMM version
set use_vbox5_nova 1
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -20,4 +20,7 @@ set use_ps2 [have_spec ps2]
set use_vms 1
set use_cpu_load 0
# use non-generic vbox5 VMM version
set use_vbox5_nova 1
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -39,11 +39,8 @@ set boot_modules {
dynamic_rom
}
set virtualbox5_binary "virtualbox5-rem"
if {[have_spec nova]} {
set virtualbox5_binary "virtualbox5-nova"
}
set virtualbox5_binary "virtualbox5"
if {$use_vbox5_nova} { set virtualbox5_binary "virtualbox5-nova" }
set config_of_app {

View File

@ -2,8 +2,9 @@ set use_net 0
set use_ps2 [have_spec ps2]
set use_usb 0
set use_serial 1
set use_top 1
set use_vbox5 1
set use_vbox5_nova 0
# use_gui starts two VMs
set use_gui 0
@ -15,10 +16,12 @@ set build_components {
server/report_rom
}
if {$use_vbox5} {
append build_components virtualbox5
set virtualbox_binary "virtualbox5-rem"
if {[have_spec nova]} { set virtualbox_binary "virtualbox5-nova" }
append build_components virtualbox5
set virtualbox_binary "virtualbox5"
if {$use_vbox5_nova} {
set virtualbox_binary "virtualbox5-nova"
}
create_boot_directory
@ -31,6 +34,7 @@ source ${genode_dir}/repos/base/run/platform_drv.inc
# override defaults of platform_drv.inc
proc platform_drv_priority {} { return { priority="-1"} }
lappend_if [expr $use_top] build_components app/top
lappend_if [expr $use_ps2] build_components drivers/input
lappend_if [expr $use_usb] build_components drivers/usb
lappend_if [expr $use_serial] build_components server/log_terminal
@ -53,11 +57,12 @@ set config {
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>}
append_if [have_spec muen] config {
<service name="LOG"/>
<service name="VM"/>}
append_if [expr $use_top] config {
<service name="TRACE"/>}
append config {
</parent-provides>
<default-route>
@ -69,11 +74,17 @@ append config {
<provides><service name="Timer"/></provides>
</start>}
append_if [expr $use_top] config {
<start name="top">
<resource name="RAM" quantum="2M"/>
<config period_ms="15000"/>
</start>}
append_platform_drv_config
append_if [expr $use_ps2] config {
<start name="ps2_drv" priority="-1">
<resource name="RAM" quantum="1M"/>
<resource name="RAM" quantum="2M"/>
<provides><service name="Input"/></provides>
</start>}
@ -82,13 +93,8 @@ append_if [expr $use_usb] config {
<resource name="RAM" quantum="12M"/>
<provides><service name="Input"/></provides>}
append_if [expr $use_usb && ![have_spec muen]] config {
<config uhci="yes" ehci="yes" xhci="yes">}
append_if [expr $use_usb && [have_spec muen]] config {
<config uhci="no" ehci="no" xhci="yes">}
append_if [expr $use_usb] config {
<config uhci="yes" ehci="yes" xhci="yes">
<hid/>
</config>
</start>}
@ -97,8 +103,12 @@ append_if [have_spec framebuffer] config {
<start name="fb_drv" priority="-1" caps="150">
<binary name="vesa_fb_drv"/>
<resource name="RAM" quantum="4M"/>
<provides><service name="Framebuffer"/></provides>
<config width="1280" height="1024" depth="16" buffered="yes"/>
<provides><service name="Framebuffer"/></provides>}
append_if [expr [have_spec framebuffer] && [have_include power_on/qemu]] config {
<config width="1280" height="1024" depth="16" buffered="yes"/>}
append_if [expr [have_spec framebuffer] && [have_include power_on/qemu]] config {
<config buffered="yes"/>}
append_if [have_spec framebuffer] config {
</start>}
append_if [have_spec sdl] config {
@ -196,11 +206,8 @@ append config {
</start>}
append_if [expr $use_gui] config {
<start name="vbox2" priority="-2" caps="500">}
append_if [expr $use_gui] config "
<binary name=\"$virtualbox_binary\"/>"
append_if [expr $use_gui] config {
<start name="vbox2" priority="-2" caps="800">
<binary name="} $virtualbox_binary {"/>
<resource name="RAM" quantum="448M"/>
<config vbox_file="test.vbox" vm_name="TestVM">
<libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc"/>
@ -221,10 +228,8 @@ append_if [expr $use_gui] config {
</start>}
append config {
<start name="vbox1" priority="-2" caps="500">}
append config "
<binary name=\"$virtualbox_binary\"/>"
append config {
<start name="vbox1" priority="-2" caps="800">
<binary name="} $virtualbox_binary {"/>
<resource name="RAM" quantum="448M"/>
<config vbox_file="test.vbox" vm_name="TestVM">
<libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc"/>
@ -269,7 +274,7 @@ lappend_if [expr $use_ps2] boot_modules ps2_drv
lappend_if [have_spec framebuffer] boot_modules vesa_fb_drv
lappend_if [have_spec linux] boot_modules fb_sdl
lappend_if [have_spec x86] boot_modules rtc_drv
lappend_if [expr $use_top] boot_modules top
lappend_if [expr $use_gui] boot_modules report_rom
append boot_modules {

View File

@ -81,9 +81,7 @@ append config {
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>}
append_if [have_spec muen] config {
<service name="LOG"/>
<service name="VM"/>}
append_if [expr $use_cpu_load] config {

View File

@ -1,163 +0,0 @@
/*
* \brief Genode specific VirtualBox SUPLib supplements.
* File used by Genode platforms not supporting hardware
* virtualisation features.
* \author Alexander Boettcher
* \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/log.h>
#include <base/semaphore.h>
#include <timer_session/connection.h>
/* VirtualBox includes */
#include <VBox/vmm/vm.h>
#include <VBox/err.h>
/* Genode's VirtualBox includes */
#include "sup.h"
#include "vmm.h"
/* Libc include */
#include <pthread.h>
#include <sched.h> /* sched_yield */
/* VirtualBox SUPLib interface */
int SUPR3QueryVTxSupported(void)
{
return VERR_INTERNAL_ERROR;
}
int SUPR3CallVMMR0Fast(PVMR0 pVMR0, unsigned uOperation, VMCPUID idCpu)
{
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:
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:
return VINF_SUCCESS;
case VMMR0_DO_GVMM_SCHED_POLL:
/* called by 'vmR3HaltGlobal1Halt' */
Genode::log(__func__, ": SUPR3CallVMMR0Ex: VMMR0_DO_GVMM_SCHED_POLL");
return VINF_SUCCESS;
default:
Genode::error("SUPR3CallVMMR0Ex: unhandled uOperation ", (int)uOperation);
return VERR_GENERAL_FAILURE;
}
}
bool create_emt_vcpu(pthread_t * thread, size_t stack_size,
void *(*start_routine)(void *), void *arg,
Genode::Cpu_session * cpu_session,
Genode::Affinity::Location location,
unsigned int cpu_id,
const char * name)
{
/* no hardware acceleration support */
return false;
}
/**
* Dummies and unimplemented stuff.
*/
uint64_t genode_cpu_hz() {
return 1000000000ULL; /* XXX fixed 1GHz return value */
}
void genode_update_tsc(void (*update_func)(void), Genode::uint64_t update_us)
{
using namespace Genode;
Timer::Connection timer(genode_env());
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);
}
}
HRESULT genode_setup_machine(ComObjPtr<Machine> machine)
{
ULONG memory_vbox;
HRESULT rc = machine->COMGETTER(MemorySize)(&memory_vbox);
if (FAILED(rc))
return rc;
size_t const vmm_memory = 1024ULL * 1024 * (memory_vbox + 16);
return genode_check_memory_config(machine, vmm_memory);
}
extern "C" int sched_yield(void)
{
Genode::warning(__func__, " unimplemented");
return -1;
}
int SUPR3PageAllocEx(::size_t cPages, uint32_t fFlags, void **ppvPages,
PRTR0PTR pR0Ptr, PSUPPAGE paPages)
{
Genode::error(__func__, " unimplemented");
return VERR_GENERAL_FAILURE;
}
extern "C" bool PGMUnmapMemoryGenode(void *, ::size_t)
{
Genode::error(__func__, " unimplemented");
return VERR_GENERAL_FAILURE;
}
extern "C" void PGMFlushVMMemory()
{
Genode::error(__func__, " unimplemented");
}

View File

@ -0,0 +1,227 @@
/*
* \brief VirtualBox SUPLib supplements
* \author Norman Feske
* \date 2013-08-20
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/attached_ram_dataspace.h>
#include <trace/timestamp.h>
/* Genode/Virtualbox includes */
#include "sup.h"
#include "vmm.h"
/* VirtualBox includes */
#include <iprt/ldr.h>
#include <iprt/semaphore.h>
#include <VBox/err.h>
SUPR3DECL(SUPPAGINGMODE) SUPR3GetPagingMode(void)
{
return sizeof(void *) == 4 ? SUPPAGINGMODE_32_BIT : SUPPAGINGMODE_AMD64_NX;
}
int SUPR3Term(bool) { return VINF_SUCCESS; }
int SUPR3HardenedLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod,
uint32_t fFlags, PRTERRINFO pErrInfo)
{
return RTLdrLoad(pszFilename, phLdrMod);
}
SUPR3DECL(int) SUPR3PageFreeEx(void *pvPages, size_t cPages)
{
Genode::log(__func__, " pvPages=", pvPages, " pages=", cPages);
return VINF_SUCCESS;
}
int SUPR3QueryMicrocodeRev(uint32_t *puMicrocodeRev)
{
return E_FAIL;
}
uint32_t SUPSemEventMultiGetResolution(PSUPDRVSESSION)
{
return 100000*10; /* called by 'vmR3HaltGlobal1Init' */
}
int SUPSemEventCreate(PSUPDRVSESSION pSession, PSUPSEMEVENT phEvent)
{
return RTSemEventCreate((PRTSEMEVENT)phEvent);
}
int SUPSemEventClose(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent)
{
Assert (hEvent);
return RTSemEventDestroy((RTSEMEVENT)hEvent);
}
int SUPSemEventSignal(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent)
{
Assert (hEvent);
return RTSemEventSignal((RTSEMEVENT)hEvent);
}
int SUPSemEventWaitNoResume(PSUPDRVSESSION pSession, SUPSEMEVENT hEvent,
uint32_t cMillies)
{
Assert (hEvent);
return RTSemEventWaitNoResume((RTSEMEVENT)hEvent, cMillies);
}
int SUPSemEventMultiCreate(PSUPDRVSESSION, PSUPSEMEVENTMULTI phEventMulti)
{
RTSEMEVENTMULTI sem;
/*
* Input validation.
*/
AssertPtrReturn(phEventMulti, VERR_INVALID_POINTER);
/*
* Create the event semaphore object.
*/
int rc = RTSemEventMultiCreate(&sem);
static_assert(sizeof(sem) == sizeof(*phEventMulti), "oi");
*phEventMulti = reinterpret_cast<SUPSEMEVENTMULTI>(sem);
return rc;
}
int SUPSemEventMultiWaitNoResume(PSUPDRVSESSION, SUPSEMEVENTMULTI event,
uint32_t ms)
{
RTSEMEVENTMULTI const rtevent = reinterpret_cast<RTSEMEVENTMULTI>(event);
return RTSemEventMultiWait(rtevent, ms);
}
int SUPSemEventMultiSignal(PSUPDRVSESSION, SUPSEMEVENTMULTI event) {
return RTSemEventMultiSignal(reinterpret_cast<RTSEMEVENTMULTI>(event)); }
int SUPSemEventMultiReset(PSUPDRVSESSION, SUPSEMEVENTMULTI event) {
return RTSemEventMultiReset(reinterpret_cast<RTSEMEVENTMULTI>(event)); }
int SUPSemEventMultiClose(PSUPDRVSESSION, SUPSEMEVENTMULTI event) {
return RTSemEventMultiDestroy(reinterpret_cast<RTSEMEVENTMULTI>(event)); }
int SUPR3CallVMMR0(PVMR0 pVMR0, VMCPUID idCpu, unsigned uOperation,
void *pvArg)
{
if (uOperation == VMMR0_DO_CALL_HYPERVISOR) {
Genode::log(__func__, ": VMMR0_DO_CALL_HYPERVISOR - doing nothing");
return VINF_SUCCESS;
}
if (uOperation == VMMR0_DO_VMMR0_TERM) {
Genode::log(__func__, ": VMMR0_DO_VMMR0_TERM - doing nothing");
return VINF_SUCCESS;
}
if (uOperation == VMMR0_DO_GVMM_DESTROY_VM) {
Genode::log(__func__, ": VMMR0_DO_GVMM_DESTROY_VM - doing nothing");
return VINF_SUCCESS;
}
AssertMsg(uOperation != VMMR0_DO_VMMR0_TERM &&
uOperation != VMMR0_DO_CALL_HYPERVISOR &&
uOperation != VMMR0_DO_GVMM_DESTROY_VM,
("SUPR3CallVMMR0: unhandled uOperation %d", uOperation));
return VERR_GENERAL_FAILURE;
}
void genode_VMMR0_DO_GVMM_CREATE_VM(PSUPVMMR0REQHDR pReqHdr)
{
GVMMCREATEVMREQ &req = reinterpret_cast<GVMMCREATEVMREQ &>(*pReqHdr);
size_t const cCpus = req.cCpus;
/*
* Allocate and initialize VM struct
*
* The VM struct is followed by the variable-sizedA array of VMCPU
* objects. 'RT_UOFFSETOF' is used to determine the size including
* the VMCPU array.
*
* VM struct must be page-aligned, which is checked at least in
* PDMR3CritSectGetNop().
*/
size_t const cbVM = RT_UOFFSETOF(VM, aCpus[cCpus]);
static Genode::Attached_ram_dataspace vm(genode_env().ram(),
genode_env().rm(),
cbVM);
Assert (vm.size() >= cbVM);
VM *pVM = vm.local_addr<VM>();
Genode::memset(pVM, 0, cbVM);
/*
* On Genode, VMMR0 and VMMR3 share a single address space. Hence, the
* same pVM pointer is valid as pVMR0 and pVMR3.
*/
pVM->enmVMState = VMSTATE_CREATING;
pVM->pVMR0 = (RTHCUINTPTR)pVM;
pVM->pVMRC = (RTGCUINTPTR)pVM;
pVM->pSession = req.pSession;
pVM->cbSelf = cbVM;
pVM->cCpus = cCpus;
pVM->uCpuExecutionCap = 100; /* expected by 'vmR3CreateU()' */
pVM->offVMCPU = RT_UOFFSETOF(VM, aCpus);
for (uint32_t i = 0; i < cCpus; i++) {
pVM->aCpus[i].pVMR0 = pVM->pVMR0;
pVM->aCpus[i].pVMR3 = pVM;
pVM->aCpus[i].idHostCpu = NIL_RTCPUID;
pVM->aCpus[i].hNativeThreadR0 = NIL_RTNATIVETHREAD;
}
pVM->aCpus[0].hNativeThreadR0 = RTThreadNativeSelf();
/* out parameters of the request */
req.pVMR0 = pVM->pVMR0;
req.pVMR3 = pVM;
}
void genode_VMMR0_DO_GVMM_REGISTER_VMCPU(PVMR0 pVMR0, VMCPUID idCpu)
{
PVM pVM = reinterpret_cast<PVM>(pVMR0);
pVM->aCpus[idCpu].hNativeThreadR0 = RTThreadNativeSelf();
}
HRESULT genode_check_memory_config(ComObjPtr<Machine>,
size_t const memory_vmm)
{
/* Request max available memory */
size_t const memory_available = genode_env().pd().avail_ram().value;
if (memory_vmm <= memory_available)
return S_OK;
Genode::error("Available memory too low to start the VM - available: ",
memory_available, " MB < ", memory_vmm, " MB requested");
return E_FAIL;
}

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,7 @@
* @param pRam The RAM range.
*/
+#include <base/log.h>
+extern "C" bool PGMUnmapMemoryGenode(void *, size_t size);
+extern "C" bool PGMUnmapMemoryGenode(void *, RTGCPHYS, size_t size);
+
static int pgmHandlerPhysicalSetRamFlagsAndFlushShadowPTs(PVM pVM, PPGMPHYSHANDLER pCur, PPGMRAMRANGE pRam)
{
@ -64,7 +64,7 @@
+ if (pCur->cPages != pRam->cb / 4096)
+ Genode::warning("dubious ? phys=", Genode::Hex(pRam->GCPhys));
+
+ PGMUnmapMemoryGenode(pRam->pvR3, pRam->cb);
+ PGMUnmapMemoryGenode(pRam->pvR3, pRam->GCPhys, pRam->cb);
+ }
+
if (fFlushTLBs)

View File

@ -60,7 +60,8 @@ int PGMR3MapPT(PVM, RTGCPTR GCPtr, uint32_t cb, uint32_t fFlags,
int PGMR3MappingsSize(PVM pVM, uint32_t *pcb)
{
Genode::log(__func__, ": not implemented ", __builtin_return_address(0));
if (verbose)
Genode::log(__func__, ": not implemented ", __builtin_return_address(0));
*pcb = 0;

View File

@ -47,7 +47,7 @@
#include "mm.h"
extern "C" bool PGMUnmapMemoryGenode(void *, ::size_t);
extern "C" bool PGMUnmapMemoryGenode(void *, RTGCPHYS, ::size_t);
extern "C" void PGMFlushVMMemory();
@ -650,7 +650,7 @@ int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned uOperation,
if (last_chunk != chunkid) {
/* revoke mapping from guest VM */
void * vmm_local = reinterpret_cast<void *>(vm_memory().local_addr(chunkid << GMM_CHUNK_SHIFT));
PGMUnmapMemoryGenode(vmm_local, GMM_CHUNK_SIZE);
PGMUnmapMemoryGenode(vmm_local, 0, GMM_CHUNK_SIZE);
last_chunk = chunkid;
}
@ -732,7 +732,7 @@ void genode_update_tsc(void (*update_func)(void), Genode::uint64_t update_us)
}
bool PGMUnmapMemoryGenode(void * vmm_local, ::size_t size)
bool PGMUnmapMemoryGenode(void * vmm_local, RTGCPHYS, ::size_t size)
{
Assert(vmm_local);
@ -761,7 +761,7 @@ bool PGMUnmapMemoryGenode(void * vmm_local, ::size_t size)
extern "C" void PGMFlushVMMemory()
{
PGMUnmapMemoryGenode((void *)vm_memory().local_addr(0), MAX_VM_MEMORY);
PGMUnmapMemoryGenode((void *)vm_memory().local_addr(0), 0, MAX_VM_MEMORY);
}
@ -782,7 +782,7 @@ bool create_emt_vcpu(pthread_t * pthread, ::size_t stack,
void *(*start_routine)(void *), void *arg,
Genode::Cpu_session * cpu_session,
Genode::Affinity::Location location,
unsigned int cpu_id, const char * name)
unsigned int cpu_id, const char * name, long)
{
Genode::Xml_node const features = platform_rom().sub_node("features");
bool const svm = features.attribute_value("svm", false);

View File

@ -386,6 +386,6 @@ HRESULT genode_check_memory_config(ComObjPtr<Machine>,
return S_OK;
Genode::error("Available memory too low to start the VM - available: ",
memory_vmm, "MB < ", memory_available, "MB requested");
memory_available, " MB < ", memory_vmm, " MB requested");
return E_FAIL;
}

View File

@ -37,7 +37,7 @@ bool create_emt_vcpu(pthread_t * pthread, size_t stack,
Genode::Cpu_session * cpu_session,
Genode::Affinity::Location location,
unsigned int cpu_id,
const char * name);
const char * name, long prio);
uint64_t genode_cpu_hz();

View File

@ -0,0 +1,101 @@
/*
* \brief Genode specific VirtualBox SUPLib supplements
* \author Norman Feske
* \author Alexander Boettcher
*/
/*
* Copyright (C) 2013-2019 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _VIRTUALBOX__SVM_H_
#define _VIRTUALBOX__SVM_H_
/* based on HWSVMR0.h - adjusted to Genode */
#define GENODE_SVM_ASSERT_SELREG(REG) \
AssertMsg(!pCtx->REG.Attr.n.u1Present || \
(pCtx->REG.Attr.n.u1Granularity \
? (pCtx->REG.u32Limit & 0xfffU) == 0xfffU \
: pCtx->REG.u32Limit <= 0xfffffU), \
("%u %u %#x %#x %#llx\n", pCtx->REG.Attr.n.u1Present, \
pCtx->REG.Attr.n.u1Granularity, pCtx->REG.u32Limit, \
pCtx->REG.Attr.u, pCtx->REG.u64Base))
#define GENODE_READ_SELREG(REG) \
pCtx->REG.Sel = state->REG.value().sel; \
pCtx->REG.ValidSel = state->REG.value().sel; \
pCtx->REG.fFlags = CPUMSELREG_FLAGS_VALID; \
pCtx->REG.u32Limit = state->REG.value().limit; \
pCtx->REG.u64Base = state->REG.value().base; \
pCtx->REG.Attr.u = sel_ar_conv_from_genode(state->REG.value().ar)
static inline bool svm_save_state(Genode::Vm_state * state, VM * pVM, PVMCPU pVCpu)
{
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
GENODE_READ_SELREG(cs);
GENODE_READ_SELREG(ds);
GENODE_READ_SELREG(es);
GENODE_READ_SELREG(fs);
GENODE_READ_SELREG(gs);
GENODE_READ_SELREG(ss);
GENODE_SVM_ASSERT_SELREG(cs);
GENODE_SVM_ASSERT_SELREG(ds);
GENODE_SVM_ASSERT_SELREG(es);
GENODE_SVM_ASSERT_SELREG(fs);
GENODE_SVM_ASSERT_SELREG(gs);
GENODE_SVM_ASSERT_SELREG(ss);
GENODE_READ_SELREG(ldtr);
GENODE_READ_SELREG(tr);
return true;
}
#undef GENODE_ASSERT_SELREG
#undef GENODE_READ_SELREG
#define GENODE_WRITE_SELREG(REG) \
Assert(pCtx->REG.fFlags & CPUMSELREG_FLAGS_VALID); \
Assert(pCtx->REG.ValidSel == pCtx->REG.Sel); \
state->REG.value(Segment{pCtx->REG.Sel, sel_ar_conv_to_genode(pCtx->REG.Attr.u), \
pCtx->REG.u32Limit, pCtx->REG.u64Base});
static inline bool svm_load_state(Genode::Vm_state * state, VM * pVM, PVMCPU pVCpu)
{
typedef Genode::Vm_state::Segment Segment;
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
state->efer.value(pCtx->msrEFER | MSR_K6_EFER_SVME);
/* unimplemented */
if (CPUMIsGuestInLongModeEx(pCtx))
return false;
state->efer.value(state->efer.value() & ~MSR_K6_EFER_LME);
GENODE_WRITE_SELREG(es);
GENODE_WRITE_SELREG(ds);
GENODE_WRITE_SELREG(fs);
GENODE_WRITE_SELREG(gs);
GENODE_WRITE_SELREG(cs);
GENODE_WRITE_SELREG(ss);
GENODE_WRITE_SELREG(ldtr);
GENODE_WRITE_SELREG(tr);
return true;
}
#undef GENODE_WRITE_SELREG
#endif /* _VIRTUALBOX__SVM_H_ */

View File

@ -8,7 +8,7 @@ CC_WARN += -Wall
SRC_CC = frontend/main.cc frontend/console.cc \
frontend/VirtualBoxErrorInfoImpl.cpp \
devices.cc drivers.cc dummies.cc libc.cc \
logger.cc mm.cc pdm.cc pgm.cc rt.cc sup.cc \
logger.cc mm.cc pdm.cc pgm.cc rt.cc \
hm.cc thread.cc dynlib.cc unimpl.cc
LIBS += base

View File

@ -1,7 +1,10 @@
TARGET = virtualbox5-rem
LIBS += virtualbox5-hwaccl-off
TARGET = virtualbox5
include $(REP_DIR)/src/virtualbox5/target.inc
LIBS += virtualbox5
vpath frontend/% $(REP_DIR)/src/virtualbox5/
vpath %.cc $(REP_DIR)/src/virtualbox5/
CC_CXX_WARN_STRICT =

View File

@ -31,7 +31,18 @@
/* vbox */
#include <internal/thread.h>
static Genode::Cpu_connection * cpu_connection(RTTHREADTYPE type) {
static long prio_class(RTTHREADTYPE const type)
{
unsigned const VIRTUAL_GENODE_VBOX_LEVELS = 16;
static_assert (RTTHREADTYPE_END < VIRTUAL_GENODE_VBOX_LEVELS,
"prio levels exceeds VIRTUAL_GENODE_VBOX_LEVELS");
return (VIRTUAL_GENODE_VBOX_LEVELS - type) *
Genode::Cpu_session::PRIORITY_LIMIT / VIRTUAL_GENODE_VBOX_LEVELS;
}
static Genode::Cpu_connection * cpu_connection(RTTHREADTYPE type)
{
using namespace Genode;
static Cpu_connection * con[RTTHREADTYPE_END - 1];
@ -44,18 +55,12 @@ static Genode::Cpu_connection * cpu_connection(RTTHREADTYPE type) {
if (con[type - 1])
return con[type - 1];
unsigned const VIRTUAL_GENODE_VBOX_LEVELS = 16;
static_assert (RTTHREADTYPE_END < VIRTUAL_GENODE_VBOX_LEVELS,
"prio levels exceeds VIRTUAL_GENODE_VBOX_LEVELS");
long const prio = (VIRTUAL_GENODE_VBOX_LEVELS - type) *
Cpu_session::PRIORITY_LIMIT / VIRTUAL_GENODE_VBOX_LEVELS;
char * data = new (vmm_heap()) char[16];
Genode::snprintf(data, 16, "vbox %u", type);
con[type - 1] = new (vmm_heap()) Cpu_connection(genode_env(), data, prio);
con[type - 1] = new (vmm_heap()) Cpu_connection(genode_env(), data,
prio_class(type));
return con[type - 1];
}
@ -90,7 +95,8 @@ static int create_thread(pthread_t *thread, const pthread_attr_t *attr,
Genode::Affinity::Location location(space.location_of_index(cpu_id));
if (create_emt_vcpu(thread, stack_size, start_routine, arg,
cpu_session, location, cpu_id, rtthread->szName))
cpu_session, location, cpu_id, rtthread->szName,
prio_class(rtthread->enmType)))
return 0;
/*
* The virtualization layer had no need to setup the EMT

View File

@ -0,0 +1,884 @@
/*
* \brief Genode VirtualBox SUPLib supplements
* \author Alexander Boettcher
* \author Norman Feske
* \author Christian Helmuth
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _VIRTUALBOX__VCPU_H_
#define _VIRTUALBOX__VCPU_H_
/* Genode includes */
#include <base/log.h>
#include <util/flex_iterator.h>
#include <util/touch.h>
#include <rom_session/connection.h>
#include <timer_session/connection.h>
#include <vm_session/connection.h>
#include <cpu/vm_state.h>
/* VirtualBox includes */
#include "PGMInternal.h" /* enable access to pgm.s.* */
#include "HMInternal.h" /* enable access to hm.s.* */
#include "CPUMInternal.h" /* enable access to cpum.s.* */
#include <VBox/vmm/vm.h>
#include <VBox/vmm/hm_svm.h>
#include <VBox/err.h>
#include <VBox/vmm/pdmapi.h>
#include <iprt/time.h>
/* Genode's VirtualBox includes */
#include "sup.h"
/* Genode libc pthread binding */
#include "thread.h"
/* LibC includes */
#include <setjmp.h>
#include <VBox/vmm/rem.h>
static bool debug_map_memory = false;
/*
* VirtualBox stores segment attributes in Intel format using a 32-bit
* value. Genode represents the attributes in packed format using a 16-bit
* value.
*/
static inline Genode::uint16_t sel_ar_conv_to_genode(Genode::uint32_t v)
{
return (v & 0xff) | ((v & 0x1f000) >> 4);
}
static inline Genode::uint32_t sel_ar_conv_from_genode(Genode::uint16_t v)
{
return (v & 0xff) | (((uint32_t )v << 4) & 0x1f000);
}
namespace Vcpu_sync
{
struct Session : Genode::Session
{
GENODE_RPC(Rpc_resume, void, resume);
GENODE_RPC(Rpc_request_pause, void, request_pause);
GENODE_RPC_INTERFACE(Rpc_resume, Rpc_request_pause);
};
struct Client : Genode::Rpc_client<Session>
{
Client(Genode::Capability<Session> cap) : Rpc_client<Session>(cap) { }
void resume() { call<Rpc_resume>(); }
void request_pause() { call<Rpc_request_pause>(); }
};
};
class Vcpu_handler : public Genode::List<Vcpu_handler>::Element,
public Genode::Rpc_object<Vcpu_sync::Session, Vcpu_handler>
{
protected:
Genode::Entrypoint _ep;
Genode::Lock _lock;
Genode::Vm_state *_state { nullptr };
/* halt / wakeup handling with timeout support */
Genode::Lock _r0_block_guard;
Genode::Semaphore _r0_block;
Genode::uint64_t _r0_wakeup_abs { 0 };
/* information used for NPT/EPT handling */
Genode::addr_t npt_ept_exit_addr { 0 };
RTGCUINT npt_ept_errorcode { 0 };
bool npt_ept_unmap { false };
/* state machine between EMT and EP thread of a vCPU */
enum State { RUNNING, PAUSED, IRQ_WIN, NPT_EPT } _vm_state { PAUSED };
Vcpu_sync::Client _ep_emt;
private:
X86FXSTATE _guest_fpu_state __attribute__((aligned(0x10)));
bool _irq_win = false;
unsigned const _cpu_id;
PVM _vm { nullptr };
PVMCPU _vcpu { nullptr };
unsigned int _last_inj_info = 0;
unsigned int _last_inj_error = 0;
void fpu_save(char * data) {
Assert(!(reinterpret_cast<Genode::addr_t>(data) & 0xF));
asm volatile ("fxsave %0" : "=m" (*data));
}
void fpu_load(char * data) {
Assert(!(reinterpret_cast<Genode::addr_t>(data) & 0xF));
asm volatile ("fxrstor %0" : : "m" (*data));
}
enum {
REQ_IRQWIN_EXIT = 0x1000U,
IRQ_INJ_VALID_MASK = 0x80000000UL,
IRQ_INJ_NONE = 0U,
/*
* Intel® 64 and IA-32 Architectures Software Developers Manual
* Volume 3C, Chapter 24.4.2.
* May 2012
*/
BLOCKING_BY_STI = 1U << 0,
BLOCKING_BY_MOV_SS = 1U << 1,
ACTIVITY_STATE_ACTIVE = 0U,
INTERRUPT_STATE_NONE = 0U,
};
/*
* 'longjmp()' restores some FPU registers saved by 'setjmp()',
* so we need to save the guest FPU state before calling 'longjmp()'
*/
__attribute__((noreturn)) void _fpu_save_and_longjmp()
{
fpu_save(reinterpret_cast<char *>(&_guest_fpu_state));
}
protected:
int map_memory(Genode::Vm_connection &vm_session,
RTGCPHYS GCPhys, RTGCUINT vbox_fault_reason);
Genode::addr_t _vm_exits = 0;
Genode::addr_t _recall_skip = 0;
Genode::addr_t _recall_req = 0;
Genode::addr_t _recall_inv = 0;
Genode::addr_t _recall_drop = 0;
Genode::addr_t _irq_request = 0;
Genode::addr_t _irq_inject = 0;
Genode::addr_t _irq_drop = 0;
struct {
unsigned intr_state;
unsigned ctrl[2];
} next_utcb;
unsigned _ept_fault_addr_type;
Genode::uint64_t * pdpte_map(VM *pVM, RTGCPHYS cr3);
void switch_to_hw()
{
again:
_ep_emt.resume();
/* wait for next exit */
_lock.lock();
if (_vm_state == IRQ_WIN) {
*_state = Genode::Vm_state {}; /* reset */
_irq_window_pthread();
goto again;
} else
if (_vm_state == NPT_EPT) {
if (npt_ept_unmap) {
Genode::error("NPT/EPT unmap not supported - stop");
while (true) {
_lock.lock();
}
}
Genode::addr_t const gp_map_addr = npt_ept_exit_addr & ~((1UL << 12) - 1);
int res = attach_memory_to_vm(gp_map_addr, npt_ept_errorcode);
if (res == VINF_SUCCESS) {
*_state = Genode::Vm_state {}; /* reset */
goto again;
}
}
if (!(_vm_state == PAUSED || _vm_state == NPT_EPT))
Genode::error("which state we are ? ", (int)_vm_state, " ", Genode::Thread::myself()->name());
Assert(_vm_state == PAUSED || _vm_state == NPT_EPT);
}
void _default_handler()
{
if (_vm_state != RUNNING)
Genode::error(__func__, " _vm_state=", (int)_vm_state, " exit_reason=", Genode::Hex(_state->exit_reason));
Assert(_vm_state == RUNNING);
Assert(_state->actv_state.value() == ACTIVITY_STATE_ACTIVE);
Assert(!(_state->inj_info.value() & IRQ_INJ_VALID_MASK));
_vm_exits ++;
_vm_state = PAUSED;
_lock.unlock();
}
void _recall_handler()
{
if (_vm_state != RUNNING)
Genode::error(__func__, " _vm_state=", (int)_vm_state, " exit_reason=", Genode::Hex(_state->exit_reason));
Assert(_vm_state == RUNNING);
_vm_exits ++;
_recall_inv ++;
Assert(_state->actv_state.value() == ACTIVITY_STATE_ACTIVE);
if (_state->inj_info.value() & IRQ_INJ_VALID_MASK) {
Assert(_state->flags.value() & X86_EFL_IF);
if (_state->intr_state.value() != INTERRUPT_STATE_NONE)
Genode::log("intr state ", Genode::Hex(_state->intr_state.value()),
" ", Genode::Hex(_state->intr_state.value() & 0xf));
Assert(_state->intr_state.value() == INTERRUPT_STATE_NONE);
if (!continue_hw_accelerated())
_recall_drop ++;
/* got recall during irq injection and the guest is ready for
* delivery of IRQ - just continue */
run_vm();
return;
}
/* are we forced to go back to emulation mode ? */
if (!continue_hw_accelerated()) {
/* go back to emulation mode */
_default_handler();
return;
}
/* check whether we have to request irq injection window */
if (check_to_request_irq_window(_vcpu)) {
*_state = Genode::Vm_state {}; /* reset */
_state->inj_info.value(_state->inj_info.value());
_irq_win = true;
run_vm();
return;
}
_default_handler();
return;
}
inline bool vbox_to_state(VM *pVM, PVMCPU pVCpu)
{
typedef Genode::Vm_state::Range Range;
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
_state->ip.value(pCtx->rip);
_state->sp.value(pCtx->rsp);
_state->ax.value(pCtx->rax);
_state->bx.value(pCtx->rbx);
_state->cx.value(pCtx->rcx);
_state->dx.value(pCtx->rdx);
_state->bp.value(pCtx->rbp);
_state->si.value(pCtx->rsi);
_state->di.value(pCtx->rdi);
_state->r8.value(pCtx->r8);
_state->r9.value(pCtx->r9);
_state->r10.value(pCtx->r10);
_state->r11.value(pCtx->r11);
_state->r12.value(pCtx->r12);
_state->r13.value(pCtx->r13);
_state->r14.value(pCtx->r14);
_state->r15.value(pCtx->r15);
_state->flags.value(pCtx->rflags.u);
_state->sysenter_cs.value(pCtx->SysEnter.cs);
_state->sysenter_sp.value(pCtx->SysEnter.esp);
_state->sysenter_ip.value(pCtx->SysEnter.eip);
_state->dr7.value(pCtx->dr[7]);
_state->cr0.value(pCtx->cr0);
_state->cr2.value(pCtx->cr2);
_state->cr3.value(pCtx->cr3);
_state->cr4.value(pCtx->cr4);
_state->idtr.value(Range{pCtx->idtr.pIdt, pCtx->idtr.cbIdt});
_state->gdtr.value(Range{pCtx->gdtr.pGdt, pCtx->gdtr.cbGdt});
_state->efer.value(CPUMGetGuestEFER(pVCpu));
/*
* Update the PDPTE registers if necessary
*
* Intel manual sections 4.4.1 of Vol. 3A and 26.3.2.4 of Vol. 3C
* indicate the conditions when this is the case. The following
* code currently does not check if the recompiler modified any
* CR registers, which means the update can happen more often
* than really necessary.
*/
if (pVM->hm.s.vmx.fSupported &&
CPUMIsGuestPagingEnabledEx(pCtx) &&
CPUMIsGuestInPAEModeEx(pCtx)) {
Genode::uint64_t *pdpte = pdpte_map(pVM, pCtx->cr3);
_state->pdpte_0.value(pdpte[0]);
_state->pdpte_1.value(pdpte[1]);
_state->pdpte_2.value(pdpte[2]);
_state->pdpte_3.value(pdpte[3]);
}
_state->star.value(pCtx->msrSTAR);
_state->lstar.value(pCtx->msrLSTAR);
_state->fmask.value(pCtx->msrSFMASK);
_state->kernel_gs_base.value(pCtx->msrKERNELGSBASE);
/* from HMVMXR0.cpp */
bool interrupt_pending = false;
uint8_t tpr = 0;
uint8_t pending_interrupt = 0;
PDMApicGetTPR(pVCpu, &tpr, &interrupt_pending, &pending_interrupt);
_state->tpr.value(tpr);
_state->tpr_threshold.value(0);
if (interrupt_pending) {
const uint8_t pending_priority = (pending_interrupt >> 4) & 0xf;
const uint8_t tpr_priority = (tpr >> 4) & 0xf;
if (pending_priority <= tpr_priority)
_state->tpr_threshold.value(pending_priority);
else
_state->tpr_threshold.value(tpr_priority);
}
return true;
}
inline bool state_to_vbox(VM *pVM, PVMCPU pVCpu)
{
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
pCtx->rip = _state->ip.value();
pCtx->rsp = _state->sp.value();
pCtx->rax = _state->ax.value();
pCtx->rbx = _state->bx.value();
pCtx->rcx = _state->cx.value();
pCtx->rdx = _state->dx.value();
pCtx->rbp = _state->bp.value();
pCtx->rsi = _state->si.value();
pCtx->rdi = _state->di.value();
pCtx->rflags.u = _state->flags.value();
pCtx->r8 = _state->r8.value();
pCtx->r9 = _state->r9.value();
pCtx->r10 = _state->r10.value();
pCtx->r11 = _state->r11.value();
pCtx->r12 = _state->r12.value();
pCtx->r13 = _state->r13.value();
pCtx->r14 = _state->r14.value();
pCtx->r15 = _state->r15.value();
pCtx->dr[7] = _state->dr7.value();
if (pCtx->SysEnter.cs != _state->sysenter_cs.value())
CPUMSetGuestMsr(pVCpu, MSR_IA32_SYSENTER_CS, _state->sysenter_cs.value());
if (pCtx->SysEnter.esp != _state->sysenter_sp.value())
CPUMSetGuestMsr(pVCpu, MSR_IA32_SYSENTER_ESP, _state->sysenter_sp.value());
if (pCtx->SysEnter.eip != _state->sysenter_ip.value())
CPUMSetGuestMsr(pVCpu, MSR_IA32_SYSENTER_EIP, _state->sysenter_ip.value());
if (pCtx->idtr.cbIdt != _state->idtr.value().limit ||
pCtx->idtr.pIdt != _state->idtr.value().base)
CPUMSetGuestIDTR(pVCpu, _state->idtr.value().base, _state->idtr.value().limit);
if (pCtx->gdtr.cbGdt != _state->gdtr.value().limit ||
pCtx->gdtr.pGdt != _state->gdtr.value().base)
CPUMSetGuestGDTR(pVCpu, _state->gdtr.value().base, _state->gdtr.value().limit);
CPUMSetGuestEFER(pVCpu, _state->efer.value());
if (pCtx->cr0 != _state->cr0.value())
CPUMSetGuestCR0(pVCpu, _state->cr0.value());
if (pCtx->cr2 != _state->cr2.value())
CPUMSetGuestCR2(pVCpu, _state->cr2.value());
if (pCtx->cr3 != _state->cr3.value()) {
CPUMSetGuestCR3(pVCpu, _state->cr3.value());
VMCPU_FF_SET(pVCpu, VMCPU_FF_HM_UPDATE_CR3);
}
if (pCtx->cr4 != _state->cr4.value())
CPUMSetGuestCR4(pVCpu, _state->cr4.value());
if (pCtx->msrSTAR != _state->star.value())
CPUMSetGuestMsr(pVCpu, MSR_K6_STAR, _state->star.value());
if (pCtx->msrLSTAR != _state->lstar.value())
CPUMSetGuestMsr(pVCpu, MSR_K8_LSTAR, _state->lstar.value());
if (pCtx->msrSFMASK != _state->fmask.value())
CPUMSetGuestMsr(pVCpu, MSR_K8_SF_MASK, _state->fmask.value());
if (pCtx->msrKERNELGSBASE != _state->kernel_gs_base.value())
CPUMSetGuestMsr(pVCpu, MSR_K8_KERNEL_GS_BASE, _state->kernel_gs_base.value());
const uint32_t tpr = _state->tpr.value();
/* reset message transfer descriptor for next invocation */
Assert (!(_state->inj_info.value() & IRQ_INJ_VALID_MASK));
next_utcb.intr_state = _state->intr_state.value();
next_utcb.ctrl[0] = _state->ctrl_primary.value();
next_utcb.ctrl[1] = _state->ctrl_secondary.value();
if (next_utcb.intr_state & 3) {
next_utcb.intr_state &= ~3U;
}
VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_TO_R3);
CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_FPU_REM);
pVCpu->cpum.s.fUseFlags |= (CPUM_USED_FPU_GUEST | CPUM_USED_FPU_SINCE_REM);
if (_state->intr_state.value() != 0) {
Assert(_state->intr_state.value() == BLOCKING_BY_STI ||
_state->intr_state.value() == BLOCKING_BY_MOV_SS);
EMSetInhibitInterruptsPC(pVCpu, pCtx->rip);
} else
VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
PDMApicSetTPR(pVCpu, tpr);
return true;
}
inline bool check_to_request_irq_window(PVMCPU pVCpu)
{
if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
return false;
if (!TRPMHasTrap(pVCpu) &&
!VMCPU_FF_IS_PENDING(pVCpu, (VMCPU_FF_INTERRUPT_APIC |
VMCPU_FF_INTERRUPT_PIC)))
return false;
_irq_request++;
unsigned const vector = 0;
_state->inj_info.value(REQ_IRQWIN_EXIT | vector);
return true;
}
void _irq_window()
{
if (_vm_state != RUNNING)
Genode::error(__func__, " _vm_state=", (int)_vm_state, " exit_reason=", Genode::Hex(_state->exit_reason));
Assert(_vm_state == RUNNING);
_vm_exits ++;
_vm_state = IRQ_WIN;
_lock.unlock();
}
void _npt_ept()
{
if (_vm_state != RUNNING)
Genode::error(__func__, " _vm_state=", (int)_vm_state, " exit_reason=", Genode::Hex(_state->exit_reason));
Assert(_vm_state == RUNNING);
_vm_exits ++;
_vm_state = NPT_EPT;
_lock.unlock();
}
void _irq_window_pthread()
{
PVMCPU pVCpu = _vcpu;
Assert(_state->intr_state.value() == INTERRUPT_STATE_NONE);
Assert(_state->flags.value() & X86_EFL_IF);
Assert(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS));
Assert(!(_state->inj_info.value() & IRQ_INJ_VALID_MASK));
Assert(_irq_win);
_irq_win = false;
/* request current tpr state from guest, it may block IRQs */
PDMApicSetTPR(pVCpu, _state->tpr_threshold.value());
if (!TRPMHasTrap(pVCpu)) {
bool res = VMCPU_FF_TEST_AND_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_NMI);
if (res)
Genode::log("NMI was set");
if (VMCPU_FF_IS_PENDING(pVCpu, (VMCPU_FF_INTERRUPT_APIC |
VMCPU_FF_INTERRUPT_PIC))) {
uint8_t irq;
int rc = PDMGetInterrupt(pVCpu, &irq);
Assert(RT_SUCCESS(rc));
rc = TRPMAssertTrap(pVCpu, irq, TRPM_HARDWARE_INT);
Assert(RT_SUCCESS(rc));
}
if (!TRPMHasTrap(pVCpu)) {
_irq_drop++;
/* happens if PDMApicSetTPR (see above) mask IRQ */
_state->inj_info.value(IRQ_INJ_NONE);
Genode::error("virq window pthread aaaaaaa while loop");
return;
}
}
_irq_inject++;
/*
* If we have no IRQ for injection, something with requesting the
* IRQ window went wrong. Probably it was forgotten to be reset.
*/
Assert(TRPMHasTrap(pVCpu));
/* interrupt can be dispatched */
uint8_t u8Vector;
TRPMEVENT enmType;
SVMEVENT Event;
RTGCUINT u32ErrorCode;
RTGCUINTPTR GCPtrFaultAddress;
uint8_t cbInstr;
Event.u = 0;
/* If a new event is pending, then dispatch it now. */
int rc = TRPMQueryTrapAll(pVCpu, &u8Vector, &enmType, &u32ErrorCode, 0, 0);
AssertRC(rc);
Assert(enmType == TRPM_HARDWARE_INT);
Assert(u8Vector != X86_XCPT_NMI);
/* Clear the pending trap. */
rc = TRPMResetTrap(pVCpu);
AssertRC(rc);
Event.n.u8Vector = u8Vector;
Event.n.u1Valid = 1;
Event.n.u32ErrorCode = u32ErrorCode;
Event.n.u3Type = SVM_EVENT_EXTERNAL_IRQ;
_state->inj_info.value(Event.u);
_state->inj_error.value(Event.n.u32ErrorCode);
_last_inj_info = _state->inj_info.value();
_last_inj_error = _state->inj_error.value();
/*
Genode::log("type:info:vector ", Genode::Hex(Event.n.u3Type),
Genode::Hex(utcb->inj_info), Genode::Hex(u8Vector),
" intr:actv - ", Genode::Hex(utcb->intr_state),
Genode::Hex(utcb->actv_state), " mtd ",
Genode::Hex(utcb->mtd));
*/
}
inline bool continue_hw_accelerated(bool verbose = false)
{
uint32_t check_vm = VM_FF_HM_TO_R3_MASK | VM_FF_REQUEST
| VM_FF_PGM_POOL_FLUSH_PENDING
| VM_FF_PDM_DMA;
uint32_t check_vcpu = VMCPU_FF_HM_TO_R3_MASK
| VMCPU_FF_PGM_SYNC_CR3
| VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL
| VMCPU_FF_REQUEST;
if (!VM_FF_IS_PENDING(_vm, check_vm) &&
!VMCPU_FF_IS_PENDING(_vcpu, check_vcpu))
return true;
Assert(!(VM_FF_IS_PENDING(_vm, VM_FF_PGM_NO_MEMORY)));
#define VERBOSE_VM(flag) \
do { \
if (VM_FF_IS_PENDING(_vm, flag)) \
Genode::log("flag ", flag, " pending"); \
} while (0)
#define VERBOSE_VMCPU(flag) \
do { \
if (VMCPU_FF_IS_PENDING(_vcpu, flag)) \
Genode::log("flag ", flag, " pending"); \
} while (0)
if (verbose) {
/*
* VM_FF_HM_TO_R3_MASK
*/
VERBOSE_VM(VM_FF_TM_VIRTUAL_SYNC);
VERBOSE_VM(VM_FF_PGM_NEED_HANDY_PAGES);
/* handled by the assertion above */
/* VERBOSE_VM(VM_FF_PGM_NO_MEMORY); */
VERBOSE_VM(VM_FF_PDM_QUEUES);
VERBOSE_VM(VM_FF_EMT_RENDEZVOUS);
VERBOSE_VM(VM_FF_REQUEST);
VERBOSE_VM(VM_FF_PGM_POOL_FLUSH_PENDING);
VERBOSE_VM(VM_FF_PDM_DMA);
/*
* VMCPU_FF_HM_TO_R3_MASK
*/
VERBOSE_VMCPU(VMCPU_FF_TO_R3);
/* when this flag gets set, a recall request follows */
/* VERBOSE_VMCPU(VMCPU_FF_TIMER); */
VERBOSE_VMCPU(VMCPU_FF_PDM_CRITSECT);
VERBOSE_VMCPU(VMCPU_FF_PGM_SYNC_CR3);
VERBOSE_VMCPU(VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL);
VERBOSE_VMCPU(VMCPU_FF_REQUEST);
}
#undef VERBOSE_VMCPU
#undef VERBOSE_VM
return false;
}
virtual bool hw_load_state(Genode::Vm_state *, VM *, PVMCPU) = 0;
virtual bool hw_save_state(Genode::Vm_state *, VM *, PVMCPU) = 0;
virtual int vm_exit_requires_instruction_emulation(PCPUMCTX) = 0;
virtual void run_vm() = 0;
virtual void pause_vm() = 0;
virtual int attach_memory_to_vm(RTGCPHYS const,
RTGCUINT vbox_fault_reason) = 0;
public:
enum Exit_condition
{
SVM_NPT = 0xfc,
SVM_INVALID = 0xfd,
VCPU_STARTUP = 0xfe,
RECALL = 0xff,
};
Vcpu_handler(Genode::Env &env, size_t stack_size,
Genode::Affinity::Location location,
unsigned int cpu_id)
:
_ep(env, stack_size,
Genode::String<12>("EP-EMT-", cpu_id).string(), location),
_ep_emt(_ep.rpc_ep().manage(this)),
_cpu_id(cpu_id)
{ }
void resume()
{
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(_vcpu);
/* write FPU state from pCtx to FPU registers */
// fpu_load(reinterpret_cast<char *>(pCtx->pXStateR3));
Assert(_vm_state == IRQ_WIN || _vm_state == PAUSED || _vm_state == NPT_EPT);
_vm_state = RUNNING;
run_vm();
}
void request_pause()
{
_recall_req ++;
if (_irq_win) {
_recall_skip ++;
return;
}
if (_vm_state != RUNNING)
return;
pause_vm();
}
unsigned int cpu_id() { return _cpu_id; }
void recall(PVM vm)
{
if (!_vm || !_vcpu) {
_vm = vm;
_vcpu = &vm->aCpus[_cpu_id];
}
if (_vm != vm || _vcpu != &vm->aCpus[_cpu_id])
Genode::error("wrong CPU !?");
_ep_emt.request_pause();
#if 0
if (_recall_req % 1000 == 0) {
using Genode::log;
while (other) {
log(other->_cpu_id, " exits=", other->_vm_exits,
" req:skip:drop,inv recall=", other->_recall_req, ":",
other->_recall_skip, ":", other->_recall_drop, ":",
other->_recall_inv, " req:inj:drop irq=",
other->_irq_request, ":", other->_irq_inject, ":",
other->_irq_drop);
other = other->next();
}
}
#endif
}
void check_time()
{
{
Genode::Lock_guard<Genode::Lock> lock(_r0_block_guard);
const uint64_t u64NowGip = RTTimeNanoTS();
if (!_r0_wakeup_abs || _r0_wakeup_abs >= u64NowGip)
return;
}
wake_up();
}
void halt(Genode::uint64_t rttime_abs)
{
{
Genode::Lock_guard<Genode::Lock> lock(_r0_block_guard);
_r0_wakeup_abs = rttime_abs;
}
_r0_block.down();
}
void wake_up()
{
{
Genode::Lock_guard<Genode::Lock> lock(_r0_block_guard);
_r0_wakeup_abs = 0;
}
_r0_block.up();
}
int run_hw(PVMR0 pVMR0)
{
VM * pVM = reinterpret_cast<VM *>(pVMR0);
PVMCPU pVCpu = &pVM->aCpus[_cpu_id];
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
if (!_vm || !_vcpu) {
_vm = pVM;
_vcpu = &pVM->aCpus[_cpu_id];
}
if (_vm != pVM || _vcpu != &pVM->aCpus[_cpu_id])
Genode::error("wrong CPU !?");
/* take the utcb state prepared during the last exit */
_state->inj_info.value(IRQ_INJ_NONE);
_state->intr_state.value(next_utcb.intr_state);
_state->actv_state.value(ACTIVITY_STATE_ACTIVE);
_state->ctrl_primary.value(next_utcb.ctrl[0]);
_state->ctrl_secondary.value(next_utcb.ctrl[1]);
/* Transfer vCPU state from vbox to Genode format */
if (!vbox_to_state(pVM, pVCpu) ||
!hw_load_state(_state, pVM, pVCpu)) {
Genode::error("loading vCPU state failed");
return VERR_INTERNAL_ERROR;
}
/* check whether to request interrupt window for injection */
_irq_win = check_to_request_irq_window(pVCpu);
/*
* Flag vCPU to be "pokeable" by external events such as interrupts
* from virtual devices. Only if this flag is set, the
* 'vmR3HaltGlobal1NotifyCpuFF' function calls 'SUPR3CallVMMR0Ex'
* with VMMR0_DO_GVMM_SCHED_POKE as argument to indicate such
* events. This function, in turn, will recall the vCPU.
*/
VMCPU_SET_STATE(pVCpu, VMCPUSTATE_STARTED_EXEC);
/* switch to hardware accelerated mode */
switch_to_hw();
Assert(_state->actv_state.value() == ACTIVITY_STATE_ACTIVE);
/* write FPU state of vCPU (in current FPU registers) to pCtx */
Genode::memcpy(pCtx->pXStateR3, &_guest_fpu_state, sizeof(X86FXSTATE));
/* see hmR0VmxExitToRing3 - sync recompiler state */
CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_SYSENTER_MSR |
CPUM_CHANGED_LDTR | CPUM_CHANGED_GDTR |
CPUM_CHANGED_IDTR | CPUM_CHANGED_TR |
CPUM_CHANGED_HIDDEN_SEL_REGS |
CPUM_CHANGED_GLOBAL_TLB_FLUSH);
VMCPU_SET_STATE(pVCpu, VMCPUSTATE_STARTED);
/* Transfer vCPU state from Genode to vbox format */
if (!state_to_vbox(pVM, pVCpu) ||
!hw_save_state(_state, pVM, pVCpu)) {
Genode::error("saving vCPU state failed");
return VERR_INTERNAL_ERROR;
}
#ifdef VBOX_WITH_REM
REMFlushTBs(pVM);
#endif
/* track guest mode changes - see VMM/VMMAll/IEMAllCImpl.cpp.h */
PGMChangeMode(pVCpu, pCtx->cr0, pCtx->cr4, pCtx->msrEFER);
int rc = vm_exit_requires_instruction_emulation(pCtx);
/* evaluated in VMM/include/EMHandleRCTmpl.h */
return rc;
}
};
#endif /* _VIRTUALBOX__VCPU_H_ */

View File

@ -0,0 +1,183 @@
/*
* \brief Genode/Nova specific VirtualBox SUPLib supplements
* \author Alexander Boettcher
* \date 2013-11-18
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _VIRTUALBOX__VCPU_SVM_H_
#define _VIRTUALBOX__VCPU_SVM_H_
/* base includes */
#include <vm_session/connection.h>
#include <vm_session/vm_session.h>
#include <cpu/vm_state.h>
/* Genode's VirtualBox includes */
#include "vcpu.h"
#include "svm.h"
class Vcpu_handler_svm : public Vcpu_handler
{
private:
Genode::Vm_handler<Vcpu_handler_svm> _handler;
Genode::Vm_connection &_vm_session;
Genode::Vm_session_client::Vcpu_id _vcpu;
Genode::Attached_dataspace _state_ds;
void _svm_default() { _default_handler(); }
void _svm_vintr() { _irq_window(); }
void _svm_ioio()
{
if (_state->qual_primary.value() & 0x4) {
unsigned ctrl0 = _state->ctrl_primary.value();
Genode::warning("invalid gueststate");
*_state = Genode::Vm_state {}; /* reset */
_state->ctrl_primary.value(ctrl0);
_state->ctrl_secondary.value(0);
_vm_session.run(_vcpu);
} else
_default_handler();
}
template <unsigned X>
void _svm_npt()
{
bool const unmap = _state->qual_primary.value() & 1;
Genode::addr_t const exit_addr = _state->qual_secondary.value();
RTGCUINT const vbox_errorcode = _state->qual_primary.value();
npt_ept_exit_addr = exit_addr;
npt_ept_unmap = unmap;
npt_ept_errorcode = vbox_errorcode;
_npt_ept();
}
void _svm_startup()
{
/* enable VM exits for CPUID */
next_utcb.ctrl[0] = SVM_CTRL1_INTERCEPT_CPUID;
next_utcb.ctrl[1] = 0;
}
void _svm_recall() { Vcpu_handler::_recall_handler(); }
void _handle_vm_exception()
{
unsigned const exit = _state->exit_reason;
// Genode::warning(__func__, " ", Genode::Hex(exit), " _irq_win=", _irq_win);
switch (exit) {
case RECALL: _svm_recall(); break;
case SVM_EXIT_IOIO: _svm_ioio(); break;
case SVM_EXIT_VINTR: _svm_vintr(); break;
// case SVM_EXIT_RDTSC: _svm_default(); break;
case SVM_EXIT_MSR: _svm_default(); break;
case SVM_NPT: _svm_npt<SVM_NPT>(); break;
case SVM_EXIT_HLT: _svm_default(); break;
case SVM_EXIT_CPUID: _svm_default(); break;
case VCPU_STARTUP:
_svm_startup();
_lock.unlock();
/* pause - no resume */
return;
default:
Genode::error(__func__, " unknown exit - stop - ",
Genode::Hex(exit));
_vm_state = PAUSED;
return;
}
}
void run_vm() { _vm_session.run(_vcpu); }
void pause_vm() { _vm_session.pause(_vcpu); }
int attach_memory_to_vm(RTGCPHYS const gp_attach_addr,
RTGCUINT vbox_errorcode)
{
return map_memory(_vm_session, gp_attach_addr, vbox_errorcode);
}
void _exit_config(Genode::Vm_state &state, unsigned exit)
{
switch (exit) {
case RECALL:
case SVM_EXIT_IOIO:
case SVM_EXIT_VINTR:
case SVM_EXIT_RDTSC:
case SVM_EXIT_MSR:
case SVM_NPT:
case SVM_EXIT_HLT:
case SVM_EXIT_CPUID:
case VCPU_STARTUP:
/* todo - touch all members */
Genode::memset(&state, ~0U, sizeof(state));
break;
default:
break;
}
}
public:
Vcpu_handler_svm(Genode::Env &env, size_t stack_size,
Genode::Affinity::Location location,
unsigned int cpu_id,
Genode::Vm_connection &vm_session,
Genode::Allocator &alloc)
:
Vcpu_handler(env, stack_size, location, cpu_id),
_handler(_ep, *this, &Vcpu_handler_svm::_handle_vm_exception,
&Vcpu_handler_svm::_exit_config),
_vm_session(vm_session),
/* construct vcpu */
_vcpu(_vm_session.with_upgrade([&]() {
return _vm_session.create_vcpu(alloc, env, _handler); })),
/* get state of vcpu */
_state_ds(env.rm(), _vm_session.cpu_state(_vcpu))
{
_state = _state_ds.local_addr<Genode::Vm_state>();
/* sync with initial startup exception */
_lock.lock();
_vm_session.run(_vcpu);
/* sync with initial startup exception */
_lock.lock();
// _lock.unlock();
}
bool hw_save_state(Genode::Vm_state *state, VM * pVM, PVMCPU pVCpu) {
return svm_save_state(state, pVM, pVCpu);
}
bool hw_load_state(Genode::Vm_state *state, VM * pVM, PVMCPU pVCpu) {
return svm_load_state(state, pVM, pVCpu);
}
int vm_exit_requires_instruction_emulation(PCPUMCTX)
{
if (_state->exit_reason == RECALL)
return VINF_SUCCESS;
return VINF_EM_RAW_EMULATE_INSTR;
}
};
#endif /* _VIRTUALBOX__VCPU_SVM_H_ */

View File

@ -0,0 +1,285 @@
/*
* \brief Genode/Nova specific VirtualBox SUPLib supplements
* \author Alexander Boettcher
* \author Norman Feske
* \author Christian Helmuth
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _VIRTUALBOX__VCPU_VMX_H_
#define _VIRTUALBOX__VCPU_VMX_H_
/* base includes */
#include <vm_session/connection.h>
#include <vm_session/vm_session.h>
#include <cpu/vm_state.h>
/* libc includes */
#include <stdlib.h>
/* VirtualBox includes */
#include <VBox/vmm/hm_vmx.h>
/* Genode's VirtualBox includes */
#include "vcpu.h"
#include "vmx.h"
class Vcpu_handler_vmx : public Vcpu_handler
{
private:
Genode::Vm_handler<Vcpu_handler_vmx> _handler;
Genode::Vm_connection &_vm_session;
Genode::Vm_session_client::Vcpu_id _vcpu;
Genode::Attached_dataspace _state_ds;
template <unsigned X>
void _vmx_ept()
{
Genode::addr_t const exit_qual = _state->qual_primary.value();
Genode::addr_t const exit_addr = _state->qual_secondary.value();
bool const unmap = exit_qual & 0x38;
RTGCUINT vbox_errorcode = 0;
if (exit_qual & VMX_EXIT_QUALIFICATION_EPT_INSTR_FETCH)
vbox_errorcode |= X86_TRAP_PF_ID;
if (exit_qual & VMX_EXIT_QUALIFICATION_EPT_DATA_WRITE)
vbox_errorcode |= X86_TRAP_PF_RW;
if (exit_qual & VMX_EXIT_QUALIFICATION_EPT_ENTRY_PRESENT)
vbox_errorcode |= X86_TRAP_PF_P;
npt_ept_exit_addr = exit_addr;
npt_ept_unmap = unmap;
npt_ept_errorcode = vbox_errorcode;
_npt_ept();
}
void _vmx_default() { _default_handler(); }
void _vmx_startup()
{
/* configure VM exits to get */
/* from src/VBox/VMM/VMMR0/HWVMXR0.cpp of virtualbox sources */
next_utcb.ctrl[0] = VMX_VMCS_CTRL_PROC_EXEC_HLT_EXIT |
VMX_VMCS_CTRL_PROC_EXEC_MOV_DR_EXIT |
VMX_VMCS_CTRL_PROC_EXEC_UNCOND_IO_EXIT |
/*
VMX_VMCS_CTRL_PROC_EXEC_MONITOR_EXIT |
VMX_VMCS_CTRL_PROC_EXEC_MWAIT_EXIT |
*/
/* VMX_VMCS_CTRL_PROC_EXEC_CR8_LOAD_EXIT |
VMX_VMCS_CTRL_PROC_EXEC_CR8_STORE_EXIT |*/
VMX_VMCS_CTRL_PROC_EXEC_USE_TPR_SHADOW |
VMX_VMCS_CTRL_PROC_EXEC_RDPMC_EXIT;
/* VMX_VMCS_CTRL_PROC_EXEC_PAUSE_EXIT | */
/*
* Disable trapping RDTSC for now as it creates a huge load with
* VM guests that execute it frequently.
*/
// VMX_VMCS_CTRL_PROC_EXEC_RDTSC_EXIT;
next_utcb.ctrl[1] = VMX_VMCS_CTRL_PROC_EXEC2_VIRT_APIC |
VMX_VMCS_CTRL_PROC_EXEC2_WBINVD_EXIT |
VMX_VMCS_CTRL_PROC_EXEC2_UNRESTRICTED_GUEST |
VMX_VMCS_CTRL_PROC_EXEC2_VPID |
VMX_VMCS_CTRL_PROC_EXEC2_RDTSCP |
VMX_VMCS_CTRL_PROC_EXEC2_EPT |
VMX_VMCS_CTRL_PROC_EXEC2_INVPCID;
}
void _vmx_triple()
{
Genode::error("triple fault - dead");
exit(-1);
}
void _vmx_irqwin() { _irq_window(); }
void _vmx_recall() { Vcpu_handler::_recall_handler(); }
__attribute__((noreturn)) void _vmx_invalid()
{
unsigned const dubious = _state->inj_info.value() |
_state->intr_state.value() |
_state->actv_state.value();
if (dubious)
Genode::warning(__func__, " - dubious -"
" inj_info=", Genode::Hex(_state->inj_info.value()),
" inj_error=", Genode::Hex(_state->inj_error.value()),
" intr_state=", Genode::Hex(_state->intr_state.value()),
" actv_state=", Genode::Hex(_state->actv_state.value()));
Genode::error("invalid guest state - dead");
exit(-1);
}
/*
* This VM exit is in part handled by the NOVA kernel (writing the CR
* register) and in part by VirtualBox (updating the PDPTE registers,
* which requires access to the guest physical memory).
* Intel manual sections 4.4.1 of Vol. 3A and 26.3.2.4 of Vol. 3C
* indicate the conditions when the PDPTE registers need to get
* updated.
*/
void _vmx_mov_crx() { _default_handler(); return; }
void _handle_vm_exception()
{
unsigned const exit = _state->exit_reason;
switch (exit) {
case VMX_EXIT_TRIPLE_FAULT: _vmx_triple(); break;
case VMX_EXIT_INIT_SIGNAL: _vmx_default(); break;
case VMX_EXIT_INT_WINDOW: _vmx_irqwin(); break;
case VMX_EXIT_TASK_SWITCH: _vmx_default(); break;
case VMX_EXIT_CPUID: _vmx_default(); break;
case VMX_EXIT_HLT: _vmx_default(); break;
/* we don't support tsc offsetting for now - so let the rdtsc exit */
case VMX_EXIT_RDTSC: _vmx_default(); break;
case VMX_EXIT_RDTSCP: _vmx_default(); break;
case VMX_EXIT_VMCALL: _vmx_default(); break;
case VMX_EXIT_IO_INSTR: _vmx_default(); break;
case VMX_EXIT_RDMSR: _vmx_default(); break;
case VMX_EXIT_WRMSR: _vmx_default(); break;
case VMX_EXIT_ERR_INVALID_GUEST_STATE: _vmx_invalid(); break;
case VMX_EXIT_PAUSE: _vmx_default(); break;
case VMX_EXIT_WBINVD: _vmx_default(); break;
case VMX_EXIT_MOV_CRX: _vmx_mov_crx(); break;
case VMX_EXIT_MOV_DRX: _vmx_default(); break;
case VMX_EXIT_TPR_BELOW_THRESHOLD: _vmx_default(); break;
case VMX_EXIT_EPT_VIOLATION: _vmx_ept<VMX_EXIT_EPT_VIOLATION>(); break;
case RECALL: _vmx_recall(); break;
case VCPU_STARTUP:
_vmx_startup();
_lock.unlock();
/* pause - no resume */
return;
default:
Genode::error(__func__, " unknown exit - stop - ",
Genode::Hex(exit));
_vm_state = PAUSED;
return;
}
}
void run_vm() { _vm_session.run(_vcpu); }
void pause_vm() { _vm_session.pause(_vcpu); }
int attach_memory_to_vm(RTGCPHYS const gp_attach_addr,
RTGCUINT vbox_errorcode)
{
return map_memory(_vm_session, gp_attach_addr, vbox_errorcode);
}
void _exit_config(Genode::Vm_state &state, unsigned exit)
{
switch (exit) {
case VMX_EXIT_TRIPLE_FAULT:
case VMX_EXIT_INIT_SIGNAL:
case VMX_EXIT_INT_WINDOW:
case VMX_EXIT_TASK_SWITCH:
case VMX_EXIT_CPUID:
case VMX_EXIT_HLT:
case VMX_EXIT_RDTSC:
case VMX_EXIT_RDTSCP:
case VMX_EXIT_VMCALL:
case VMX_EXIT_IO_INSTR:
case VMX_EXIT_RDMSR:
case VMX_EXIT_WRMSR:
case VMX_EXIT_ERR_INVALID_GUEST_STATE:
// case VMX_EXIT_PAUSE:
case VMX_EXIT_WBINVD:
case VMX_EXIT_MOV_CRX:
case VMX_EXIT_MOV_DRX:
case VMX_EXIT_TPR_BELOW_THRESHOLD:
case VMX_EXIT_EPT_VIOLATION:
case VCPU_STARTUP:
case RECALL:
/* todo - touch all members */
Genode::memset(&state, ~0U, sizeof(state));
break;
default:
break;
}
}
public:
Vcpu_handler_vmx(Genode::Env &env, size_t stack_size,
Genode::Affinity::Location location,
unsigned int cpu_id,
Genode::Vm_connection &vm_session,
Genode::Allocator &alloc)
:
Vcpu_handler(env, stack_size, location, cpu_id),
_handler(_ep, *this, &Vcpu_handler_vmx::_handle_vm_exception,
&Vcpu_handler_vmx::_exit_config),
_vm_session(vm_session),
/* construct vcpu */
_vcpu(_vm_session.with_upgrade([&]() {
return _vm_session.create_vcpu(alloc, env, _handler); })),
/* get state of vcpu */
_state_ds(env.rm(), _vm_session.cpu_state(_vcpu))
{
_state = _state_ds.local_addr<Genode::Vm_state>();
/* sync with initial startup exception */
_lock.lock();
_vm_session.run(_vcpu);
/* sync with initial startup exception */
_lock.lock();
// _lock.unlock();
}
bool hw_save_state(Genode::Vm_state *state, VM * pVM, PVMCPU pVCpu) {
return vmx_save_state(state, pVM, pVCpu);
}
bool hw_load_state(Genode::Vm_state * state, VM * pVM, PVMCPU pVCpu) {
return vmx_load_state(state, pVM, pVCpu);
}
int vm_exit_requires_instruction_emulation(PCPUMCTX pCtx)
{
switch (_state->exit_reason) {
case VMX_EXIT_HLT:
pCtx->rip++;
return VINF_EM_HALT;
case VMX_EXIT_IO_INSTR:
/* EMHandleRCTmpl.h does not distinguish READ/WRITE rc */
return VINF_IOM_R3_IOPORT_WRITE;
case VMX_EXIT_RDMSR:
return VINF_CPUM_R3_MSR_READ;
case VMX_EXIT_WRMSR:
return VINF_CPUM_R3_MSR_WRITE;
case VMX_EXIT_TPR_BELOW_THRESHOLD:
/* the instruction causing the exit has already been executed */
case RECALL:
return VINF_SUCCESS;
case VMX_EXIT_EPT_VIOLATION:
if (_ept_fault_addr_type == PGMPAGETYPE_MMIO)
/* EMHandleRCTmpl.h does not distinguish READ/WRITE rc */
return VINF_IOM_R3_MMIO_READ_WRITE;
case VMX_EXIT_MOV_DRX:
/* looks complicated in original R0 code -> emulate instead */
return VINF_EM_RAW_EMULATE_INSTR;
default:
return VINF_EM_RAW_EMULATE_INSTR;
}
}
};
#endif /* _VIRTUALBOX__VCPU_VMX_H_ */

View File

@ -0,0 +1,103 @@
/*
* \brief Genode specific VirtualBox SUPLib supplements
* \author Norman Feske
* \author Alexander Boettcher
*/
/*
* Copyright (C) 2013-2019 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _VIRTUALBOX__VMX_H_
#define _VIRTUALBOX__VMX_H_
#define GENODE_READ_SELREG_REQUIRED(REG) \
(pCtx->REG.Sel != state->REG.value().sel) || \
(pCtx->REG.ValidSel != state->REG.value().sel) || \
(pCtx->REG.fFlags != CPUMSELREG_FLAGS_VALID) || \
(pCtx->REG.u32Limit != state->REG.value().limit) || \
(pCtx->REG.u64Base != state->REG.value().base) || \
(pCtx->REG.Attr.u != sel_ar_conv_from_genode(state->REG.value().ar))
#define GENODE_READ_SELREG(REG) \
pCtx->REG.Sel = state->REG.value().sel; \
pCtx->REG.ValidSel = state->REG.value().sel; \
pCtx->REG.fFlags = CPUMSELREG_FLAGS_VALID; \
pCtx->REG.u32Limit = state->REG.value().limit; \
pCtx->REG.u64Base = state->REG.value().base; \
pCtx->REG.Attr.u = sel_ar_conv_from_genode(state->REG.value().ar)
static inline bool vmx_save_state(Genode::Vm_state * state, VM * pVM, PVMCPU pVCpu)
{
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
GENODE_READ_SELREG(cs);
GENODE_READ_SELREG(ds);
GENODE_READ_SELREG(es);
GENODE_READ_SELREG(fs);
GENODE_READ_SELREG(gs);
GENODE_READ_SELREG(ss);
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);
}
return true;
}
#undef GENODE_READ_SELREG_REQUIRED
#undef GENODE_READ_SELREG
enum { VMCS_SEG_UNUSABLE = 0x10000 };
#define GENODE_WRITE_SELREG(REG) \
Assert(pCtx->REG.fFlags & CPUMSELREG_FLAGS_VALID); \
Assert(pCtx->REG.ValidSel == pCtx->REG.Sel); \
state->REG.value(Segment{pCtx->REG.Sel, \
sel_ar_conv_to_genode(pCtx->REG.Attr.u ? : VMCS_SEG_UNUSABLE), \
pCtx->REG.u32Limit, \
pCtx->REG.u64Base});
static inline bool vmx_load_state(Genode::Vm_state * state, VM * pVM, PVMCPU pVCpu)
{
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
typedef Genode::Vm_state::Segment Segment;
GENODE_WRITE_SELREG(es);
GENODE_WRITE_SELREG(ds);
GENODE_WRITE_SELREG(fs);
GENODE_WRITE_SELREG(gs);
GENODE_WRITE_SELREG(cs);
GENODE_WRITE_SELREG(ss);
/* ldtr */
if (pCtx->ldtr.Sel == 0) {
state->ldtr.value(Segment{0, sel_ar_conv_to_genode(0x82), 0, 0});
} else {
state->ldtr.value(Segment{pCtx->ldtr.Sel,
sel_ar_conv_to_genode(pCtx->ldtr.Attr.u),
pCtx->ldtr.u32Limit, pCtx->ldtr.u64Base});
}
/* tr */
state->tr.value(Segment{pCtx->tr.Sel, sel_ar_conv_to_genode(pCtx->tr.Attr.u),
pCtx->tr.u32Limit, pCtx->tr.u64Base});
return true;
}
#undef GENODE_WRITE_SELREG
#endif /* _VIRTUALBOX__VMX_H_ */

View File

@ -65,6 +65,10 @@ vbox5_ubuntu_14_04_32
vbox5_ubuntu_14_04_64
vbox5_ubuntu_16_04_32
vbox5_ubuntu_16_04_64
vbox5_vm_ubuntu_16_04_32
vbox5_vm_ubuntu_16_04_64
vbox5_vm_win7_32
vbox5_vm_win7_64
vbox5_win10_64
vbox5_win7_32
vbox5_win7_64