mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-29 15:44:02 +00:00
parent
07ee9654e4
commit
5673c163fb
@ -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 =
|
||||
|
@ -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 =
|
@ -1 +1 @@
|
||||
0db2b02901c5b62cd181d817ba71c3b0d417a40a
|
||||
0ccf5764631d03e73d2a952391c96fe457f6f5ee
|
||||
|
84
repos/ports/recipes/src/vbox5/content.mk
Normal file
84
repos/ports/recipes/src/vbox5/content.mk
Normal 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" > $@
|
||||
|
1
repos/ports/recipes/src/vbox5/hash
Normal file
1
repos/ports/recipes/src/vbox5/hash
Normal file
@ -0,0 +1 @@
|
||||
2019-05-20-d d04357716c16c4a70e53bba190ef5a73c4e64b51
|
19
repos/ports/recipes/src/vbox5/used_apis
Normal file
19
repos/ports/recipes/src/vbox5/used_apis
Normal 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
|
@ -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"/>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
25
repos/ports/run/vbox5_vm_ubuntu_16_04_32.run
Normal file
25
repos/ports/run/vbox5_vm_ubuntu_16_04_32.run
Normal 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
|
27
repos/ports/run/vbox5_vm_ubuntu_16_04_64.run
Normal file
27
repos/ports/run/vbox5_vm_ubuntu_16_04_64.run
Normal 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
|
27
repos/ports/run/vbox5_vm_win7_32.run
Normal file
27
repos/ports/run/vbox5_vm_win7_32.run
Normal 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
|
27
repos/ports/run/vbox5_vm_win7_64.run
Normal file
27
repos/ports/run/vbox5_vm_win7_64.run
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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");
|
||||
}
|
227
repos/ports/src/virtualbox5/generic/sup.cc
Normal file
227
repos/ports/src/virtualbox5/generic/sup.cc
Normal 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;
|
||||
}
|
1266
repos/ports/src/virtualbox5/generic/sup_vmm.cc
Normal file
1266
repos/ports/src/virtualbox5/generic/sup_vmm.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
@ -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();
|
||||
|
101
repos/ports/src/virtualbox5/svm.h
Normal file
101
repos/ports/src/virtualbox5/svm.h
Normal 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_ */
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
884
repos/ports/src/virtualbox5/vcpu.h
Normal file
884
repos/ports/src/virtualbox5/vcpu.h
Normal 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 Developer’s 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_ */
|
183
repos/ports/src/virtualbox5/vcpu_svm.h
Normal file
183
repos/ports/src/virtualbox5/vcpu_svm.h
Normal 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_ */
|
285
repos/ports/src/virtualbox5/vcpu_vmx.h
Normal file
285
repos/ports/src/virtualbox5/vcpu_vmx.h
Normal 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_ */
|
103
repos/ports/src/virtualbox5/vmx.h
Normal file
103
repos/ports/src/virtualbox5/vmx.h
Normal 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_ */
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user