base: refactor VM/vCPU API

Issue #3554
This commit is contained in:
Christian Helmuth 2020-12-18 14:08:06 +01:00 committed by Norman Feske
parent 6e8728f2d3
commit 219809ffed
67 changed files with 3120 additions and 3016 deletions

View File

@ -1,9 +1 @@
#
# \brief Portions of base library shared by core and non-core processes
# \author Norman Feske
# \date 2013-02-14
#
vpath vm_session.cc $(REP_DIR)/src/lib/base/x86
include $(REP_DIR)/lib/mk/base-foc-common.inc

View File

@ -1,3 +1,5 @@
LIBS += timeout
vpath vm.cc $(REP_DIR)/src/lib/base/x86
include $(REP_DIR)/lib/mk/base-foc.inc

View File

@ -158,7 +158,8 @@ class Genode::Platform_thread : Interface
/**
* Make thread to vCPU
*/
Foc::l4_cap_idx_t setup_vcpu(unsigned, Cap_mapping const &, Cap_mapping &);
Foc::l4_cap_idx_t setup_vcpu(unsigned, Cap_mapping const &,
Cap_mapping &, Region_map::Local_addr &);
/************************

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -15,8 +15,10 @@
#define _CORE__VM_SESSION_COMPONENT_H_
/* Genode includes */
#include <base/registry.h>
#include <base/rpc_server.h>
#include <base/heap.h>
#include <util/bit_allocator.h>
#include <vm_session/vm_session.h>
/* core includes */
@ -24,30 +26,46 @@
#include <dataspace_component.h>
#include <region_map_component.h>
#include <trace/source_registry.h>
#include <foc_native_vcpu/foc_native_vcpu.h>
namespace Genode { class Vm_session_component; struct Vcpu; }
namespace Genode
{
class Vm_session_component;
struct Vcpu;
enum { MAX_VCPU_IDS = (Platform::VCPU_VIRT_EXT_END -
Platform::VCPU_VIRT_EXT_START) / L4_PAGESIZE };
typedef Bit_allocator<MAX_VCPU_IDS> Vcpu_id_allocator;
}
struct Genode::Vcpu : List<Vcpu>::Element
struct Genode::Vcpu : Rpc_object<Vm_session::Native_vcpu, Vcpu>
{
private:
Constrained_ram_allocator &_ram_alloc;
Cap_quota_guard &_cap_alloc;
Ram_dataspace_capability _ds_cap { };
Vm_session::Vcpu_id const _id;
Cap_mapping _recall { true };
Rpc_entrypoint &_ep;
Constrained_ram_allocator &_ram_alloc;
Cap_quota_guard &_cap_alloc;
Vcpu_id_allocator &_vcpu_ids;
Cap_mapping _recall { true };
Foc::l4_cap_idx_t _task_index_client { };
Region_map::Local_addr _foc_vcpu_state { };
public:
Vcpu(Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc, Vm_session::Vcpu_id const id);
Vcpu(Rpc_entrypoint &, Constrained_ram_allocator &, Cap_quota_guard &,
Platform_thread &, Cap_mapping &, Vcpu_id_allocator &);
~Vcpu();
bool match(Vm_session::Vcpu_id const id) const { return id.id == _id.id; }
Dataspace_capability ds_cap() { return _ds_cap; }
Cap_mapping &recall_cap() { return _recall; }
/*******************************
** Native_vcpu RPC interface **
*******************************/
Foc::l4_cap_idx_t task_index() const { return _task_index_client; }
Region_map::Local_addr foc_vcpu_state() const { return _foc_vcpu_state; }
};
@ -60,17 +78,19 @@ class Genode::Vm_session_component
{
private:
typedef Constrained_ram_allocator Con_ram_allocator;
typedef Constrained_ram_allocator Con_ram_allocator;
typedef Allocator_avl_tpl<Rm_region> Avl_region;
Rpc_entrypoint &_ep;
Con_ram_allocator _constrained_md_ram_alloc;
Sliced_heap _heap;
Avl_region _map { &_heap };
List<Vcpu> _vcpus { };
Avl_region _map { &_heap };
Cap_mapping _task_vcpu { true };
unsigned _id_alloc { 0 };
Vcpu_id_allocator _vcpu_ids { };
Registry<Registered<Vcpu>> _vcpus { };
/* helpers for vm_session_common.cc */
void _attach_vm_memory(Dataspace_component &, addr_t, Attach_attr);
void _detach_vm_memory(addr_t, size_t);
@ -94,25 +114,20 @@ class Genode::Vm_session_component
** Region_map_detach interface **
*********************************/
void detach(Region_map::Local_addr) override;
void unmap_region(addr_t, size_t) override;
/* used on destruction of attached dataspaces */
void detach(Region_map::Local_addr) override; /* vm_session_common.cc */
void unmap_region(addr_t, size_t) override; /* vm_session_common.cc */
/**************************
** Vm session interface **
**************************/
Dataspace_capability _cpu_state(Vcpu_id);
Capability<Native_vcpu> create_vcpu(Thread_capability);
void attach_pic(addr_t) override { /* unused on Fiasco.OC */ }
void _exception_handler(Signal_context_capability, Vcpu_id) { }
void _run(Vcpu_id) { }
void _pause(Vcpu_id) { }
void attach(Dataspace_capability, addr_t, Attach_attr) override;
void attach_pic(addr_t) override { }
void detach(addr_t, size_t) override;
Vcpu_id _create_vcpu(Thread_capability);
Capability<Native_vcpu> _native_vcpu(Vcpu_id) {
return Capability<Native_vcpu>(); }
void attach(Dataspace_capability, addr_t, Attach_attr) override; /* vm_session_common.cc */
void detach(addr_t, size_t) override; /* vm_session_common.cc */
};
#endif /* _CORE__VM_SESSION_COMPONENT_H_ */

View File

@ -349,8 +349,9 @@ Platform_thread::~Platform_thread()
}
Foc::l4_cap_idx_t Platform_thread::setup_vcpu(unsigned const vcpu_id,
Cap_mapping const &task_vcpu,
Cap_mapping &vcpu_irq)
Cap_mapping const &task_vcpu,
Cap_mapping &vcpu_irq,
Region_map::Local_addr &vcpu_state)
{
if (!_platform_pd)
return Foc::L4_INVALID_CAP;
@ -358,8 +359,11 @@ Foc::l4_cap_idx_t Platform_thread::setup_vcpu(unsigned const vcpu_id,
if (vcpu_id >= (Platform::VCPU_VIRT_EXT_END - Platform::VCPU_VIRT_EXT_START) / L4_PAGESIZE)
return Foc::L4_INVALID_CAP;
addr_t const vcpu_addr = Platform::VCPU_VIRT_EXT_START + L4_PAGESIZE*vcpu_id;
l4_fpage_t const vm_page = l4_fpage( vcpu_addr, L4_PAGESHIFT, L4_FPAGE_RW);
/* vCPU state attached by kernel syscall to client PD directly */
vcpu_state = Region_map::Local_addr(Platform::VCPU_VIRT_EXT_START +
L4_PAGESIZE * vcpu_id);
l4_fpage_t const vm_page = l4_fpage(vcpu_state, L4_PAGESHIFT, L4_FPAGE_RW);
l4_msgtag_t msg = l4_task_add_ku_mem(_platform_pd->native_task().data()->kcap(), vm_page);
if (l4_error(msg)) {
@ -367,7 +371,7 @@ Foc::l4_cap_idx_t Platform_thread::setup_vcpu(unsigned const vcpu_id,
return Foc::L4_INVALID_CAP;
}
msg = l4_thread_vcpu_control_ext(_thread.local.data()->kcap(), vcpu_addr);
msg = l4_thread_vcpu_control_ext(_thread.local.data()->kcap(), vcpu_state);
if (l4_error(msg)) {
error("vcpu_control_exit failed ", l4_error(msg));
return Foc::L4_INVALID_CAP;

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -26,6 +26,67 @@
using namespace Genode;
struct Vcpu_creation_error : Exception { };
Vcpu::Vcpu(Rpc_entrypoint &ep,
Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc,
Platform_thread &thread,
Cap_mapping &task_cap,
Vcpu_id_allocator &vcpu_alloc)
:
_ep(ep),
_ram_alloc(ram_alloc),
_cap_alloc(cap_alloc),
_vcpu_ids(vcpu_alloc)
{
Foc::l4_msgtag_t msg = l4_factory_create_irq(Foc::L4_BASE_FACTORY_CAP,
_recall.local.data()->kcap());
if (l4_error(msg)) {
Genode::error("vcpu irq creation failed", l4_error(msg));
throw Vcpu_creation_error();
}
try {
unsigned const vcpu_id = _vcpu_ids.alloc();
_task_index_client = thread.setup_vcpu(vcpu_id, task_cap, recall_cap(),
_foc_vcpu_state);
if (_task_index_client == Foc::L4_INVALID_CAP) {
vcpu_alloc.free(vcpu_id);
if (l4_error(Foc::l4_irq_detach(_recall.local.data()->kcap())))
error("cannot detach IRQ");
throw Vcpu_creation_error();
}
} catch (Vcpu_id_allocator::Out_of_indices) {
throw Vcpu_creation_error();
}
_ep.manage(this);
}
Vcpu::~Vcpu()
{
_ep.dissolve(this);
if (_task_index_client != Foc::L4_INVALID_CAP) {
if (l4_error(Foc::l4_irq_detach(_recall.local.data()->kcap())))
error("cannot detach IRQ");
}
if (_foc_vcpu_state) {
unsigned const vcpu_id = ((addr_t)_foc_vcpu_state -
Platform::VCPU_VIRT_EXT_START) / L4_PAGESIZE;
_vcpu_ids.free(vcpu_id);
}
}
/**************************
** Vm_session_component **
**************************/
Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
Resources resources,
Label const &,
@ -41,7 +102,7 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
_constrained_md_ram_alloc(ram, _ram_quota_guard(), _cap_quota_guard()),
_heap(_constrained_md_ram_alloc, local_rm)
{
_cap_quota_guard().withdraw(Cap_quota{1});
Cap_quota_guard::Reservation caps(_cap_quota_guard(), Cap_quota{1});
using namespace Foc;
l4_msgtag_t msg = l4_factory_create_vm(L4_BASE_FACTORY_CAP,
@ -54,15 +115,15 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
/* configure managed VM area */
_map.add_range(0, 0UL - 0x1000);
_map.add_range(0UL - 0x1000, 0x1000);
caps.acknowledge();
}
Vm_session_component::~Vm_session_component()
{
for (;Vcpu * vcpu = _vcpus.first();) {
_vcpus.remove(vcpu);
destroy(_heap, vcpu);
}
_vcpus.for_each([&] (Vcpu &vcpu) {
destroy(_heap, &vcpu); });
/* detach all regions */
while (true) {
@ -76,97 +137,34 @@ Vm_session_component::~Vm_session_component()
}
Vcpu::Vcpu(Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc,
Vm_session::Vcpu_id const id)
:
_ram_alloc(ram_alloc),
_cap_alloc(cap_alloc),
_id(id)
Capability<Vm_session::Native_vcpu> Vm_session_component::create_vcpu(Thread_capability cap)
{
try {
/* create ds for vCPU state */
_ds_cap = _ram_alloc.alloc(0x1000, Cache_attribute::CACHED);
} catch (...) {
throw;
}
Foc::l4_msgtag_t msg = l4_factory_create_irq(Foc::L4_BASE_FACTORY_CAP,
_recall.local.data()->kcap());
if (l4_error(msg)) {
_ram_alloc.free(_ds_cap);
Genode::error("vcpu irq creation failed", l4_error(msg));
throw 1;
}
}
Vcpu::~Vcpu()
{
if (_ds_cap.valid())
_ram_alloc.free(_ds_cap);
}
Vm_session::Vcpu_id Vm_session_component::_create_vcpu(Thread_capability cap)
{
Vcpu_id ret;
if (!cap.valid())
return ret;
return { };
auto lambda = [&] (Cpu_thread_component *thread) {
/* allocate vCPU object */
Vcpu * vcpu = nullptr;
_ep.apply(cap, [&] (Cpu_thread_component *thread) {
if (!thread)
return;
/* allocate vCPU object */
Vcpu * vcpu = nullptr;
try {
vcpu = new (_heap) Vcpu(_constrained_md_ram_alloc,
_cap_quota_guard(),
Vcpu_id {_id_alloc});
Foc::l4_cap_idx_t task =
thread->platform_thread().setup_vcpu(_id_alloc, _task_vcpu, vcpu->recall_cap());
if (task == Foc::L4_INVALID_CAP)
throw 0;
_ep.apply(vcpu->ds_cap(), [&] (Dataspace_component *ds) {
if (!ds)
throw 1;
/* tell client where to find task cap */
*reinterpret_cast<Foc::l4_cap_idx_t *>(ds->phys_addr()) = task;
});
} catch (int) {
if (vcpu)
destroy(_heap, vcpu);
vcpu = new (_heap) Registered<Vcpu>(_vcpus,
_ep,
_constrained_md_ram_alloc,
_cap_quota_guard(),
thread->platform_thread(),
_task_vcpu,
_vcpu_ids);
} catch (Vcpu_creation_error) {
return;
} catch (...) {
if (vcpu)
destroy(_heap, vcpu);
throw;
}
});
_vcpus.insert(vcpu);
ret.id = _id_alloc++;
};
_ep.apply(cap, lambda);
return ret;
return vcpu ? vcpu->cap() : Capability<Vm_session::Native_vcpu> {};
}
Dataspace_capability Vm_session_component::_cpu_state(Vcpu_id const vcpu_id)
{
for (Vcpu *vcpu = _vcpus.first(); vcpu; vcpu = vcpu->next()) {
if (!vcpu->match(vcpu_id))
continue;
return vcpu->ds_cap();
}
return Dataspace_capability();
}
void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc,
addr_t const guest_phys,
@ -202,6 +200,7 @@ void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc,
}
}
void Vm_session_component::_detach_vm_memory(addr_t guest_phys, size_t size)
{
Flexpage_iterator flex(guest_phys, size, guest_phys, size, 0);

View File

@ -0,0 +1,31 @@
/*
* \brief Fiasco.OC vCPU RPC interface
* \author Christian Helmuth
* \author Alexander Böttcher
* \date 2021-01-19
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__FOC_NATIVE_VCPU__FOC_NATIVE_VCPU_H_
#define _INCLUDE__FOC_NATIVE_VCPU__FOC_NATIVE_VCPU_H_
#include <vm_session/vm_session.h>
#include <dataspace/dataspace.h>
#include <foc/syscall.h>
struct Genode::Vm_session::Native_vcpu : Interface
{
GENODE_RPC(Rpc_foc_vcpu_state, Region_map::Local_addr, foc_vcpu_state);
GENODE_RPC(Rpc_task_index, Foc::l4_cap_idx_t, task_index);
GENODE_RPC_INTERFACE(Rpc_task_index, Rpc_foc_vcpu_state);
};
#endif /* _INCLUDE__FOC_NATIVE_VCPU__FOC_NATIVE_VCPU_H_ */

View File

@ -129,6 +129,7 @@ CONTENT += $(addprefix src/timer/,$(SRC_TIMER)) \
$(addprefix src/core/,$(SRC_CORE)) \
src/lib/hw src/lib/ld src/lib/cxx \
src/include/base/internal src/include/pager \
src/include/hw_native_vcpu \
include/drivers/uart
# remove duplicates

View File

@ -137,7 +137,10 @@ Vm_session_component::~Vm_session_component()
/* free region in allocator */
for (unsigned i = 0; i < _vcpu_id_alloc; i++) {
Vcpu & vcpu = _vcpus[i];
if (!_vcpus[i].constructed())
continue;
Vcpu & vcpu = *_vcpus[i];
if (vcpu.ds_cap.valid()) {
_region_map.detach(vcpu.ds_addr);
_constrained_md_ram_alloc.free(vcpu.ds_cap);

View File

@ -91,7 +91,10 @@ Vm_session_component::~Vm_session_component()
/* free region in allocator */
for (unsigned i = 0; i < _vcpu_id_alloc; i++) {
Vcpu & vcpu = _vcpus[i];
if (!_vcpus[i].constructed())
continue;
Vcpu & vcpu = *_vcpus[i];
if (vcpu.ds_cap.valid()) {
_region_map.detach(vcpu.ds_addr);
_constrained_md_ram_alloc.free(vcpu.ds_cap);

View File

@ -28,82 +28,55 @@ size_t Vm_session_component::_ds_size() {
return align_addr(sizeof(Board::Vm_state), get_page_size_log2()); }
addr_t Vm_session_component::_alloc_ds()
void Vm_session_component::Vcpu::exception_handler(Signal_context_capability handler)
{
addr_t addr;
if (platform().ram_alloc().alloc_aligned(_ds_size(), (void**)&addr,
get_page_size_log2()).error())
throw Insufficient_ram_quota();
return addr;
}
void Vm_session_component::_run(Vcpu_id) { }
void Vm_session_component::_pause(Vcpu_id) { }
Capability<Vm_session::Native_vcpu> Vm_session_component::_native_vcpu(Vcpu_id id)
{
if (!_valid_id(id)) { return Capability<Vm_session::Native_vcpu>(); }
return reinterpret_cap_cast<Vm_session::Native_vcpu>(_vcpus[id.id].kobj.cap());
}
void Vm_session_component::_exception_handler(Signal_context_capability handler,
Vcpu_id id)
{
if (!_valid_id(id)) {
Genode::warning("invalid vcpu id ", id.id);
if (!handler.valid()) {
Genode::warning("invalid signal");
return;
}
Vcpu & vcpu = _vcpus[id.id];
if (vcpu.kobj.constructed()) {
if (kobj.constructed()) {
Genode::warning("Cannot register vcpu handler twice");
return;
}
unsigned const cpu = vcpu.location.valid() ? vcpu.location.xpos() : 0;
unsigned const cpu = location.valid() ? location.xpos() : 0;
if (!vcpu.kobj.create(cpu, vcpu.ds_addr, Capability_space::capid(handler), _id))
if (!kobj.create(cpu, ds_addr, Capability_space::capid(handler), id))
Genode::warning("Cannot instantiate vm kernel object, ",
"invalid signal context?");
}
Vm_session::Vcpu_id Vm_session_component::_create_vcpu(Thread_capability tcap)
Capability<Vm_session::Native_vcpu> Vm_session_component::create_vcpu(Thread_capability const tcap)
{
using namespace Genode;
if (_vcpu_id_alloc == Board::VCPU_MAX) return Vcpu_id{Vcpu_id::INVALID};
if (_vcpu_id_alloc == Board::VCPU_MAX) return { };
Affinity::Location vcpu_location;
auto lambda = [&] (Cpu_thread_component *ptr) {
_ep.apply(tcap, [&] (Cpu_thread_component *ptr) {
if (!ptr) return;
vcpu_location = ptr->platform_thread().affinity();
};
_ep.apply(tcap, lambda);
});
if (_vcpus[_vcpu_id_alloc].constructed())
return { };
_vcpus[_vcpu_id_alloc].construct(_id, _ep);
Vcpu & vcpu = *_vcpus[_vcpu_id_alloc];
Vcpu & vcpu = _vcpus[_vcpu_id_alloc];
vcpu.ds_cap = _constrained_md_ram_alloc.alloc(_ds_size(),
Cache_attribute::UNCACHED);
try {
vcpu.ds_cap = _constrained_md_ram_alloc.alloc(_ds_size(),
Cache_attribute::UNCACHED);
vcpu.ds_addr = _region_map.attach(vcpu.ds_cap);
} catch (...) {
_constrained_md_ram_alloc.free(vcpu.ds_cap);
if (vcpu.ds_cap.valid())
_constrained_md_ram_alloc.free(vcpu.ds_cap);
_vcpus[_vcpu_id_alloc].destruct();
throw;
}
vcpu.location = vcpu_location;
return Vcpu_id { _vcpu_id_alloc++ };
}
Genode::Dataspace_capability
Vm_session_component::_cpu_state(Vm_session::Vcpu_id id)
{
return (_valid_id(id)) ? _vcpus[id.id].ds_cap
: Genode::Ram_dataspace_capability();
_vcpu_id_alloc ++;
return vcpu.cap();
}

View File

@ -21,6 +21,8 @@
#include <vm_session/vm_session.h>
#include <dataspace/capability.h>
#include <hw_native_vcpu/hw_native_vcpu.h>
/* Core includes */
#include <object.h>
#include <region_map_component.h>
@ -49,13 +51,36 @@ class Genode::Vm_session_component
Vm_session_component(Vm_session_component const &);
Vm_session_component &operator = (Vm_session_component const &);
struct Vcpu
struct Vcpu : public Rpc_object<Vm_session::Native_vcpu, Vcpu>
{
Kernel::Vm::Identity &id;
Rpc_entrypoint &ep;
Ram_dataspace_capability ds_cap { };
Region_map::Local_addr ds_addr { nullptr };
Kernel_object<Kernel::Vm> kobj {};
Affinity::Location location {};
} _vcpus[Board::VCPU_MAX];
Vcpu(Kernel::Vm::Identity &id, Rpc_entrypoint &ep) : id(id), ep(ep)
{
ep.manage(this);
}
~Vcpu()
{
ep.dissolve(this);
}
/*******************************
** Native_vcpu RPC interface **
*******************************/
Capability<Dataspace> state() const { return ds_cap; }
Native_capability native_vcpu() { return kobj.cap(); }
void exception_handler(Signal_context_capability);
};
Constructible<Vcpu> _vcpus[Board::VCPU_MAX];
Rpc_entrypoint &_ep;
Constrained_ram_allocator _constrained_md_ram_alloc;
@ -68,10 +93,11 @@ class Genode::Vm_session_component
unsigned _vcpu_id_alloc { 0 };
static size_t _ds_size();
bool _valid_id(Vcpu_id id) { return id.id < Board::VCPU_MAX; }
addr_t _alloc_ds();
void * _alloc_table();
void _attach(addr_t phys_addr, addr_t vm_addr, size_t size);
/* helpers for vm_session_common.cc */
void _attach_vm_memory(Dataspace_component &, addr_t,
Attach_attr);
void _detach_vm_memory(addr_t, size_t);
@ -107,13 +133,7 @@ class Genode::Vm_session_component
void attach_pic(addr_t) override;
void detach(addr_t, size_t) override;
Dataspace_capability _cpu_state(Vcpu_id);
Vcpu_id _create_vcpu(Thread_capability);
void _exception_handler(Signal_context_capability,
Vcpu_id);
void _run(Vcpu_id);
void _pause(Vcpu_id);
Capability<Native_vcpu> _native_vcpu(Vcpu_id);
Capability<Native_vcpu> create_vcpu(Thread_capability);
};
#endif /* _CORE__VM_SESSION_COMPONENT_H_ */

View File

@ -0,0 +1,29 @@
/*
* \brief hw vCPU RPC interface
* \author Christian Helmuth
* \date 2021-01-19
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__HW_NATIVE_VCPU__HW_NATIVE_VCPU_H_
#define _INCLUDE__HW_NATIVE_VCPU__HW_NATIVE_VCPU_H_
#include <vm_session/vm_session.h>
#include <dataspace/dataspace.h>
struct Genode::Vm_session::Native_vcpu : Interface
{
GENODE_RPC(Rpc_state, Capability<Dataspace>, state);
GENODE_RPC(Rpc_native_vcpu, Native_capability, native_vcpu);
GENODE_RPC(Rpc_exception_handler, void, exception_handler, Signal_context_capability);
GENODE_RPC_INTERFACE(Rpc_state, Rpc_native_vcpu, Rpc_exception_handler);
};
#endif /* _INCLUDE__HW_NATIVE_VCPU__HW_NATIVE_VCPU_H_ */

View File

@ -0,0 +1,91 @@
/*
* \brief Client-side VM session interface
* \author Alexander Boettcher
* \date 2018-08-27
*/
/*
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <base/allocator.h>
#include <base/attached_dataspace.h>
#include <base/env.h>
#include <base/registry.h>
#include <base/internal/capability_space.h>
#include <kernel/interface.h>
#include <vm_session/connection.h>
#include <vm_session/handler.h>
#include <hw_native_vcpu/hw_native_vcpu.h>
using namespace Genode;
using Exit_config = Vm_connection::Exit_config;
/****************************
** hw vCPU implementation **
****************************/
struct Hw_vcpu : Rpc_client<Vm_session::Native_vcpu>, Noncopyable
{
private:
Attached_dataspace _state;
Native_capability _kernel_vcpu { };
Capability<Native_vcpu> _create_vcpu(Vm_connection &, Vcpu_handler_base &);
public:
Hw_vcpu(Env &, Vm_connection &, Vcpu_handler_base &);
void run() {
Kernel::run_vm(Capability_space::capid(_kernel_vcpu)); }
void pause() {
Kernel::pause_vm(Capability_space::capid(_kernel_vcpu)); }
Vcpu_state & state() { return *_state.local_addr<Vcpu_state>(); }
};
Hw_vcpu::Hw_vcpu(Env &env, Vm_connection &vm, Vcpu_handler_base &handler)
:
Rpc_client<Native_vcpu>(_create_vcpu(vm, handler)),
_state(env.rm(), vm.with_upgrade([&] () { return call<Rpc_state>(); }))
{
call<Rpc_exception_handler>(handler.signal_cap());
_kernel_vcpu = call<Rpc_native_vcpu>();
}
Capability<Vm_session::Native_vcpu> Hw_vcpu::_create_vcpu(Vm_connection &vm,
Vcpu_handler_base &handler)
{
Thread &tep { *reinterpret_cast<Thread *>(&handler.rpc_ep()) };
return vm.with_upgrade([&] () {
return vm.call<Vm_session::Rpc_create_vcpu>(tep.cap()); });
}
/**************
** vCPU API **
**************/
void Vm_connection::Vcpu::run() { static_cast<Hw_vcpu &>(_native_vcpu).run(); }
void Vm_connection::Vcpu::pause() { static_cast<Hw_vcpu &>(_native_vcpu).pause(); }
Vcpu_state & Vm_connection::Vcpu::state() { return static_cast<Hw_vcpu &>(_native_vcpu).state(); }
Vm_connection::Vcpu::Vcpu(Vm_connection &vm, Allocator &alloc,
Vcpu_handler_base &handler, Exit_config const &)
:
_native_vcpu(*new (alloc) Hw_vcpu(vm._env, vm, handler))
{ }

View File

@ -1,76 +0,0 @@
/*
* \brief Client-side VM session interface
* \author Alexander Boettcher
* \date 2018-08-27
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <base/allocator.h>
#include <base/env.h>
#include <base/registry.h>
#include <base/internal/capability_space.h>
#include <kernel/interface.h>
#include <vm_session/client.h>
using namespace Genode;
struct Vcpu;
static Genode::Registry<Genode::Registered<Vcpu>> vcpus;
struct Vcpu
{
Vm_session_client::Vcpu_id const id;
Capability<Vm_session::Native_vcpu> const cap;
Vcpu(Vm_session::Vcpu_id id, Capability<Vm_session::Native_vcpu> cap)
: id(id), cap(cap) { }
virtual ~Vcpu() { }
};
Vm_session::Vcpu_id
Vm_session_client::create_vcpu(Allocator & alloc, Env &, Vm_handler_base & handler)
{
Vcpu_id const id =
call<Rpc_create_vcpu>(reinterpret_cast<Thread *>(&handler._rpc_ep)->cap());
call<Rpc_exception_handler>(handler._cap, id);
Vcpu * vcpu = new (alloc) Registered<Vcpu> (vcpus, id, call<Rpc_native_vcpu>(id));
return vcpu->id;
}
void Vm_session_client::run(Vcpu_id const vcpu_id)
{
vcpus.for_each([&] (Vcpu & vcpu) {
if (vcpu.id.id != vcpu_id.id) { return; }
Kernel::run_vm(Capability_space::capid(vcpu.cap));
});
}
void Vm_session_client::pause(Vcpu_id const vcpu_id)
{
vcpus.for_each([&] (Vcpu & vcpu) {
if (vcpu.id.id != vcpu_id.id) { return; }
Kernel::pause_vm(Capability_space::capid(vcpu.cap));
});
}
Dataspace_capability Vm_session_client::cpu_state(Vcpu_id const vcpu_id)
{
return call<Rpc_cpu_state>(vcpu_id);
}
Vm_session::~Vm_session()
{ }

View File

@ -757,6 +757,7 @@ namespace Nova {
NUM_INITIAL_PT = 1UL << NUM_INITIAL_PT_LOG2,
NUM_INITIAL_PT_RESERVED = 2 * NUM_INITIAL_PT,
NUM_INITIAL_VCPU_PT_LOG2 = 8,
NUM_INITIAL_VCPU_PT = 1UL << NUM_INITIAL_VCPU_PT_LOG2,
};
/**

View File

@ -1,11 +1,12 @@
/*
* \brief Core-specific instance of the VM session interface
* \author Alexander Boettcher
* \author Christian Helmuth
* \date 2018-08-26
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -19,6 +20,7 @@
#include <vm_session/vm_session.h>
#include <trace/control_area.h>
#include <trace/source_registry.h>
#include <nova_native_vcpu/nova_native_vcpu.h>
namespace Genode { class Vm_session_component; }
@ -34,63 +36,26 @@ class Genode::Vm_session_component
typedef Constrained_ram_allocator Con_ram_allocator;
typedef Allocator_avl_tpl<Rm_region> Avl_region;
class Vcpu : private List<Vcpu>::Element,
public Trace::Source::Info_accessor
class Vcpu : public Rpc_object<Vm_session::Native_vcpu, Vcpu>,
public Trace::Source::Info_accessor
{
friend class List<Vcpu>;
friend class Vm_session_component;
public:
enum State { INIT, ALIVE };
struct Creation_failed { };
private:
Rpc_entrypoint &_ep;
Constrained_ram_allocator &_ram_alloc;
Cap_quota_guard &_cap_alloc;
Trace::Source_registry &_trace_sources;
Ram_dataspace_capability _ds_cap { };
addr_t _sel_sm_ec_sc;
addr_t _vm_pt_cnt { 0 };
Vcpu_id const _id;
State _state { INIT };
bool _alive { false };
unsigned const _id;
Affinity::Location const _location;
unsigned const _priority;
Session_label const &_label;
public:
Vcpu(Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc,
Vcpu_id const id, Affinity::Location const,
Session_label const &,
Trace::Control_area &,
Trace::Source_registry &);
~Vcpu();
addr_t sm_sel() const { return _sel_sm_ec_sc + 0; }
addr_t ec_sel() const { return _sel_sm_ec_sc + 1; }
addr_t sc_sel() const { return _sel_sm_ec_sc + 2; }
addr_t new_pt_id();
Vcpu_id id() { return _id; }
bool match(Vcpu_id const id) const { return id.id == _id.id; }
Ram_dataspace_capability ds_cap() const { return _ds_cap; }
bool init() const { return _state == State::INIT; }
void alive() { _state = ALIVE; };
static addr_t invalid() { return ~0UL; }
/********************************************
** Trace::Source::Info_accessor interface **
********************************************/
Trace::Source::Info trace_source_info() const override;
private:
addr_t const _pd_sel;
struct Trace_control_slot
{
@ -118,6 +83,42 @@ class Genode::Vm_session_component
Trace_control_slot _trace_control_slot;
Trace::Source _trace_source { *this, _trace_control_slot.control() };
public:
Vcpu(Rpc_entrypoint &,
Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc,
unsigned id,
unsigned kernel_id,
Affinity::Location,
unsigned priority,
Session_label const &,
addr_t pd_sel,
addr_t core_pd_sel,
addr_t vmm_pd_sel,
Trace::Control_area &,
Trace::Source_registry &);
~Vcpu();
addr_t sm_sel() const { return _sel_sm_ec_sc + 0; }
addr_t ec_sel() const { return _sel_sm_ec_sc + 1; }
addr_t sc_sel() const { return _sel_sm_ec_sc + 2; }
/*******************************
** Native_vcpu RPC interface **
*******************************/
Capability<Dataspace> state();
void startup();
void exit_handler(unsigned, Signal_context_capability);
/********************************************
** Trace::Source::Info_accessor interface **
********************************************/
Trace::Source::Info trace_source_info() const override;
};
Rpc_entrypoint &_ep;
@ -127,20 +128,13 @@ class Genode::Vm_session_component
Sliced_heap _heap;
Avl_region _map { &_heap };
addr_t _pd_sel { 0 };
unsigned _id_alloc { 0 };
unsigned _next_vcpu_id { 0 };
unsigned _priority;
Session_label const _session_label;
List<Vcpu> _vcpus { };
Vcpu * _lookup(Vcpu_id const vcpu_id)
{
for (Vcpu * vcpu = _vcpus.first(); vcpu; vcpu = vcpu->next())
if (vcpu->match(vcpu_id)) return vcpu;
return nullptr;
}
Registry<Registered<Vcpu>> _vcpus { };
/* helpers for vm_session_common.cc */
void _attach_vm_memory(Dataspace_component &, addr_t, Attach_attr);
void _detach_vm_memory(addr_t, size_t);
@ -163,24 +157,20 @@ class Genode::Vm_session_component
** Region_map_detach interface **
*********************************/
void detach(Region_map::Local_addr) override;
void unmap_region(addr_t, size_t) override;
/* used on destruction of attached dataspaces */
void detach(Region_map::Local_addr) override; /* vm_session_common.cc */
void unmap_region(addr_t, size_t) override; /* vm_session_common.cc */
/**************************
** Vm session interface **
**************************/
Dataspace_capability _cpu_state(Vcpu_id);
Capability<Native_vcpu> create_vcpu(Thread_capability);
void attach_pic(addr_t) override { /* unused on NOVA */ }
void attach(Dataspace_capability, addr_t, Attach_attr) override; /* vm_session_common.cc */
void detach(addr_t, size_t) override; /* vm_session_common.cc */
void _exception_handler(Signal_context_capability, Vcpu_id);
void _run(Vcpu_id);
void _pause(Vcpu_id) { }
void attach(Dataspace_capability, addr_t, Attach_attr) override;
void attach_pic(addr_t) override {}
void detach(addr_t, size_t) override;
Vcpu_id _create_vcpu(Thread_capability);
Capability<Native_vcpu> _native_vcpu(Vcpu_id) {
return Capability<Native_vcpu>(); }
};
#endif /* _CORE__VM_SESSION_COMPONENT_H_ */

View File

@ -1,11 +1,12 @@
/*
* \brief Core-specific instance of the VM session interface
* \author Alexander Boettcher
* \author Christian Helmuth
* \date 2018-08-26
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -13,7 +14,7 @@
/* Base includes */
#include <base/cache.h>
#include <cpu/vm_state.h>
#include <cpu/vcpu_state.h>
#include <util/list.h>
#include <util/flex_iterator.h>
@ -31,78 +32,60 @@
/* NOVA includes */
#include <nova/syscalls.h>
using Genode::addr_t;
using Genode::Vm_session_component;
using Vcpu_id = Genode::Vm_session::Vcpu_id;
using namespace Genode;
enum { CAP_RANGE_LOG2 = 2, CAP_RANGE = 1 << CAP_RANGE_LOG2 };
Vm_session_component::Vcpu::Vcpu(Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc,
Vcpu_id const id,
Affinity::Location const location,
Session_label const &label,
Trace::Control_area &trace_control_area,
Trace::Source_registry &trace_sources)
:
_ram_alloc(ram_alloc),
_cap_alloc(cap_alloc),
_trace_sources(trace_sources),
_sel_sm_ec_sc(invalid()),
_id(id),
_location(location),
_label(label),
_trace_control_slot(trace_control_area)
static addr_t invalid_sel() { return ~0UL; }
static Nova::uint8_t map_async_caps(Nova::Obj_crd const src,
Nova::Obj_crd const dst,
addr_t const dst_pd)
{
/* account caps required to setup vCPU */
_cap_alloc.withdraw(Cap_quota{CAP_RANGE});
using Nova::Utcb;
/* now try to allocate cap indexes */
_sel_sm_ec_sc = cap_map().insert(CAP_RANGE_LOG2);
if (_sel_sm_ec_sc == invalid()) {
error("out of caps in core");
_cap_alloc.replenish(Cap_quota{CAP_RANGE});
return;
}
Utcb &utcb = *reinterpret_cast<Utcb *>(Thread::myself()->utcb());
addr_t const src_pd = platform_specific().core_pd_sel();
try {
/* create ds for vCPU state */
_ds_cap = _ram_alloc.alloc(align_addr(sizeof(Genode::Vm_state), 12),
Cache_attribute::CACHED);
} catch (...) {
_cap_alloc.replenish(Cap_quota{CAP_RANGE});
cap_map().remove(_sel_sm_ec_sc, CAP_RANGE_LOG2);
throw;
}
utcb.set_msg_word(0);
/* ignore return value as one item always fits into the utcb */
bool const ok = utcb.append_item(src, 0);
(void)ok;
_trace_sources.insert(&_trace_source);
/* asynchronously map capabilities */
return Nova::delegate(src_pd, dst_pd, dst);
}
Vm_session_component::Vcpu::~Vcpu()
static Nova::uint8_t kernel_quota_upgrade(addr_t const pd_target)
{
_trace_sources.remove(&_trace_source);
if (_ds_cap.valid())
_ram_alloc.free(_ds_cap);
if (_sel_sm_ec_sc != invalid()) {
_cap_alloc.replenish(Cap_quota{CAP_RANGE});
cap_map().remove(_sel_sm_ec_sc, CAP_RANGE_LOG2);
}
return Pager_object::handle_oom(Pager_object::SRC_CORE_PD, pd_target,
"core", "ep",
Pager_object::Policy::UPGRADE_CORE_TO_DST);
}
addr_t Vm_session_component::Vcpu::new_pt_id()
{
enum { MAX_VM_EXITS = (1U << Nova::NUM_INITIAL_VCPU_PT_LOG2) };
if (_vm_pt_cnt >= MAX_VM_EXITS)
return invalid();
return MAX_VM_EXITS * _id.id + _vm_pt_cnt ++;
template <typename FUNC>
static uint8_t _with_kernel_quota_upgrade(addr_t const pd_target,
FUNC const &func)
{
uint8_t res;
do {
res = func();
} while (res == Nova::NOVA_PD_OOM &&
Nova::NOVA_OK == kernel_quota_upgrade(pd_target));
return res;
}
Genode::Trace::Source::Info Vm_session_component::Vcpu::trace_source_info() const
/********************************
** Vm_session_component::Vcpu **
********************************/
Trace::Source::Info Vm_session_component::Vcpu::trace_source_info() const
{
Genode::uint64_t sc_time = 0;
uint64_t sc_time = 0;
uint8_t res = Nova::sc_ctrl(sc_sel(), sc_time);
if (res != Nova::NOVA_OK)
@ -114,176 +97,34 @@ Genode::Trace::Source::Info Vm_session_component::Vcpu::trace_source_info() cons
}
static Nova::uint8_t map_async_caps(Nova::Obj_crd const src,
Nova::Obj_crd const dst,
addr_t const dst_pd)
void Vm_session_component::Vcpu::startup()
{
using Nova::Utcb;
using Genode::Thread;
Utcb &utcb = *reinterpret_cast<Utcb *>(Thread::myself()->utcb());
addr_t const src_pd = Genode::platform_specific().core_pd_sel();
utcb.set_msg_word(0);
/* ignore return value as one item always fits into the utcb */
bool const ok = utcb.append_item(src, 0);
(void)ok;
/* asynchronously map capabilities */
return Nova::delegate(src_pd, dst_pd, dst);
}
static Nova::uint8_t kernel_quota_upgrade(addr_t const pd_target)
{
using Genode::Pager_object;
return Pager_object::handle_oom(Pager_object::SRC_CORE_PD, pd_target,
"core", "ep",
Pager_object::Policy::UPGRADE_CORE_TO_DST);
}
template <typename FUNC>
static Genode::uint8_t _with_kernel_quota_upgrade(addr_t const pd_target,
FUNC const &func)
{
Genode::uint8_t res;
do {
res = func();
} while (res == Nova::NOVA_PD_OOM &&
Nova::NOVA_OK == kernel_quota_upgrade(pd_target));
return res;
}
Vcpu_id Vm_session_component::_create_vcpu(Thread_capability cap)
{
Vcpu_id ret;
if (!cap.valid()) return ret;
/* lookup vmm pd and cpu location of handler thread in VMM */
addr_t kernel_cpu_id = 0;
Affinity::Location vcpu_location;
auto lambda = [&] (Cpu_thread_component *ptr) {
if (!ptr)
return Vcpu::invalid();
Cpu_thread_component &thread = *ptr;
vcpu_location = thread.platform_thread().affinity();
kernel_cpu_id = platform_specific().kernel_cpu_id(thread.platform_thread().affinity());
return thread.platform_thread().pager().pd_sel();
};
addr_t const vmm_pd_sel = _ep.apply(cap, lambda);
/* if VMM pd lookup failed then deny to create vCPU */
if (!vmm_pd_sel || vmm_pd_sel == Vcpu::invalid())
return ret;
/* allocate vCPU object */
Vcpu &vcpu = *new (_heap) Vcpu(_constrained_md_ram_alloc,
_cap_quota_guard(),
Vcpu_id {_id_alloc},
vcpu_location,
_session_label,
_trace_control_area,
_trace_sources);
/* we ran out of caps in core */
if (!vcpu.ds_cap().valid())
return ret;
/* core PD selector */
addr_t const core_pd = platform_specific().core_pd_sel();
/* setup vCPU resources */
uint8_t res = _with_kernel_quota_upgrade(_pd_sel, [&] {
return Nova::create_sm(vcpu.sm_sel(), core_pd, 0);
});
if (res != Nova::NOVA_OK) {
error("create_sm = ", res);
destroy(_heap, &vcpu);
return ret;
}
addr_t const event_base = (1U << Nova::NUM_INITIAL_VCPU_PT_LOG2) * _id_alloc;
enum { THREAD_GLOBAL = true, NO_UTCB = 0, NO_STACK = 0 };
res = _with_kernel_quota_upgrade(_pd_sel, [&] {
return Nova::create_ec(vcpu.ec_sel(), _pd_sel, kernel_cpu_id,
NO_UTCB, NO_STACK, event_base, THREAD_GLOBAL);
});
if (res != Nova::NOVA_OK) {
error("create_ec = ", res);
destroy(_heap, &vcpu);
return ret;
}
addr_t const dst_sm_ec_sel = Nova::NUM_INITIAL_PT_RESERVED
+ _id_alloc * CAP_RANGE;
res = _with_kernel_quota_upgrade(vmm_pd_sel, [&] {
using namespace Nova;
enum { CAP_LOG2_COUNT = 1 };
int permission = Obj_crd::RIGHT_EC_RECALL | Obj_crd::RIGHT_SM_UP |
Obj_crd::RIGHT_SM_DOWN;
Obj_crd const src(vcpu.sm_sel(), CAP_LOG2_COUNT, permission);
Obj_crd const dst(dst_sm_ec_sel, CAP_LOG2_COUNT);
return map_async_caps(src, dst, vmm_pd_sel);
});
if (res != Nova::NOVA_OK)
{
error("map sm ", res, " ", _id_alloc);
destroy(_heap, &vcpu);
return ret;
}
_vcpus.insert(&vcpu);
_id_alloc++;
return vcpu.id();
}
void Vm_session_component::_run(Vcpu_id const vcpu_id)
{
Vcpu * ptr = _lookup(vcpu_id);
if (!ptr)
return;
Vcpu &vcpu = *ptr;
if (!vcpu.init())
return;
/* initialize SC on first call - do nothing on subsequent calls */
if (_alive) return;
uint8_t res = _with_kernel_quota_upgrade(_pd_sel, [&] {
return Nova::create_sc(vcpu.sc_sel(), _pd_sel, vcpu.ec_sel(),
return Nova::create_sc(sc_sel(), _pd_sel, ec_sel(),
Nova::Qpd(Nova::Qpd::DEFAULT_QUANTUM, _priority));
});
if (res == Nova::NOVA_OK)
vcpu.alive();
_alive = true;
else
error("create_sc=", res);
}
void Vm_session_component::_exception_handler(Signal_context_capability const cap,
Vcpu_id const vcpu_id)
void Vm_session_component::Vcpu::exit_handler(unsigned const exit,
Signal_context_capability const cap)
{
if (!cap.valid())
return;
Vcpu * ptr = _lookup(vcpu_id);
if (!ptr)
if (exit >= Nova::NUM_INITIAL_VCPU_PT)
return;
Vcpu &vcpu = *ptr;
addr_t const pt = vcpu.new_pt_id();
if (pt == Vcpu::invalid())
return;
/* map handler into vCPU-specific range of VM protection domain */
addr_t const pt = Nova::NUM_INITIAL_VCPU_PT * _id + exit;
uint8_t res = _with_kernel_quota_upgrade(_pd_sel, [&] {
Nova::Obj_crd const src(cap.local_name(), 0);
@ -296,77 +137,111 @@ void Vm_session_component::_exception_handler(Signal_context_capability const ca
error("map pt ", res, " failed");
}
Genode::Dataspace_capability Vm_session_component::_cpu_state(Vcpu_id const vcpu_id)
{
Vcpu * ptr = _lookup(vcpu_id);
if (!ptr)
return Dataspace_capability();
Vcpu &vcpu = *ptr;
return vcpu.ds_cap();
}
Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
Resources resources,
Label const &label,
Diag,
Ram_allocator &ram,
Region_map &local_rm,
unsigned const priority,
Trace::Source_registry &trace_sources)
Vm_session_component::Vcpu::Vcpu(Rpc_entrypoint &ep,
Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc,
unsigned const id,
unsigned const kernel_id,
Affinity::Location const location,
unsigned const priority,
Session_label const &label,
addr_t const pd_sel,
addr_t const core_pd_sel,
addr_t const vmm_pd_sel,
Trace::Control_area &trace_control_area,
Trace::Source_registry &trace_sources)
:
Ram_quota_guard(resources.ram_quota),
Cap_quota_guard(resources.cap_quota),
_ep(ep),
_trace_control_area(ram, local_rm), _trace_sources(trace_sources),
_constrained_md_ram_alloc(ram, _ram_quota_guard(), _cap_quota_guard()),
_heap(_constrained_md_ram_alloc, local_rm),
_priority(scale_priority(priority, "VM session")),
_session_label(label)
_ram_alloc(ram_alloc),
_cap_alloc(cap_alloc),
_trace_sources(trace_sources),
_sel_sm_ec_sc(invalid_sel()),
_id(id),
_location(location),
_priority(priority),
_label(label),
_pd_sel(pd_sel),
_trace_control_slot(trace_control_area)
{
_cap_quota_guard().withdraw(Cap_quota{1});
/* account caps required to setup vCPU */
Cap_quota_guard::Reservation caps(_cap_alloc, Cap_quota{CAP_RANGE});
_pd_sel = cap_map().insert();
if (!_pd_sel || _pd_sel == Vcpu::invalid())
throw Service_denied();
/* now try to allocate cap indexes */
_sel_sm_ec_sc = cap_map().insert(CAP_RANGE_LOG2);
if (_sel_sm_ec_sc == invalid_sel()) {
error("out of caps in core");
throw Creation_failed();
}
/* setup resources */
uint8_t res = _with_kernel_quota_upgrade(_pd_sel, [&] {
return Nova::create_sm(sm_sel(), core_pd_sel, 0);
});
addr_t const core_pd = platform_specific().core_pd_sel();
enum { KEEP_FREE_PAGES_NOT_AVAILABLE_FOR_UPGRADE = 2, UPPER_LIMIT_PAGES = 32 };
uint8_t res = Nova::create_pd(_pd_sel, core_pd, Nova::Obj_crd(),
KEEP_FREE_PAGES_NOT_AVAILABLE_FOR_UPGRADE,
UPPER_LIMIT_PAGES);
if (res != Nova::NOVA_OK) {
error("create_pd = ", res);
cap_map().remove(_pd_sel, 0, true);
throw Service_denied();
cap_map().remove(_sel_sm_ec_sc, CAP_RANGE_LOG2);
error("create_sm = ", res);
throw Creation_failed();
}
/* configure managed VM area */
_map.add_range(0, 0UL - 0x1000);
_map.add_range(0UL - 0x1000, 0x1000);
addr_t const event_base = (1U << Nova::NUM_INITIAL_VCPU_PT_LOG2) * id;
enum { THREAD_GLOBAL = true, NO_UTCB = 0, NO_STACK = 0 };
res = _with_kernel_quota_upgrade(_pd_sel, [&] {
return Nova::create_ec(ec_sel(), _pd_sel, kernel_id,
NO_UTCB, NO_STACK, event_base, THREAD_GLOBAL);
});
if (res != Nova::NOVA_OK) {
cap_map().remove(_sel_sm_ec_sc, CAP_RANGE_LOG2);
error("create_ec = ", res);
throw Creation_failed();
}
addr_t const dst_sm_ec_sel = Nova::NUM_INITIAL_PT_RESERVED + _id*CAP_RANGE;
res = _with_kernel_quota_upgrade(vmm_pd_sel, [&] {
using namespace Nova;
enum { CAP_LOG2_COUNT = 1 };
int permission = Obj_crd::RIGHT_EC_RECALL | Obj_crd::RIGHT_SM_UP |
Obj_crd::RIGHT_SM_DOWN;
Obj_crd const src(sm_sel(), CAP_LOG2_COUNT, permission);
Obj_crd const dst(dst_sm_ec_sel, CAP_LOG2_COUNT);
return map_async_caps(src, dst, vmm_pd_sel);
});
if (res != Nova::NOVA_OK) {
cap_map().remove(_sel_sm_ec_sc, CAP_RANGE_LOG2);
error("map sm ", res, " ", _id);
throw Creation_failed();
}
_ep.manage(this);
_trace_sources.insert(&_trace_source);
caps.acknowledge();
}
Vm_session_component::~Vm_session_component()
Vm_session_component::Vcpu::~Vcpu()
{
for (;Vcpu * vcpu = _vcpus.first();) {
_vcpus.remove(vcpu);
destroy(_heap, vcpu);
_ep.dissolve(this);
_trace_sources.remove(&_trace_source);
if (_sel_sm_ec_sc != invalid_sel()) {
_cap_alloc.replenish(Cap_quota{CAP_RANGE});
cap_map().remove(_sel_sm_ec_sc, CAP_RANGE_LOG2);
}
/* detach all regions */
while (true) {
addr_t out_addr = 0;
if (!_map.any_block_addr(&out_addr))
break;
detach(out_addr);
}
if (_pd_sel && _pd_sel != Vcpu::invalid())
cap_map().remove(_pd_sel, 0, true);
}
/**************************
** Vm_session_component **
**************************/
void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc,
addr_t const guest_phys,
Attach_attr const attribute)
@ -420,3 +295,120 @@ void Vm_session_component::_detach_vm_memory(addr_t guest_phys, size_t size)
page = flex.page();
}
}
Capability<Vm_session::Native_vcpu> Vm_session_component::create_vcpu(Thread_capability cap)
{
if (!cap.valid()) return { };
/* lookup vmm pd and cpu location of handler thread in VMM */
addr_t kernel_cpu_id = 0;
Affinity::Location vcpu_location;
auto lambda = [&] (Cpu_thread_component *ptr) {
if (!ptr)
return invalid_sel();
Cpu_thread_component &thread = *ptr;
vcpu_location = thread.platform_thread().affinity();
kernel_cpu_id = platform_specific().kernel_cpu_id(thread.platform_thread().affinity());
return thread.platform_thread().pager().pd_sel();
};
addr_t const vmm_pd_sel = _ep.apply(cap, lambda);
/* if VMM pd lookup failed then deny to create vCPU */
if (!vmm_pd_sel || vmm_pd_sel == invalid_sel())
return { };
/* XXX this is a quite limited ID allocator... */
unsigned const vcpu_id = _next_vcpu_id;
try {
Vcpu &vcpu =
*new (_heap) Registered<Vcpu>(_vcpus,
_ep,
_constrained_md_ram_alloc,
_cap_quota_guard(),
vcpu_id,
kernel_cpu_id,
vcpu_location,
_priority,
_session_label,
_pd_sel,
platform_specific().core_pd_sel(),
vmm_pd_sel,
_trace_control_area,
_trace_sources);
++_next_vcpu_id;
return vcpu.cap();
} catch (Vcpu::Creation_failed&) {
return { };
}
}
Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
Resources resources,
Label const &label,
Diag,
Ram_allocator &ram,
Region_map &local_rm,
unsigned const priority,
Trace::Source_registry &trace_sources)
:
Ram_quota_guard(resources.ram_quota),
Cap_quota_guard(resources.cap_quota),
_ep(ep),
_trace_control_area(ram, local_rm), _trace_sources(trace_sources),
_constrained_md_ram_alloc(ram, _ram_quota_guard(), _cap_quota_guard()),
_heap(_constrained_md_ram_alloc, local_rm),
_priority(scale_priority(priority, "VM session")),
_session_label(label)
{
_cap_quota_guard().withdraw(Cap_quota{1});
_pd_sel = cap_map().insert();
if (!_pd_sel || _pd_sel == invalid_sel())
throw Service_denied();
addr_t const core_pd = platform_specific().core_pd_sel();
enum { KEEP_FREE_PAGES_NOT_AVAILABLE_FOR_UPGRADE = 2, UPPER_LIMIT_PAGES = 32 };
uint8_t res = Nova::create_pd(_pd_sel, core_pd, Nova::Obj_crd(),
KEEP_FREE_PAGES_NOT_AVAILABLE_FOR_UPGRADE,
UPPER_LIMIT_PAGES);
if (res != Nova::NOVA_OK) {
error("create_pd = ", res);
cap_map().remove(_pd_sel, 0, true);
throw Service_denied();
}
/*
* Configure managed VM area. The two ranges work around the size
* limitation to ULONG_MAX.
*/
_map.add_range(0, 0UL - 0x1000);
_map.add_range(0UL - 0x1000, 0x1000);
}
Vm_session_component::~Vm_session_component()
{
_vcpus.for_each([&] (Vcpu &vcpu) {
destroy(_heap, &vcpu); });
/* detach all regions */
while (true) {
addr_t out_addr = 0;
if (!_map.any_block_addr(&out_addr))
break;
detach(out_addr);
}
if (_pd_sel && _pd_sel != invalid_sel())
cap_map().remove(_pd_sel, 0, true);
}

View File

@ -0,0 +1,29 @@
/*
* \brief NOVA vCPU RPC interface
* \author Christian Helmuth
* \author Alexander Böttcher
* \date 2021-01-19
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__NOVA_NATIVE_VCPU__NOVA_NATIVE_VCPU_H_
#define _INCLUDE__NOVA_NATIVE_VCPU__NOVA_NATIVE_VCPU_H_
#include <vm_session/vm_session.h>
#include <dataspace/dataspace.h>
struct Genode::Vm_session::Native_vcpu : Interface
{
GENODE_RPC(Rpc_startup, void, startup);
GENODE_RPC(Rpc_exit_handler, void, exit_handler, unsigned, Signal_context_capability);
GENODE_RPC_INTERFACE(Rpc_startup, Rpc_exit_handler);
};
#endif /* _INCLUDE__NOVA_NATIVE_VCPU__NOVA_NATIVE_VCPU_H_ */

View File

@ -0,0 +1,796 @@
/*
* \brief NOVA-specific VM-connection implementation
* \author Alexander Boettcher
* \author Christian Helmuth
* \date 2018-08-27
*/
/*
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/env.h>
#include <base/allocator.h>
#include <base/registry.h>
#include <base/attached_dataspace.h>
#include <vm_session/connection.h>
#include <vm_session/handler.h>
#include <util/noncopyable.h>
#include <cpu/vcpu_state.h>
/* Nova includes */
#include <nova/capability_space.h>
#include <nova/native_thread.h>
#include <nova/syscalls.h>
#include <nova_native_pd/client.h>
#include <nova_native_vcpu/nova_native_vcpu.h>
using namespace Genode;
using Exit_config = Vm_connection::Exit_config;
/******************************
** NOVA vCPU implementation **
******************************/
struct Nova_vcpu : Rpc_client<Vm_session::Native_vcpu>, Noncopyable
{
private:
typedef Id_space<Nova_vcpu> Vcpu_space;
static Vcpu_space &_vcpu_space()
{
static Vcpu_space instance;
return instance;
}
Vcpu_space::Element _id_elem;
struct Vcpu_id_space_exhausted : Exception { };
Signal_dispatcher_base &_obj;
Allocator &_alloc;
void *_ep_handler { nullptr };
void *_dispatching { nullptr };
bool _block { true };
bool _use_guest_fpu { false };
Vcpu_state _vcpu_state __attribute__((aligned(0x10))) { };
uint8_t _fpu_ep[512] __attribute__((aligned(0x10)));
enum Remote_state_requested {
NONE = 0,
PAUSE = 1,
RUN = 2
} _remote { NONE };
inline void _read_nova_state(Nova::Utcb &utcb, unsigned exit_reason);
inline void _write_nova_state(Nova::Utcb &utcb);
addr_t _sm_sel() const {
return Nova::NUM_INITIAL_PT_RESERVED + _id_elem.id().value * 4; }
addr_t _ec_sel() const { return _sm_sel() + 1; }
/**
* NOVA badge with 16-bit exit reason and 16-bit artificial vCPU I
*/
struct Badge
{
uint32_t _value;
Badge(unsigned long value)
: _value((uint32_t)value) { }
Badge(uint16_t vcpu_id, uint16_t exit_reason)
: _value((uint32_t)(vcpu_id << 16) | exit_reason) { }
uint16_t exit_reason() const { return (uint16_t)( _value & 0xffff); }
uint16_t vcpu_id() const { return (uint16_t)((_value >> 16) & 0xffff); }
uint32_t value() const { return _value; }
};
bool _handle_exit(Nova::Utcb &utcb, uint16_t exit_reason);
__attribute__((regparm(1))) static void _exit_entry(addr_t badge);
Nova::Mtd _portal_mtd(unsigned exit, Exit_config const &config)
{
/* TODO define and implement omissions */
(void)exit;
(void)config;
return Nova::Mtd(Nova::Mtd::ALL);
Genode::addr_t mtd = 0;
mtd |= Nova::Mtd::ACDB;
mtd |= Nova::Mtd::EBSD;
mtd |= Nova::Mtd::EFL;
mtd |= Nova::Mtd::ESP;
mtd |= Nova::Mtd::EIP;
mtd |= Nova::Mtd::DR;
mtd |= Nova::Mtd::R8_R15;
mtd |= Nova::Mtd::CR;
mtd |= Nova::Mtd::CSSS;
mtd |= Nova::Mtd::ESDS;
mtd |= Nova::Mtd::FSGS;
mtd |= Nova::Mtd::TR;
mtd |= Nova::Mtd::LDTR;
mtd |= Nova::Mtd::GDTR;
mtd |= Nova::Mtd::IDTR;
mtd |= Nova::Mtd::SYS;
mtd |= Nova::Mtd::CTRL;
mtd |= Nova::Mtd::INJ;
mtd |= Nova::Mtd::STA;
mtd |= Nova::Mtd::TSC;
mtd |= Nova::Mtd::EFER;
mtd |= Nova::Mtd::PDPTE;
mtd |= Nova::Mtd::SYSCALL_SWAPGS;
mtd |= Nova::Mtd::TPR;
mtd |= Nova::Mtd::QUAL;
_use_guest_fpu = true;
mtd |= Nova::Mtd::FPU;
return Nova::Mtd(mtd);
}
static Capability<Native_vcpu> _create_vcpu(Vm_connection &, Vcpu_handler_base &);
static Signal_context_capability _create_exit_handler(Pd_session &pd,
Vcpu_handler_base &handler,
uint16_t vcpu_id,
uint16_t exit_reason,
Nova::Mtd mtd);
/*
* Noncopyable
*/
Nova_vcpu(Nova_vcpu const &) = delete;
Nova_vcpu &operator = (Nova_vcpu const &) = delete;
public:
Nova_vcpu(Env &env, Vm_connection &vm, Allocator &alloc,
Vcpu_handler_base &handler, Exit_config const &exit_config);
void run();
void pause();
Vcpu_state & state() { return _vcpu_state; }
};
void Nova_vcpu::_read_nova_state(Nova::Utcb &utcb, unsigned exit_reason)
{
typedef Genode::Vcpu_state::Segment Segment;
typedef Genode::Vcpu_state::Range Range;
state().discharge();
state().exit_reason = exit_reason;
if (utcb.mtd & Nova::Mtd::FPU) {
state().fpu.charge([] (Vcpu_state::Fpu::State &fpu) {
asm volatile ("fxsave %0" : "=m" (fpu) :: "memory");
});
asm volatile ("fxrstor %0" : : "m" (*_fpu_ep) : "memory");
}
if (utcb.mtd & Nova::Mtd::ACDB) {
state().ax.charge(utcb.ax);
state().cx.charge(utcb.cx);
state().dx.charge(utcb.dx);
state().bx.charge(utcb.bx);
}
if (utcb.mtd & Nova::Mtd::EBSD) {
state().di.charge(utcb.di);
state().si.charge(utcb.si);
state().bp.charge(utcb.bp);
}
if (utcb.mtd & Nova::Mtd::EFL) state().flags.charge(utcb.flags);
if (utcb.mtd & Nova::Mtd::ESP) state().sp.charge(utcb.sp);
if (utcb.mtd & Nova::Mtd::DR) state().dr7.charge(utcb.dr7);
if (utcb.mtd & Nova::Mtd::EIP) {
state().ip.charge(utcb.ip);
state().ip_len.charge(utcb.instr_len);
}
if (utcb.mtd & Nova::Mtd::R8_R15) {
state(). r8.charge(utcb.read_r8());
state(). r9.charge(utcb.read_r9());
state().r10.charge(utcb.read_r10());
state().r11.charge(utcb.read_r11());
state().r12.charge(utcb.read_r12());
state().r13.charge(utcb.read_r13());
state().r14.charge(utcb.read_r14());
state().r15.charge(utcb.read_r15());
}
if (utcb.mtd & Nova::Mtd::CR) {
state().cr0.charge(utcb.cr0);
state().cr2.charge(utcb.cr2);
state().cr3.charge(utcb.cr3);
state().cr4.charge(utcb.cr4);
}
if (utcb.mtd & Nova::Mtd::CSSS) {
state().cs.charge(Segment { .sel = utcb.cs.sel,
.ar = utcb.cs.ar,
.limit = utcb.cs.limit,
.base = utcb.cs.base });
state().ss.charge(Segment { .sel = utcb.ss.sel,
.ar = utcb.ss.ar,
.limit = utcb.ss.limit,
.base = utcb.ss.base });
}
if (utcb.mtd & Nova::Mtd::ESDS) {
state().es.charge(Segment { .sel = utcb.es.sel,
.ar = utcb.es.ar,
.limit = utcb.es.limit,
.base = utcb.es.base });
state().ds.charge(Segment { .sel = utcb.ds.sel,
.ar = utcb.ds.ar,
.limit = utcb.ds.limit,
.base = utcb.ds.base });
}
if (utcb.mtd & Nova::Mtd::FSGS) {
state().fs.charge(Segment { .sel = utcb.fs.sel,
.ar = utcb.fs.ar,
.limit = utcb.fs.limit,
.base = utcb.fs.base });
state().gs.charge(Segment { .sel = utcb.gs.sel,
.ar = utcb.gs.ar,
.limit = utcb.gs.limit,
.base = utcb.gs.base });
}
if (utcb.mtd & Nova::Mtd::TR) {
state().tr.charge(Segment { .sel = utcb.tr.sel,
.ar = utcb.tr.ar,
.limit = utcb.tr.limit,
.base = utcb.tr.base });
}
if (utcb.mtd & Nova::Mtd::LDTR) {
state().ldtr.charge(Segment { .sel = utcb.ldtr.sel,
.ar = utcb.ldtr.ar,
.limit = utcb.ldtr.limit,
.base = utcb.ldtr.base });
}
if (utcb.mtd & Nova::Mtd::GDTR) {
state().gdtr.charge(Range { .limit = utcb.gdtr.limit,
.base = utcb.gdtr.base });
}
if (utcb.mtd & Nova::Mtd::IDTR) {
state().idtr.charge(Range { .limit = utcb.idtr.limit,
.base = utcb.idtr.base });
}
if (utcb.mtd & Nova::Mtd::SYS) {
state().sysenter_cs.charge(utcb.sysenter_cs);
state().sysenter_sp.charge(utcb.sysenter_sp);
state().sysenter_ip.charge(utcb.sysenter_ip);
}
if (utcb.mtd & Nova::Mtd::QUAL) {
state().qual_primary.charge(utcb.qual[0]);
state().qual_secondary.charge(utcb.qual[1]);
}
if (utcb.mtd & Nova::Mtd::CTRL) {
state().ctrl_primary.charge(utcb.ctrl[0]);
state().ctrl_secondary.charge(utcb.ctrl[1]);
}
if (utcb.mtd & Nova::Mtd::INJ) {
state().inj_info.charge(utcb.inj_info);
state().inj_error.charge(utcb.inj_error);
}
if (utcb.mtd & Nova::Mtd::STA) {
state().intr_state.charge(utcb.intr_state);
state().actv_state.charge(utcb.actv_state);
}
if (utcb.mtd & Nova::Mtd::TSC) {
state().tsc.charge(utcb.tsc_val);
state().tsc_offset.charge(utcb.tsc_off);
}
if (utcb.mtd & Nova::Mtd::EFER) {
state().efer.charge(utcb.read_efer());
}
if (utcb.mtd & Nova::Mtd::PDPTE) {
state().pdpte_0.charge(utcb.pdpte[0]);
state().pdpte_1.charge(utcb.pdpte[1]);
state().pdpte_2.charge(utcb.pdpte[2]);
state().pdpte_3.charge(utcb.pdpte[3]);
}
if (utcb.mtd & Nova::Mtd::SYSCALL_SWAPGS) {
state().star.charge(utcb.read_star());
state().lstar.charge(utcb.read_lstar());
state().cstar.charge(utcb.read_cstar());
state().fmask.charge(utcb.read_fmask());
state().kernel_gs_base.charge(utcb.read_kernel_gs_base());
}
if (utcb.mtd & Nova::Mtd::TPR) {
state().tpr.charge(utcb.read_tpr());
state().tpr_threshold.charge(utcb.read_tpr_threshold());
}
}
void Nova_vcpu::_write_nova_state(Nova::Utcb &utcb)
{
utcb.items = 0;
utcb.mtd = 0;
if (state().ax.charged() || state().cx.charged() ||
state().dx.charged() || state().bx.charged()) {
utcb.mtd |= Nova::Mtd::ACDB;
utcb.ax = state().ax.value();
utcb.cx = state().cx.value();
utcb.dx = state().dx.value();
utcb.bx = state().bx.value();
}
if (state().bp.charged() || state().di.charged() || state().si.charged()) {
utcb.mtd |= Nova::Mtd::EBSD;
utcb.di = state().di.value();
utcb.si = state().si.value();
utcb.bp = state().bp.value();
}
if (state().flags.charged()) {
utcb.mtd |= Nova::Mtd::EFL;
utcb.flags = state().flags.value();
}
if (state().sp.charged()) {
utcb.mtd |= Nova::Mtd::ESP;
utcb.sp = state().sp.value();
}
if (state().ip.charged()) {
utcb.mtd |= Nova::Mtd::EIP;
utcb.ip = state().ip.value();
utcb.instr_len = state().ip_len.value();
}
if (state().dr7.charged()) {
utcb.mtd |= Nova::Mtd::DR;
utcb.dr7 = state().dr7.value();
}
if (state().r8.charged() || state().r9.charged() ||
state().r10.charged() || state().r11.charged() ||
state().r12.charged() || state().r13.charged() ||
state().r14.charged() || state().r15.charged()) {
utcb.mtd |= Nova::Mtd::R8_R15;
utcb.write_r8 (state().r8.value());
utcb.write_r9 (state().r9.value());
utcb.write_r10(state().r10.value());
utcb.write_r11(state().r11.value());
utcb.write_r12(state().r12.value());
utcb.write_r13(state().r13.value());
utcb.write_r14(state().r14.value());
utcb.write_r15(state().r15.value());
}
if (state().cr0.charged() || state().cr2.charged() ||
state().cr3.charged() || state().cr4.charged()) {
utcb.mtd |= Nova::Mtd::CR;
utcb.cr0 = state().cr0.value();
utcb.cr2 = state().cr2.value();
utcb.cr3 = state().cr3.value();
utcb.cr4 = state().cr4.value();
}
if (state().cs.charged() || state().ss.charged()) {
utcb.mtd |= Nova::Mtd::CSSS;
utcb.cs.sel = state().cs.value().sel;
utcb.cs.ar = state().cs.value().ar;
utcb.cs.limit = state().cs.value().limit;
utcb.cs.base = state().cs.value().base;
utcb.ss.sel = state().ss.value().sel;
utcb.ss.ar = state().ss.value().ar;
utcb.ss.limit = state().ss.value().limit;
utcb.ss.base = state().ss.value().base;
}
if (state().es.charged() || state().ds.charged()) {
utcb.mtd |= Nova::Mtd::ESDS;
utcb.es.sel = state().es.value().sel;
utcb.es.ar = state().es.value().ar;
utcb.es.limit = state().es.value().limit;
utcb.es.base = state().es.value().base;
utcb.ds.sel = state().ds.value().sel;
utcb.ds.ar = state().ds.value().ar;
utcb.ds.limit = state().ds.value().limit;
utcb.ds.base = state().ds.value().base;
}
if (state().fs.charged() || state().gs.charged()) {
utcb.mtd |= Nova::Mtd::FSGS;
utcb.fs.sel = state().fs.value().sel;
utcb.fs.ar = state().fs.value().ar;
utcb.fs.limit = state().fs.value().limit;
utcb.fs.base = state().fs.value().base;
utcb.gs.sel = state().gs.value().sel;
utcb.gs.ar = state().gs.value().ar;
utcb.gs.limit = state().gs.value().limit;
utcb.gs.base = state().gs.value().base;
}
if (state().tr.charged()) {
utcb.mtd |= Nova::Mtd::TR;
utcb.tr.sel = state().tr.value().sel;
utcb.tr.ar = state().tr.value().ar;
utcb.tr.limit = state().tr.value().limit;
utcb.tr.base = state().tr.value().base;
}
if (state().ldtr.charged()) {
utcb.mtd |= Nova::Mtd::LDTR;
utcb.ldtr.sel = state().ldtr.value().sel;
utcb.ldtr.ar = state().ldtr.value().ar;
utcb.ldtr.limit = state().ldtr.value().limit;
utcb.ldtr.base = state().ldtr.value().base;
}
if (state().gdtr.charged()) {
utcb.mtd |= Nova::Mtd::GDTR;
utcb.gdtr.limit = state().gdtr.value().limit;
utcb.gdtr.base = state().gdtr.value().base;
}
if (state().idtr.charged()) {
utcb.mtd |= Nova::Mtd::IDTR;
utcb.idtr.limit = state().idtr.value().limit;
utcb.idtr.base = state().idtr.value().base;
}
if (state().sysenter_cs.charged() || state().sysenter_sp.charged() ||
state().sysenter_ip.charged()) {
utcb.mtd |= Nova::Mtd::SYS;
utcb.sysenter_cs = state().sysenter_cs.value();
utcb.sysenter_sp = state().sysenter_sp.value();
utcb.sysenter_ip = state().sysenter_ip.value();
}
if (state().ctrl_primary.charged() || state().ctrl_secondary.charged()) {
utcb.mtd |= Nova::Mtd::CTRL;
utcb.ctrl[0] = state().ctrl_primary.value();
utcb.ctrl[1] = state().ctrl_secondary.value();
}
if (state().inj_info.charged() || state().inj_error.charged()) {
utcb.mtd |= Nova::Mtd::INJ;
utcb.inj_info = state().inj_info.value();
utcb.inj_error = state().inj_error.value();
}
if (state().intr_state.charged() || state().actv_state.charged()) {
utcb.mtd |= Nova::Mtd::STA;
utcb.intr_state = state().intr_state.value();
utcb.actv_state = state().actv_state.value();
}
if (state().tsc.charged() || state().tsc_offset.charged()) {
utcb.mtd |= Nova::Mtd::TSC;
utcb.tsc_val = state().tsc.value();
utcb.tsc_off = state().tsc_offset.value();
}
if (state().efer.charged()) {
utcb.mtd |= Nova::Mtd::EFER;
utcb.write_efer(state().efer.value());
}
if (state().pdpte_0.charged() || state().pdpte_1.charged() ||
state().pdpte_2.charged() || state().pdpte_3.charged()) {
utcb.mtd |= Nova::Mtd::PDPTE;
utcb.pdpte[0] = state().pdpte_0.value();
utcb.pdpte[1] = state().pdpte_1.value();
utcb.pdpte[2] = state().pdpte_2.value();
utcb.pdpte[3] = state().pdpte_3.value();
}
if (state().star.charged() || state().lstar.charged() ||
state().cstar.charged() || state().fmask.charged() ||
state().kernel_gs_base.charged()) {
utcb.mtd |= Nova::Mtd::SYSCALL_SWAPGS;
utcb.write_star(state().star.value());
utcb.write_lstar(state().lstar.value());
utcb.write_cstar(state().cstar.value());
utcb.write_fmask(state().fmask.value());
utcb.write_kernel_gs_base(state().kernel_gs_base.value());
}
if (state().tpr.charged() || state().tpr_threshold.charged()) {
utcb.mtd |= Nova::Mtd::TPR;
utcb.write_tpr(state().tpr.value());
utcb.write_tpr_threshold(state().tpr_threshold.value());
}
if (_use_guest_fpu || state().fpu.charged()) {
asm volatile ("fxsave %0" : "=m" (*_fpu_ep) :: "memory");
}
if (state().fpu.charged()) {
state().fpu.with_state([] (Vcpu_state::Fpu::State const &fpu) {
asm volatile ("fxrstor %0" : : "m" (fpu) : "memory");
});
}
}
void Nova_vcpu::run()
{
if (!_ep_handler) {
/* not started yet - trigger startup of native vCPU */
call<Rpc_startup>();
return;
}
Thread * const current = Thread::myself();
if (_dispatching == current) {
_block = false;
return;
}
if ((_ep_handler == current) && !_block)
return;
if (_ep_handler != current)
_remote = RUN;
Nova::ec_ctrl(Nova::EC_RECALL, _ec_sel());
Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_UP);
}
/*
* Do not touch the UTCB before _read_nova_state() and after
* _write_nova_state(), particularly not by logging diagnostics.
*/
bool Nova_vcpu::_handle_exit(Nova::Utcb &utcb, uint16_t exit_reason)
{
/* reset blocking state */
bool const previous_blocked = _block;
_block = true;
/* NOVA specific exit reasons */
enum { VM_EXIT_STARTUP = 0xfe, VM_EXIT_RECALL = 0xff };
if (exit_reason == VM_EXIT_STARTUP)
_ep_handler = Thread::myself();
/* transform state from NOVA to Genode */
if (exit_reason != VM_EXIT_RECALL || !previous_blocked)
_read_nova_state(utcb, exit_reason);
if (exit_reason == VM_EXIT_RECALL) {
if (previous_blocked)
state().exit_reason = exit_reason;
/* consume potential multiple sem ups */
Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_UP);
Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_DOWNZERO);
if (_remote == PAUSE) {
_remote = NONE;
} else {
if (_remote == RUN) {
_remote = NONE;
if (!previous_blocked) {
/* still running - reply without state transfer */
_block = false;
utcb.items = 0;
utcb.mtd = 0;
return false;
}
}
if (previous_blocked) {
/* resume vCPU - with vCPU state update */
_block = false;
_write_nova_state(utcb);
return false;
}
}
}
try {
_dispatching = Thread::myself();
/* call dispatch handler */
_obj.dispatch(1);
_dispatching = nullptr;
} catch (...) {
_dispatching = nullptr;
throw;
}
if (_block) {
/* block vCPU in kernel - no vCPU state update */
utcb.items = 0;
utcb.mtd = 0;
return true;
}
/* reply to NOVA and transfer vCPU state */
_write_nova_state(utcb);
return false;
}
void Nova_vcpu::_exit_entry(addr_t badge)
{
Thread &myself = *Thread::myself();
Nova::Utcb &utcb = *reinterpret_cast<Nova::Utcb *>(myself.utcb());
uint16_t const exit_reason { Badge(badge).exit_reason() };
Vcpu_space::Id const vcpu_id { Badge(badge).vcpu_id() };
try {
_vcpu_space().apply<Nova_vcpu>(vcpu_id, [&] (Nova_vcpu &vcpu)
{
bool const block = vcpu._handle_exit(utcb, exit_reason);
if (block) {
Nova::reply(myself.stack_top(), vcpu._sm_sel());
} else {
Nova::reply(myself.stack_top());
}
});
} catch (Vcpu_space::Unknown_id &) {
/* somebody called us directly ? ... ignore/deny */
utcb.items = 0;
utcb.mtd = 0;
Nova::reply(myself.stack_top());
}
}
void Nova_vcpu::pause()
{
Thread * const current = Thread::myself();
if (_dispatching == current) {
/* current thread is already dispatching */
if (_block)
/* issue pause exit next time - fall through */
_block = false;
else {
_block = true;
return;
}
}
if ((_ep_handler == current) && _block) {
_remote = PAUSE;
/* already blocked */
}
if (_ep_handler != current)
_remote = PAUSE;
if (!_ep_handler) {
/* not started yet - let startup handler issue the recall */
return;
}
Nova::ec_ctrl(Nova::EC_RECALL, _ec_sel());
Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_UP);
}
Signal_context_capability Nova_vcpu::_create_exit_handler(Pd_session &pd,
Vcpu_handler_base &handler,
uint16_t vcpu_id,
uint16_t exit_reason,
Nova::Mtd mtd)
{
Thread *tep = reinterpret_cast<Thread *>(&handler.rpc_ep());
Native_capability thread_cap = Capability_space::import(tep->native_thread().ec_sel);
Nova_native_pd_client native_pd { pd.native_pd() };
Native_capability vm_exit_cap =
native_pd.alloc_rpc_cap(thread_cap, (addr_t)Nova_vcpu::_exit_entry, mtd.value());
Badge const badge { vcpu_id, exit_reason };
native_pd.imprint_rpc_cap(vm_exit_cap, badge.value());
return reinterpret_cap_cast<Signal_context>(vm_exit_cap);
}
Capability<Vm_session::Native_vcpu> Nova_vcpu::_create_vcpu(Vm_connection &vm,
Vcpu_handler_base &handler)
{
Thread &tep { *reinterpret_cast<Thread *>(&handler.rpc_ep()) };
return vm.with_upgrade([&] () {
return vm.call<Vm_session::Rpc_create_vcpu>(tep.cap()); });
}
Nova_vcpu::Nova_vcpu(Env &env, Vm_connection &vm, Allocator &alloc,
Vcpu_handler_base &handler, Exit_config const &exit_config)
:
Rpc_client<Native_vcpu>(_create_vcpu(vm, handler)),
_id_elem(*this, _vcpu_space()), _obj(handler), _alloc(alloc)
{
/*
* XXX can be alleviated by managing ID values with Bit_allocator
* that allocates lowest free index in dynamic scenarios
*/
if (_id_elem.id().value > 0xffff)
throw Vcpu_id_space_exhausted();
uint16_t const vcpu_id = (uint16_t)_id_elem.id().value;
Signal_context_capability dontcare_exit =
_create_exit_handler(env.pd(), handler, vcpu_id, 0x100, Nova::Mtd(Nova::Mtd::EIP));
for (unsigned i = 0; i < Nova::NUM_INITIAL_VCPU_PT; ++i) {
Signal_context_capability signal_exit;
Nova::Mtd mtd = _portal_mtd(i, exit_config);
if (mtd.value()) {
signal_exit = _create_exit_handler(env.pd(), handler, vcpu_id, i, mtd);
} else {
signal_exit = dontcare_exit;
}
call<Rpc_exit_handler>(i, signal_exit);
}
}
/**************
** vCPU API **
**************/
void Vm_connection::Vcpu::run() { static_cast<Nova_vcpu &>(_native_vcpu).run(); }
void Vm_connection::Vcpu::pause() { static_cast<Nova_vcpu &>(_native_vcpu).pause(); }
Vcpu_state & Vm_connection::Vcpu::state() { return static_cast<Nova_vcpu &>(_native_vcpu).state(); }
Vm_connection::Vcpu::Vcpu(Vm_connection &vm, Allocator &alloc,
Vcpu_handler_base &handler, Exit_config const &exit_config)
:
_native_vcpu(*new (alloc) Nova_vcpu(vm._env, vm, alloc, handler, exit_config))
{ }

View File

@ -1,791 +0,0 @@
/*
* \brief Client-side VM session interface
* \author Alexander Boettcher
* \date 2018-08-27
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <vm_session/client.h>
#include <base/env.h>
#include <base/allocator.h>
#include <base/registry.h>
#include <base/rpc_server.h>
#include <cpu/vm_state.h>
#include <nova/capability_space.h>
#include <nova/native_thread.h>
#include <nova/syscalls.h>
#include <nova_native_pd/client.h>
using namespace Genode;
struct Vcpu;
static Genode::Registry<Genode::Registered<Vcpu> > vcpus;
struct Vcpu {
private:
Signal_dispatcher_base &_obj;
Allocator &_alloc;
Vm_session_client::Vcpu_id _id;
addr_t _state { 0 };
void *_ep_handler { nullptr };
void *_dispatching { nullptr };
bool _block { true };
bool _use_guest_fpu { false };
uint8_t _fpu_ep[512] __attribute__((aligned(0x10)));
enum Remote_state_requested {
NONE = 0,
PAUSE = 1,
RUN = 2
} _remote { NONE };
void _read_nova_state(Nova::Utcb &utcb, Vm_state &state,
unsigned exit_reason)
{
typedef Genode::Vm_state::Segment Segment;
typedef Genode::Vm_state::Range Range;
state = Vm_state {};
state.exit_reason = exit_reason;
if (utcb.mtd & Nova::Mtd::FPU) {
state.fpu.value([&] (uint8_t *fpu, size_t const) {
asm volatile ("fxsave %0" : "=m" (*fpu) :: "memory");
});
asm volatile ("fxrstor %0" : : "m" (*_fpu_ep) : "memory");
}
if (utcb.mtd & Nova::Mtd::ACDB) {
state.ax.value(utcb.ax);
state.cx.value(utcb.cx);
state.dx.value(utcb.dx);
state.bx.value(utcb.bx);
}
if (utcb.mtd & Nova::Mtd::EBSD) {
state.di.value(utcb.di);
state.si.value(utcb.si);
state.bp.value(utcb.bp);
}
if (utcb.mtd & Nova::Mtd::EFL) state.flags.value(utcb.flags);
if (utcb.mtd & Nova::Mtd::ESP) state.sp.value(utcb.sp);
if (utcb.mtd & Nova::Mtd::DR) state.dr7.value(utcb.dr7);
if (utcb.mtd & Nova::Mtd::EIP) {
state.ip.value(utcb.ip);
state.ip_len.value(utcb.instr_len);
}
if (utcb.mtd & Nova::Mtd::R8_R15) {
state. r8.value(utcb.read_r8());
state. r9.value(utcb.read_r9());
state.r10.value(utcb.read_r10());
state.r11.value(utcb.read_r11());
state.r12.value(utcb.read_r12());
state.r13.value(utcb.read_r13());
state.r14.value(utcb.read_r14());
state.r15.value(utcb.read_r15());
}
if (utcb.mtd & Nova::Mtd::CR) {
state.cr0.value(utcb.cr0);
state.cr2.value(utcb.cr2);
state.cr3.value(utcb.cr3);
state.cr4.value(utcb.cr4);
}
if (utcb.mtd & Nova::Mtd::CSSS) {
state.cs.value(Segment{utcb.cs.sel, utcb.cs.ar, utcb.cs.limit,
utcb.cs.base});
state.ss.value(Segment{utcb.ss.sel, utcb.ss.ar, utcb.ss.limit,
utcb.ss.base});
}
if (utcb.mtd & Nova::Mtd::ESDS) {
state.es.value(Segment{utcb.es.sel, utcb.es.ar, utcb.es.limit,
utcb.es.base});
state.ds.value(Segment{utcb.ds.sel, utcb.ds.ar, utcb.ds.limit,
utcb.ds.base});
}
if (utcb.mtd & Nova::Mtd::FSGS) {
state.fs.value(Segment{utcb.fs.sel, utcb.fs.ar, utcb.fs.limit,
utcb.fs.base});
state.gs.value(Segment{utcb.gs.sel, utcb.gs.ar, utcb.gs.limit,
utcb.gs.base});
}
if (utcb.mtd & Nova::Mtd::TR) {
state.tr.value(Segment{utcb.tr.sel, utcb.tr.ar, utcb.tr.limit,
utcb.tr.base});
}
if (utcb.mtd & Nova::Mtd::LDTR) {
state.ldtr.value(Segment{utcb.ldtr.sel, utcb.ldtr.ar,
utcb.ldtr.limit, utcb.ldtr.base});
}
if (utcb.mtd & Nova::Mtd::GDTR) {
state.gdtr.value(Range{utcb.gdtr.base, utcb.gdtr.limit});
}
if (utcb.mtd & Nova::Mtd::IDTR) {
state.idtr.value(Range{utcb.idtr.base, utcb.idtr.limit});
}
if (utcb.mtd & Nova::Mtd::SYS) {
state.sysenter_cs.value(utcb.sysenter_cs);
state.sysenter_sp.value(utcb.sysenter_sp);
state.sysenter_ip.value(utcb.sysenter_ip);
}
if (utcb.mtd & Nova::Mtd::QUAL) {
state.qual_primary.value(utcb.qual[0]);
state.qual_secondary.value(utcb.qual[1]);
}
if (utcb.mtd & Nova::Mtd::CTRL) {
state.ctrl_primary.value(utcb.ctrl[0]);
state.ctrl_secondary.value(utcb.ctrl[1]);
}
if (utcb.mtd & Nova::Mtd::INJ) {
state.inj_info.value(utcb.inj_info);
state.inj_error.value(utcb.inj_error);
}
if (utcb.mtd & Nova::Mtd::STA) {
state.intr_state.value(utcb.intr_state);
state.actv_state.value(utcb.actv_state);
}
if (utcb.mtd & Nova::Mtd::TSC) {
state.tsc.value(utcb.tsc_val);
state.tsc_offset.value(utcb.tsc_off);
}
if (utcb.mtd & Nova::Mtd::EFER) {
state.efer.value(utcb.read_efer());
}
if (utcb.mtd & Nova::Mtd::PDPTE) {
state.pdpte_0.value(utcb.pdpte[0]);
state.pdpte_1.value(utcb.pdpte[1]);
state.pdpte_2.value(utcb.pdpte[2]);
state.pdpte_3.value(utcb.pdpte[3]);
}
if (utcb.mtd & Nova::Mtd::SYSCALL_SWAPGS) {
state.star.value(utcb.read_star());
state.lstar.value(utcb.read_lstar());
state.cstar.value(utcb.read_cstar());
state.fmask.value(utcb.read_fmask());
state.kernel_gs_base.value(utcb.read_kernel_gs_base());
}
if (utcb.mtd & Nova::Mtd::TPR) {
state.tpr.value(utcb.read_tpr());
state.tpr_threshold.value(utcb.read_tpr_threshold());
}
}
void _write_nova_state(Nova::Utcb &utcb, Vm_state &state)
{
utcb.items = 0;
utcb.mtd = 0;
if (state.ax.valid() || state.cx.valid() ||
state.dx.valid() || state.bx.valid()) {
utcb.mtd |= Nova::Mtd::ACDB;
utcb.ax = state.ax.value();
utcb.cx = state.cx.value();
utcb.dx = state.dx.value();
utcb.bx = state.bx.value();
}
if (state.bp.valid() || state.di.valid() || state.si.valid()) {
utcb.mtd |= Nova::Mtd::EBSD;
utcb.di = state.di.value();
utcb.si = state.si.value();
utcb.bp = state.bp.value();
}
if (state.flags.valid()) {
utcb.mtd |= Nova::Mtd::EFL;
utcb.flags = state.flags.value();
}
if (state.sp.valid()) {
utcb.mtd |= Nova::Mtd::ESP;
utcb.sp = state.sp.value();
}
if (state.ip.valid()) {
utcb.mtd |= Nova::Mtd::EIP;
utcb.ip = state.ip.value();
utcb.instr_len = state.ip_len.value();
}
if (state.dr7.valid()) {
utcb.mtd |= Nova::Mtd::DR;
utcb.dr7 = state.dr7.value();
}
if (state.r8.valid() || state.r9.valid() ||
state.r10.valid() || state.r11.valid() ||
state.r12.valid() || state.r13.valid() ||
state.r14.valid() || state.r15.valid()) {
utcb.mtd |= Nova::Mtd::R8_R15;
utcb.write_r8 (state.r8.value());
utcb.write_r9 (state.r9.value());
utcb.write_r10(state.r10.value());
utcb.write_r11(state.r11.value());
utcb.write_r12(state.r12.value());
utcb.write_r13(state.r13.value());
utcb.write_r14(state.r14.value());
utcb.write_r15(state.r15.value());
}
if (state.cr0.valid() || state.cr2.valid() || state.cr3.valid() ||
state.cr4.valid()) {
utcb.mtd |= Nova::Mtd::CR;
utcb.cr0 = state.cr0.value();
utcb.cr2 = state.cr2.value();
utcb.cr3 = state.cr3.value();
utcb.cr4 = state.cr4.value();
}
if (state.cs.valid() || state.ss.valid()) {
utcb.mtd |= Nova::Mtd::CSSS;
utcb.cs.sel = state.cs.value().sel;
utcb.cs.ar = state.cs.value().ar;
utcb.cs.limit = state.cs.value().limit;
utcb.cs.base = state.cs.value().base;
utcb.ss.sel = state.ss.value().sel;
utcb.ss.ar = state.ss.value().ar;
utcb.ss.limit = state.ss.value().limit;
utcb.ss.base = state.ss.value().base;
}
if (state.es.valid() || state.ds.valid()) {
utcb.mtd |= Nova::Mtd::ESDS;
utcb.es.sel = state.es.value().sel;
utcb.es.ar = state.es.value().ar;
utcb.es.limit = state.es.value().limit;
utcb.es.base = state.es.value().base;
utcb.ds.sel = state.ds.value().sel;
utcb.ds.ar = state.ds.value().ar;
utcb.ds.limit = state.ds.value().limit;
utcb.ds.base = state.ds.value().base;
}
if (state.fs.valid() || state.gs.valid()) {
utcb.mtd |= Nova::Mtd::FSGS;
utcb.fs.sel = state.fs.value().sel;
utcb.fs.ar = state.fs.value().ar;
utcb.fs.limit = state.fs.value().limit;
utcb.fs.base = state.fs.value().base;
utcb.gs.sel = state.gs.value().sel;
utcb.gs.ar = state.gs.value().ar;
utcb.gs.limit = state.gs.value().limit;
utcb.gs.base = state.gs.value().base;
}
if (state.tr.valid()) {
utcb.mtd |= Nova::Mtd::TR;
utcb.tr.sel = state.tr.value().sel;
utcb.tr.ar = state.tr.value().ar;
utcb.tr.limit = state.tr.value().limit;
utcb.tr.base = state.tr.value().base;
}
if (state.ldtr.valid()) {
utcb.mtd |= Nova::Mtd::LDTR;
utcb.ldtr.sel = state.ldtr.value().sel;
utcb.ldtr.ar = state.ldtr.value().ar;
utcb.ldtr.limit = state.ldtr.value().limit;
utcb.ldtr.base = state.ldtr.value().base;
}
if (state.gdtr.valid()) {
utcb.mtd |= Nova::Mtd::GDTR;
utcb.gdtr.limit = state.gdtr.value().limit;
utcb.gdtr.base = state.gdtr.value().base;
}
if (state.idtr.valid()) {
utcb.mtd |= Nova::Mtd::IDTR;
utcb.idtr.limit = state.idtr.value().limit;
utcb.idtr.base = state.idtr.value().base;
}
if (state.sysenter_cs.valid() || state.sysenter_sp.valid() ||
state.sysenter_ip.valid()) {
utcb.mtd |= Nova::Mtd::SYS;
utcb.sysenter_cs = state.sysenter_cs.value();
utcb.sysenter_sp = state.sysenter_sp.value();
utcb.sysenter_ip = state.sysenter_ip.value();
}
if (state.ctrl_primary.valid() || state.ctrl_secondary.valid()) {
utcb.mtd |= Nova::Mtd::CTRL;
utcb.ctrl[0] = state.ctrl_primary.value();
utcb.ctrl[1] = state.ctrl_secondary.value();
}
if (state.inj_info.valid() || state.inj_error.valid()) {
utcb.mtd |= Nova::Mtd::INJ;
utcb.inj_info = state.inj_info.value();
utcb.inj_error = state.inj_error.value();
}
if (state.intr_state.valid() || state.actv_state.valid()) {
utcb.mtd |= Nova::Mtd::STA;
utcb.intr_state = state.intr_state.value();
utcb.actv_state = state.actv_state.value();
}
if (state.tsc.valid() || state.tsc_offset.valid()) {
utcb.mtd |= Nova::Mtd::TSC;
utcb.tsc_val = state.tsc.value();
utcb.tsc_off = state.tsc_offset.value();
}
if (state.efer.valid()) {
utcb.mtd |= Nova::Mtd::EFER;
utcb.write_efer(state.efer.value());
}
if (state.pdpte_0.valid() || state.pdpte_1.valid() ||
state.pdpte_2.valid() || state.pdpte_3.valid()) {
utcb.mtd |= Nova::Mtd::PDPTE;
utcb.pdpte[0] = state.pdpte_0.value();
utcb.pdpte[1] = state.pdpte_1.value();
utcb.pdpte[2] = state.pdpte_2.value();
utcb.pdpte[3] = state.pdpte_3.value();
}
if (state.star.valid() || state.lstar.valid() ||
state.cstar.valid() || state.fmask.valid() ||
state.kernel_gs_base.valid()) {
utcb.mtd |= Nova::Mtd::SYSCALL_SWAPGS;
utcb.write_star(state.star.value());
utcb.write_lstar(state.lstar.value());
utcb.write_cstar(state.cstar.value());
utcb.write_fmask(state.fmask.value());
utcb.write_kernel_gs_base(state.kernel_gs_base.value());
}
if (state.tpr.valid() || state.tpr_threshold.valid()) {
utcb.mtd |= Nova::Mtd::TPR;
utcb.write_tpr(state.tpr.value());
utcb.write_tpr_threshold(state.tpr_threshold.value());
}
if (_use_guest_fpu || state.fpu.valid())
asm volatile ("fxsave %0" : "=m" (*_fpu_ep) :: "memory");
if (state.fpu.valid()) {
state.fpu.value([&] (uint8_t *fpu, size_t const) {
asm volatile ("fxrstor %0" : : "m" (*fpu) : "memory");
});
}
}
void _dispatch()
{
try {
_dispatching = Thread::myself();
/* call dispatch handler */
_obj.dispatch(1);
_dispatching = nullptr;
} catch (...) {
_dispatching = nullptr;
throw;
}
}
addr_t _sm_sel() const {
return Nova::NUM_INITIAL_PT_RESERVED + _id.id * 4; }
addr_t _ec_sel() const { return _sm_sel() + 1; }
Vcpu(const Vcpu&);
Vcpu &operator = (Vcpu const &);
public:
Vcpu(Vm_handler_base &o, Vm_session::Vcpu_id const id,
Allocator &alloc)
: _obj(o), _alloc(alloc), _id(id) { }
virtual ~Vcpu() { }
Allocator &allocator() { return _alloc; }
addr_t badge(uint16_t exit) const {
return ((0UL + _id.id) << (sizeof(exit) * 8)) | exit; }
Vm_session_client::Vcpu_id id() const { return _id; }
__attribute__((regparm(1))) static void exit_entry(addr_t o)
{
Thread &myself = *Thread::myself();
Nova::Utcb &utcb = *reinterpret_cast<Nova::Utcb *>(myself.utcb());
uint16_t const exit_reason = o;
unsigned const vcpu_id = o >> (sizeof(exit_reason) * 8);
Vcpu * vcpu = nullptr;
vcpus.for_each([&] (Vcpu &vc) {
if (vcpu_id == vc._id.id)
vcpu = &vc;
});
if (!vcpu) {
/* somebody called us directly ? ... ignore/deny */
utcb.items = 0;
utcb.mtd = 0;
Nova::reply(myself.stack_top());
}
/* reset blocking state */
bool const previous_blocked = vcpu->_block;
vcpu->_block = true;
/* NOVA specific exit reasons */
enum { VM_EXIT_STARTUP = 0xfe, VM_EXIT_RECALL = 0xff };
if (exit_reason == VM_EXIT_STARTUP)
vcpu->_ep_handler = &myself;
Vm_state &state = *reinterpret_cast<Vm_state *>(vcpu->_state);
/* transform state from NOVA to Genode */
if (exit_reason != VM_EXIT_RECALL || !previous_blocked)
vcpu->_read_nova_state(utcb, state, exit_reason);
if (exit_reason == VM_EXIT_RECALL) {
if (previous_blocked)
state.exit_reason = exit_reason;
/* consume potential multiple sem ups */
Nova::sm_ctrl(vcpu->_sm_sel(), Nova::SEMAPHORE_UP);
Nova::sm_ctrl(vcpu->_sm_sel(), Nova::SEMAPHORE_DOWNZERO);
if (vcpu->_remote == PAUSE) {
vcpu->_remote = NONE;
} else {
if (vcpu->_remote == RUN) {
vcpu->_remote = NONE;
if (!previous_blocked) {
/* still running - reply without state transfer */
vcpu->_block = false;
utcb.items = 0;
utcb.mtd = 0;
Nova::reply(myself.stack_top());
}
}
if (previous_blocked) {
/* resume vCPU - with vCPU state update */
vcpu->_block = false;
vcpu->_write_nova_state(utcb, state);
Nova::reply(myself.stack_top());
}
}
}
vcpu->_dispatch();
if (vcpu->_block) {
/* block vCPU in kernel - no vCPU state update */
utcb.items = 0;
utcb.mtd = 0;
Nova::reply(myself.stack_top(), vcpu->_sm_sel());
}
/* reply to NOVA and transfer vCPU state */
vcpu->_write_nova_state(utcb, state);
Nova::reply(myself.stack_top());
}
bool resume()
{
if (!_ep_handler) {
/* not started yet */
return true;
}
Thread * const current = Thread::myself();
if (_dispatching == current) {
_block = false;
return false;
}
if ((_ep_handler == current) && !_block)
return false;
if (_ep_handler != current)
_remote = RUN;
Nova::ec_ctrl(Nova::EC_RECALL, _ec_sel());
Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_UP);
return false;
}
void pause()
{
Thread * const current = Thread::myself();
if (_dispatching == current) {
/* current thread is already dispatching */
if (_block)
/* issue pause exit next time - fall through */
_block = false;
else {
_block = true;
return;
}
}
if ((_ep_handler == current) && _block) {
_remote = PAUSE;
/* already blocked */
}
if (_ep_handler != current)
_remote = PAUSE;
if (!_ep_handler) {
/* not started yet - let startup handler issue the recall */
return;
}
Nova::ec_ctrl(Nova::EC_RECALL, _ec_sel());
Nova::sm_ctrl(_sm_sel(), Nova::SEMAPHORE_UP);
}
void assign_ds_state(Region_map &rm, Dataspace_capability cap) {
_state = rm.attach(cap); }
Nova::Mtd portal_mtd(unsigned exit, Vm_handler_base &handler)
{
Vm_state &state = *reinterpret_cast<Vm_state *>(_state);
state = Vm_state {};
if (!handler.config_vm_event(state, exit))
return Nova::Mtd(Nova::Mtd::ALL);
Genode::addr_t mtd = 0;
if (state.ax.valid() || state.cx.valid() ||
state.dx.valid() || state.bx.valid())
mtd |= Nova::Mtd::ACDB;
if (state.bp.valid() || state.di.valid() || state.si.valid())
mtd |= Nova::Mtd::EBSD;
if (state.flags.valid())
mtd |= Nova::Mtd::EFL;
if (state.sp.valid())
mtd |= Nova::Mtd::ESP;
if (state.ip.valid())
mtd |= Nova::Mtd::EIP;
if (state.dr7.valid())
mtd |= Nova::Mtd::DR;
if (state.r8.valid() || state.r9.valid() || state.r10.valid() ||
state.r11.valid() || state.r12.valid() || state.r13.valid() ||
state.r14.valid() || state.r15.valid())
mtd |= Nova::Mtd::R8_R15;
if (state.cr0.valid() || state.cr2.valid() || state.cr3.valid() ||
state.cr4.valid())
mtd |= Nova::Mtd::CR;
if (state.cs.valid() || state.ss.valid())
mtd |= Nova::Mtd::CSSS;
if (state.es.valid() || state.ds.valid())
mtd |= Nova::Mtd::ESDS;
if (state.fs.valid() || state.gs.valid())
mtd |= Nova::Mtd::FSGS;
if (state.tr.valid())
mtd |= Nova::Mtd::TR;
if (state.ldtr.valid())
mtd |= Nova::Mtd::LDTR;
if (state.gdtr.valid())
mtd |= Nova::Mtd::GDTR;
if (state.idtr.valid())
mtd |= Nova::Mtd::IDTR;
if (state.sysenter_cs.valid() || state.sysenter_sp.valid() ||
state.sysenter_ip.valid())
mtd |= Nova::Mtd::SYS;
if (state.ctrl_primary.valid() || state.ctrl_secondary.valid())
mtd |= Nova::Mtd::CTRL;
if (state.inj_info.valid() || state.inj_error.valid())
mtd |= Nova::Mtd::INJ;
if (state.intr_state.valid() || state.actv_state.valid())
mtd |= Nova::Mtd::STA;
if (state.tsc.valid() || state.tsc_offset.valid())
mtd |= Nova::Mtd::TSC;
if (state.efer.valid())
mtd |= Nova::Mtd::EFER;
if (state.pdpte_0.valid() || state.pdpte_1.valid() ||
state.pdpte_2.valid() || state.pdpte_3.valid())
mtd |= Nova::Mtd::PDPTE;
if (state.star.valid() || state.lstar.valid() ||
state.cstar.valid() || state.fmask.valid() ||
state.kernel_gs_base.valid())
mtd |= Nova::Mtd::SYSCALL_SWAPGS;
if (state.tpr.valid() || state.tpr_threshold.valid())
mtd |= Nova::Mtd::TPR;
if (state.qual_primary.valid() || state.qual_secondary.valid())
mtd |= Nova::Mtd::QUAL;
if (state.fpu.valid()) {
_use_guest_fpu = true;
mtd |= Nova::Mtd::FPU;
}
state = Vm_state {};
return Nova::Mtd(mtd);
}
};
Signal_context_capability
static create_exit_handler(Pd_session &pd, Rpc_entrypoint &ep,
Vcpu &vcpu, unsigned exit_reason,
Nova::Mtd &mtd)
{
Thread * tep = reinterpret_cast<Thread *>(&ep);
Native_capability thread_cap = Capability_space::import(tep->native_thread().ec_sel);
Nova_native_pd_client native_pd { pd.native_pd() };
Native_capability vm_exit_cap = native_pd.alloc_rpc_cap(thread_cap,
(addr_t)Vcpu::exit_entry,
mtd.value());
native_pd.imprint_rpc_cap(vm_exit_cap, vcpu.badge(exit_reason));
return reinterpret_cap_cast<Signal_context>(vm_exit_cap);
}
Vm_session_client::Vcpu_id
Vm_session_client::create_vcpu(Allocator &alloc, Env &env,
Vm_handler_base &handler)
{
Thread * ep = reinterpret_cast<Thread *>(&handler._rpc_ep);
Vcpu * vcpu = new (alloc) Registered<Vcpu> (vcpus, handler,
call<Rpc_create_vcpu>(ep->cap()),
alloc);
vcpu->assign_ds_state(env.rm(), call<Rpc_cpu_state>(vcpu->id()));
Signal_context_capability dontcare_exit;
enum { MAX_VM_EXITS = (1U << Nova::NUM_INITIAL_VCPU_PT_LOG2) };
for (unsigned i = 0; i < MAX_VM_EXITS; i++) {
Signal_context_capability signal_exit;
Nova::Mtd mtd = vcpu->portal_mtd(i, handler);
if (mtd.value()) {
signal_exit = create_exit_handler(env.pd(), handler._rpc_ep,
*vcpu, i, mtd);
} else {
if (!dontcare_exit.valid()) {
Nova::Mtd mtd_ip(Nova::Mtd::EIP);
dontcare_exit = create_exit_handler(env.pd(), handler._rpc_ep,
*vcpu, 0x100, mtd_ip);
}
signal_exit = dontcare_exit;
}
call<Rpc_exception_handler>(signal_exit, vcpu->id());
}
return vcpu->id();
}
void Vm_session_client::run(Vm_session_client::Vcpu_id vcpu_id)
{
vcpus.for_each([&] (Vcpu &vcpu) {
if (vcpu.id().id != vcpu_id.id)
return;
if (vcpu.resume())
call<Rpc_run>(vcpu.id());
});
}
void Vm_session_client::pause(Vm_session_client::Vcpu_id vcpu_id)
{
vcpus.for_each([&] (Vcpu &vcpu) {
if (vcpu.id().id != vcpu_id.id)
return;
vcpu.pause();
});
}
Dataspace_capability Vm_session_client::cpu_state(Vcpu_id vcpu_id)
{
Dataspace_capability cap;
vcpus.for_each([&] (Vcpu &vcpu) {
if (vcpu.id().id == vcpu_id.id)
cap = call<Rpc_cpu_state>(vcpu_id);
});
return cap;
}
Vm_session::~Vm_session()
{
vcpus.for_each([&] (Vcpu &vc) {
Allocator &alloc = vc.allocator();
destroy(alloc, &vc);
});
}

View File

@ -4,6 +4,4 @@
# \date 2013-02-14
#
vpath vm_session.cc $(REP_DIR)/src/lib/base/x86
include $(REP_DIR)/lib/mk/base-sel4-common.inc

View File

@ -1,3 +1,5 @@
LIBS += timeout
vpath vm.cc $(REP_DIR)/src/lib/base/x86
include $(REP_DIR)/lib/mk/base-sel4.inc

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -19,6 +19,7 @@
#include <vm_session/vm_session.h>
#include <trace/source_registry.h>
#include <sel4_native_vcpu/sel4_native_vcpu.h>
namespace Genode { class Vm_session_component; }
@ -31,27 +32,31 @@ class Genode::Vm_session_component
{
private:
class Vcpu : public Genode::List<Vcpu>::Element
class Vcpu : public Rpc_object<Vm_session::Native_vcpu, Vcpu>
{
private:
Constrained_ram_allocator &_ram_alloc;
Ram_dataspace_capability _ds_cap;
Cap_sel _notification { 0 };
Vm_session::Vcpu_id _vcpu_id;
Rpc_entrypoint &_ep;
Constrained_ram_allocator &_ram_alloc;
Ram_dataspace_capability const _ds_cap;
Cap_sel _notification { 0 };
void _free_up();
public:
Vcpu(Constrained_ram_allocator &, Cap_quota_guard &, Vcpu_id,
seL4_Untyped);
~Vcpu() { _free_up(); }
Vcpu(Rpc_entrypoint &, Constrained_ram_allocator &,
Cap_quota_guard &, seL4_Untyped);
~Vcpu();
Dataspace_capability ds_cap() const { return _ds_cap; }
bool match(Vcpu_id id) const { return id.id == _vcpu_id.id; }
void signal() const { seL4_Signal(_notification.value()); }
Cap_sel notification_cap() const { return _notification; }
/*******************************
** Native_vcpu RPC interface **
*******************************/
Capability<Dataspace> state() const { return _ds_cap; }
};
typedef Allocator_avl_tpl<Rm_region> Avl_region;
@ -60,8 +65,6 @@ class Genode::Vm_session_component
Constrained_ram_allocator _constrained_md_ram_alloc;
Heap _heap;
Avl_region _map { &_heap };
List<Vcpu> _vcpus { };
unsigned _id_alloc { 0 };
unsigned _pd_id { 0 };
Cap_sel _vm_page_table;
Page_table_registry _page_table_registry { _heap };
@ -75,14 +78,9 @@ class Genode::Vm_session_component
seL4_Untyped _service;
} _notifications { 0, 0 };
Vcpu * _lookup(Vcpu_id const vcpu_id)
{
for (Vcpu * vcpu = _vcpus.first(); vcpu; vcpu = vcpu->next())
if (vcpu->match(vcpu_id)) return vcpu;
return nullptr;
}
Registry<Registered<Vcpu>> _vcpus { };
/* helpers for vm_session_common.cc */
void _attach_vm_memory(Dataspace_component &, addr_t, Attach_attr);
void _detach_vm_memory(addr_t, size_t);
@ -105,24 +103,19 @@ class Genode::Vm_session_component
** Region_map_detach interface **
*********************************/
void detach(Region_map::Local_addr) override;
void unmap_region(addr_t, size_t) override;
/* used on destruction of attached dataspaces */
void detach(Region_map::Local_addr) override; /* vm_session_common.cc */
void unmap_region(addr_t, size_t) override; /* vm_session_common.cc */
/**************************
** Vm session interface **
**************************/
Dataspace_capability _cpu_state(Vcpu_id);
Capability<Native_vcpu> create_vcpu(Thread_capability);
void attach_pic(addr_t) override { /* unused on seL4 */ }
void _exception_handler(Signal_context_capability, Vcpu_id) {}
void _run(Vcpu_id) {}
void _pause(Vcpu_id);
void attach(Dataspace_capability, addr_t, Attach_attr) override;
void attach_pic(addr_t) override {}
void detach(addr_t, size_t) override;
Vcpu_id _create_vcpu(Thread_capability);
Capability<Native_vcpu> _native_vcpu(Vcpu_id) {
return Capability<Native_vcpu>(); }
void attach(Dataspace_capability, addr_t, Attach_attr) override; /* vm_session_common.cc */
void detach(addr_t, size_t) override; /* vm_session_common.cc */
};
#endif /* _CORE__VM_SESSION_COMPONENT_H_ */

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -13,7 +13,7 @@
/* base includes */
#include <util/flex_iterator.h>
#include <cpu/vm_state.h>
#include <cpu/vcpu_state.h>
/* core includes */
#include <core_env.h>
@ -25,6 +25,11 @@
using namespace Genode;
/********************************
** Vm_session_component::Vcpu **
********************************/
void Vm_session_component::Vcpu::_free_up()
{
if (_ds_cap.valid())
@ -40,15 +45,16 @@ void Vm_session_component::Vcpu::_free_up()
}
}
Vm_session_component::Vcpu::Vcpu(Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc,
Vcpu_id const vcpu_id,
seL4_Untyped const service)
Vm_session_component::Vcpu::Vcpu(Rpc_entrypoint &ep,
Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc,
seL4_Untyped const service)
:
_ep(ep),
_ram_alloc(ram_alloc),
_ds_cap (_ram_alloc.alloc(align_addr(sizeof(Genode::Vm_state), 12),
Cache_attribute::CACHED)),
_vcpu_id(vcpu_id)
_ds_cap (_ram_alloc.alloc(align_addr(sizeof(Genode::Vcpu_state), 12),
Cache_attribute::CACHED))
{
try {
/* notification cap */
@ -59,6 +65,8 @@ Vm_session_component::Vcpu::Vcpu(Constrained_ram_allocator &ram_alloc,
platform_specific().core_cnode().sel(),
_notification);
_ep.manage(this);
caps.acknowledge();
} catch (...) {
_free_up();
@ -66,6 +74,18 @@ Vm_session_component::Vcpu::Vcpu(Constrained_ram_allocator &ram_alloc,
}
}
Vm_session_component::Vcpu::~Vcpu()
{
_ep.dissolve(this);
_free_up();
}
/**************************
** Vm_session_component **
**************************/
Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
Resources resources,
Label const &,
@ -153,12 +173,11 @@ try
throw;
}
Vm_session_component::~Vm_session_component()
{
for (;Vcpu * vcpu = _vcpus.first();) {
_vcpus.remove(vcpu);
destroy(_heap, vcpu);
}
_vcpus.for_each([&] (Vcpu &vcpu) {
destroy(_heap, &vcpu); });
/* detach all regions */
while (true) {
@ -177,28 +196,27 @@ Vm_session_component::~Vm_session_component()
Platform_pd::pd_id_alloc().free(_pd_id);
}
Vm_session::Vcpu_id Vm_session_component::_create_vcpu(Thread_capability cap)
{
Vcpu_id ret;
Capability<Vm_session::Native_vcpu> Vm_session_component::create_vcpu(Thread_capability const cap)
{
if (!cap.valid())
return ret;
return { };
Vcpu * vcpu = nullptr;
auto lambda = [&] (Cpu_thread_component *thread) {
if (!thread)
return;
/* allocate vCPU object */
Vcpu * vcpu = nullptr;
/* code to revert partial allocations in case of Out_of_ram/_quota */
auto free_up = [&] () { if (vcpu) destroy(_heap, vcpu); };
try {
vcpu = new (_heap) Vcpu(_constrained_md_ram_alloc,
_cap_quota_guard(),
Vcpu_id{_id_alloc},
_notifications._service);
vcpu = new (_heap) Registered<Vcpu>(_vcpus,
_ep,
_constrained_md_ram_alloc,
_cap_quota_guard(),
_notifications._service);
Platform_thread &pthread = thread->platform_thread();
pthread.setup_vcpu(_vm_page_table, vcpu->notification_cap());
@ -218,32 +236,13 @@ Vm_session::Vcpu_id Vm_session_component::_create_vcpu(Thread_capability cap)
free_up();
return;
}
_vcpus.insert(vcpu);
ret.id = _id_alloc++;
};
_ep.apply(cap, lambda);
return ret;
return vcpu ? vcpu->cap() : Capability<Vm_session::Native_vcpu> {};
}
Dataspace_capability Vm_session_component::_cpu_state(Vcpu_id const vcpu_id)
{
Vcpu * vcpu = _lookup(vcpu_id);
if (!vcpu)
return Dataspace_capability();
return vcpu->ds_cap();
}
void Vm_session_component::_pause(Vcpu_id const vcpu_id)
{
Vcpu * vcpu = _lookup(vcpu_id);
if (!vcpu)
return;
vcpu->signal();
}
void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc,
addr_t const guest_phys,
@ -308,6 +307,7 @@ void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc,
}
}
void Vm_session_component::_detach_vm_memory(addr_t guest_phys, size_t size)
{
Flexpage_iterator flex(guest_phys, size, guest_phys, size, 0);

View File

@ -0,0 +1,26 @@
/*
* \brief seL4 vCPU RPC interface
* \author Christian Helmuth
* \author Alexander Böttcher
* \date 2021-01-19
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__SEL4_NATIVE_VCPU__SEL4_NATIVE_VCPU_H_
#define _INCLUDE__SEL4_NATIVE_VCPU__SEL4_NATIVE_VCPU_H_
#include <vm_session/vm_session.h>
#include <dataspace/dataspace.h>
struct Genode::Vm_session::Native_vcpu : Interface
{
GENODE_RPC_INTERFACE();
};
#endif /* _INCLUDE__SEL4_NATIVE_VCPU__SEL4_NATIVE_VCPU_H_ */

View File

@ -5,48 +5,80 @@
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/allocator.h>
#include <base/attached_dataspace.h>
#include <base/env.h>
#include <base/registry.h>
#include <vm_session/client.h>
#include <cpu/vm_state.h>
#include <vm_session/connection.h>
#include <vm_session/handler.h>
#include <cpu/vcpu_state.h>
#include <trace/timestamp.h>
#include <base/internal/native_thread.h>
#include <base/internal/native_utcb.h>
#include <base/internal/stack.h>
/* seL4 includes */
#include <sel4_native_vcpu/sel4_native_vcpu.h>
#include <sel4/sel4.h>
#include <sel4/arch/vmenter.h>
#include <base/internal/stack.h>
using namespace Genode;
struct Vcpu;
using Exit_config = Vm_connection::Exit_config;
static Genode::Registry<Genode::Registered<Vcpu> > vcpus;
struct Sel4_vcpu;
struct Vcpu : Genode::Thread
struct Sel4_native_rpc : Rpc_client<Vm_session::Native_vcpu>, Noncopyable
{
private:
Signal_context_capability &_signal;
Capability<Vm_session::Native_vcpu> _create_vcpu(Vm_connection &vm,
Thread_capability &cap)
{
return vm.with_upgrade([&] () {
return vm.call<Vm_session::Rpc_create_vcpu>(cap); });
}
public:
Sel4_vcpu &vcpu;
Sel4_native_rpc(Vm_connection &vm, Thread_capability &cap,
Sel4_vcpu &vcpu)
:
Rpc_client<Vm_session::Native_vcpu>(_create_vcpu(vm, cap)),
vcpu(vcpu)
{ }
};
/******************************
** seL4 vCPU implementation **
******************************/
struct Sel4_vcpu : Genode::Thread, Noncopyable
{
private:
Vcpu_handler_base &_vcpu_handler;
Vcpu_state _state __attribute__((aligned(0x10))) { };
Semaphore _wake_up { 0 };
Semaphore &_handler_ready;
Allocator &_alloc;
Blockade _startup { };
Vm_session_client::Vcpu_id _id {};
addr_t _state { 0 };
addr_t _recall { 0 };
uint64_t _tsc_offset { 0 };
Constructible<Sel4_native_rpc> _rpc { };
bool _show_error_unsupported_r { true };
bool _show_error_unsupported_tpr { true };
bool _show_error_unsupported_star { true };
@ -101,8 +133,8 @@ struct Vcpu : Genode::Thread
/* get selector to call back a vCPU into VMM */
_recall = _stack->utcb().lock_sel();
Vm_state &state = *reinterpret_cast<Vm_state *>(_state);
state = Vm_state {};
Vcpu_state &state = this->state();
state.discharge();
/* wait for first user resume() */
_wake_up.down();
@ -116,9 +148,9 @@ struct Vcpu : Genode::Thread
state.exit_reason = VMEXIT_STARTUP;
_read_sel4_state(service, state);
Genode::Signal_transmitter(_signal).submit();
Genode::Signal_transmitter(_vcpu_handler.signal_cap()).submit();
_handler_ready.down();
_vcpu_handler.ready_semaphore().down();
_wake_up.down();
State local_state { NONE };
@ -154,20 +186,20 @@ struct Vcpu : Genode::Thread
if (local_state == PAUSE) {
state = Vm_state {};
state.discharge();
state.exit_reason = VMEXIT_RECALL;
_read_sel4_state(service, state);
/* notify VM handler */
Genode::Signal_transmitter(_signal).submit();
Genode::Signal_transmitter(_vcpu_handler.signal_cap()).submit();
/*
* Wait until VM handler is really really done,
* otherwise we lose state.
*/
_handler_ready.down();
_vcpu_handler.ready_semaphore().down();
continue;
}
@ -185,7 +217,7 @@ struct Vcpu : Genode::Thread
seL4_Word badge = 0;
seL4_Word res = seL4_VMEnter(&badge);
state = Vm_state {};
state.discharge();
if (res != SEL4_VMENTER_RESULT_FAULT)
state.exit_reason = VMEXIT_RECALL;
@ -203,13 +235,13 @@ struct Vcpu : Genode::Thread
}
/* notify VM handler */
Genode::Signal_transmitter(_signal).submit();
Genode::Signal_transmitter(_vcpu_handler.signal_cap()).submit();
/*
* Wait until VM handler is really really done,
* otherwise we lose state.
*/
_handler_ready.down();
_vcpu_handler.ready_semaphore().down();
}
}
@ -330,20 +362,20 @@ struct Vcpu : Genode::Thread
uint16_t _convert_ar_16(addr_t value) {
return ((value & 0x1f000) >> 4) | (value & 0xff); }
void _write_sel4_state(seL4_X86_VCPU const service, Vm_state &state)
void _write_sel4_state(seL4_X86_VCPU const service, Vcpu_state &state)
{
if (state.ax.valid()) _recent_gpr.eax = state.ax.value();
if (state.bx.valid()) _recent_gpr.ebx = state.bx.value();
if (state.cx.valid()) _recent_gpr.ecx = state.cx.value();
if (state.dx.valid()) _recent_gpr.edx = state.dx.value();
if (state.si.valid()) _recent_gpr.esi = state.si.value();
if (state.di.valid()) _recent_gpr.edi = state.di.value();
if (state.bp.valid()) _recent_gpr.ebp = state.bp.value();
if (state.ax.charged()) _recent_gpr.eax = state.ax.value();
if (state.bx.charged()) _recent_gpr.ebx = state.bx.value();
if (state.cx.charged()) _recent_gpr.ecx = state.cx.value();
if (state.dx.charged()) _recent_gpr.edx = state.dx.value();
if (state.si.charged()) _recent_gpr.esi = state.si.value();
if (state.di.charged()) _recent_gpr.edi = state.di.value();
if (state.bp.charged()) _recent_gpr.ebp = state.bp.value();
if (state.ax.valid() || state.cx.valid() ||
state.dx.valid() || state.bx.valid() ||
state.bp.valid() || state.di.valid() ||
state.si.valid())
if (state.ax.charged() || state.cx.charged() ||
state.dx.charged() || state.bx.charged() ||
state.bp.charged() || state.di.charged() ||
state.si.charged())
{
seL4_Error res = seL4_X86_VCPU_WriteRegisters(service,
&_recent_gpr);
@ -352,10 +384,10 @@ struct Vcpu : Genode::Thread
(int)res);
}
if (state.r8.valid() || state.r9.valid() ||
state.r10.valid() || state.r11.valid() ||
state.r12.valid() || state.r13.valid() ||
state.r14.valid() || state.r15.valid())
if (state.r8.charged() || state.r9.charged() ||
state.r10.charged() || state.r11.charged() ||
state.r12.charged() || state.r13.charged() ||
state.r14.charged() || state.r15.charged())
{
if (_show_error_unsupported_r)
{
@ -364,7 +396,7 @@ struct Vcpu : Genode::Thread
}
}
if (state.tsc.valid() || state.tsc_offset.valid())
if (state.tsc.charged() || state.tsc_offset.charged())
{
_tsc_offset += state.tsc_offset.value();
/* not supported by seL4 */
@ -374,8 +406,8 @@ struct Vcpu : Genode::Thread
#endif
}
if (state.star.valid() || state.lstar.valid() || state.cstar.valid() ||
state.fmask.valid() || state.kernel_gs_base.valid())
if (state.star.charged() || state.lstar.charged() || state.cstar.charged() ||
state.fmask.charged() || state.kernel_gs_base.charged())
{
if (_show_error_unsupported_star) {
_show_error_unsupported_star = false;
@ -383,7 +415,7 @@ struct Vcpu : Genode::Thread
}
}
if (state.tpr.valid() || state.tpr_threshold.valid())
if (state.tpr.charged() || state.tpr_threshold.charged())
{
if (_show_error_unsupported_tpr)
{
@ -392,26 +424,26 @@ struct Vcpu : Genode::Thread
}
}
if (state.dr7.valid())
if (state.dr7.charged())
_write_vmcs(service, Vmcs::DR7, state.dr7.value());
if (state.cr0.valid()) {
if (state.cr0.charged()) {
_write_vmcs(service, Vmcs::CR0, cr0_set | (~cr0_mask & state.cr0.value()));
_write_vmcs(service, Vmcs::CR0_SHADOW, state.cr0.value());
}
/* not supported on seL4 - state.cr2.valid() */
/* not supported on seL4 - state.cr2.charged() */
if (state.cr3.valid())
if (state.cr3.charged())
_write_vmcs(service, Vmcs::CR3, state.cr3.value());
if (state.cr4.valid()) {
if (state.cr4.charged()) {
_write_vmcs(service, Vmcs::CR4, cr4_set | (~cr4_mask & state.cr4.value()));
_write_vmcs(service, Vmcs::CR4_SHADOW, state.cr4.value());
}
if (state.inj_info.valid()) {
addr_t ctrl_0 = state.ctrl_primary.valid() ?
if (state.inj_info.charged()) {
addr_t ctrl_0 = state.ctrl_primary.charged() ?
state.ctrl_primary.value() :
_read_vmcs(service, Vmcs::CTRL_0);
@ -423,123 +455,123 @@ struct Vcpu : Genode::Thread
else
ctrl_0 &= ~Vmcs::IRQ_WINDOW;
state.ctrl_primary.value(ctrl_0);
state.ctrl_primary.charge(ctrl_0);
}
if (state.inj_error.valid())
if (state.inj_error.charged())
_write_vmcs(service, Vmcs::INTR_ERROR, state.inj_error.value());
if (state.flags.valid())
if (state.flags.charged())
_write_vmcs(service, Vmcs::RFLAGS, state.flags.value());
if (state.sp.valid())
if (state.sp.charged())
_write_vmcs(service, Vmcs::RSP, state.sp.value());
if (state.ip.valid())
if (state.ip.charged())
_write_vmcs(service, Vmcs::RIP, state.ip.value());
if (state.ip_len.valid())
if (state.ip_len.charged())
_write_vmcs(service, Vmcs::ENTRY_INST_LEN, state.ip_len.value());
if (state.efer.valid())
if (state.efer.charged())
_write_vmcs(service, Vmcs::EFER, state.efer.value());
/* state.ctrl_primary.valid() update on vmenter - see above */
/* state.ctrl_primary.charged() update on vmenter - see above */
if (state.ctrl_secondary.valid())
if (state.ctrl_secondary.charged())
_write_vmcs(service, Vmcs::CTRL_1, state.ctrl_secondary.value());
if (state.intr_state.valid())
if (state.intr_state.charged())
_write_vmcs(service, Vmcs::STATE_INTR, state.intr_state.value());
if (state.actv_state.valid())
if (state.actv_state.charged())
_write_vmcs(service, Vmcs::STATE_ACTV, state.actv_state.value());
if (state.cs.valid()) {
if (state.cs.charged()) {
_write_vmcs(service, Vmcs::CS_SEL, state.cs.value().sel);
_write_vmcs(service, Vmcs::CS_LIMIT, state.cs.value().limit);
_write_vmcs(service, Vmcs::CS_AR, _convert_ar(state.cs.value().ar));
_write_vmcs(service, Vmcs::CS_BASE, state.cs.value().base);
}
if (state.ss.valid()) {
if (state.ss.charged()) {
_write_vmcs(service, Vmcs::SS_SEL, state.ss.value().sel);
_write_vmcs(service, Vmcs::SS_LIMIT, state.ss.value().limit);
_write_vmcs(service, Vmcs::SS_AR, _convert_ar(state.ss.value().ar));
_write_vmcs(service, Vmcs::SS_BASE, state.ss.value().base);
}
if (state.es.valid()) {
if (state.es.charged()) {
_write_vmcs(service, Vmcs::ES_SEL, state.es.value().sel);
_write_vmcs(service, Vmcs::ES_LIMIT, state.es.value().limit);
_write_vmcs(service, Vmcs::ES_AR, _convert_ar(state.es.value().ar));
_write_vmcs(service, Vmcs::ES_BASE, state.es.value().base);
}
if (state.ds.valid()) {
if (state.ds.charged()) {
_write_vmcs(service, Vmcs::DS_SEL, state.ds.value().sel);
_write_vmcs(service, Vmcs::DS_LIMIT, state.ds.value().limit);
_write_vmcs(service, Vmcs::DS_AR, _convert_ar(state.ds.value().ar));
_write_vmcs(service, Vmcs::DS_BASE, state.ds.value().base);
}
if (state.fs.valid()) {
if (state.fs.charged()) {
_write_vmcs(service, Vmcs::FS_SEL, state.fs.value().sel);
_write_vmcs(service, Vmcs::FS_LIMIT, state.fs.value().limit);
_write_vmcs(service, Vmcs::FS_AR, _convert_ar(state.fs.value().ar));
_write_vmcs(service, Vmcs::FS_BASE, state.fs.value().base);
}
if (state.gs.valid()) {
if (state.gs.charged()) {
_write_vmcs(service, Vmcs::GS_SEL, state.gs.value().sel);
_write_vmcs(service, Vmcs::GS_LIMIT, state.gs.value().limit);
_write_vmcs(service, Vmcs::GS_AR, _convert_ar(state.gs.value().ar));
_write_vmcs(service, Vmcs::GS_BASE, state.gs.value().base);
}
if (state.tr.valid()) {
if (state.tr.charged()) {
_write_vmcs(service, Vmcs::TR_SEL, state.tr.value().sel);
_write_vmcs(service, Vmcs::TR_LIMIT, state.tr.value().limit);
_write_vmcs(service, Vmcs::TR_AR, _convert_ar(state.tr.value().ar));
_write_vmcs(service, Vmcs::TR_BASE, state.tr.value().base);
}
if (state.ldtr.valid()) {
if (state.ldtr.charged()) {
_write_vmcs(service, Vmcs::LDTR_SEL, state.ldtr.value().sel);
_write_vmcs(service, Vmcs::LDTR_LIMIT, state.ldtr.value().limit);
_write_vmcs(service, Vmcs::LDTR_AR, _convert_ar(state.ldtr.value().ar));
_write_vmcs(service, Vmcs::LDTR_BASE, state.ldtr.value().base);
}
if (state.idtr.valid()) {
if (state.idtr.charged()) {
_write_vmcs(service, Vmcs::IDTR_BASE, state.idtr.value().base);
_write_vmcs(service, Vmcs::IDTR_LIMIT, state.idtr.value().limit);
}
if (state.gdtr.valid()) {
if (state.gdtr.charged()) {
_write_vmcs(service, Vmcs::GDTR_BASE, state.gdtr.value().base);
_write_vmcs(service, Vmcs::GDTR_LIMIT, state.gdtr.value().limit);
}
if (state.pdpte_0.valid())
if (state.pdpte_0.charged())
_write_vmcs(service, Vmcs::PDPTE_0, state.pdpte_0.value());
if (state.pdpte_1.valid())
if (state.pdpte_1.charged())
_write_vmcs(service, Vmcs::PDPTE_1, state.pdpte_1.value());
if (state.pdpte_2.valid())
if (state.pdpte_2.charged())
_write_vmcs(service, Vmcs::PDPTE_2, state.pdpte_2.value());
if (state.pdpte_3.valid())
if (state.pdpte_3.charged())
_write_vmcs(service, Vmcs::PDPTE_3, state.pdpte_3.value());
if (state.sysenter_cs.valid())
if (state.sysenter_cs.charged())
_write_vmcs(service, Vmcs::SYSENTER_CS, state.sysenter_cs.value());
if (state.sysenter_sp.valid())
if (state.sysenter_sp.charged())
_write_vmcs(service, Vmcs::SYSENTER_SP, state.sysenter_sp.value());
if (state.sysenter_ip.valid())
if (state.sysenter_ip.charged())
_write_vmcs(service, Vmcs::SYSENTER_IP, state.sysenter_ip.value());
}
@ -568,26 +600,26 @@ struct Vcpu : Genode::Thread
uint32_t _read_vmcs_32(seL4_X86_VCPU const service, enum Vmcs const field) {
return _read_vmcsX<uint32_t>(service, field); }
void _read_sel4_state(seL4_X86_VCPU const service, Vm_state &state)
void _read_sel4_state(seL4_X86_VCPU const service, Vcpu_state &state)
{
state.ip.value(seL4_GetMR(SEL4_VMENTER_CALL_EIP_MR));
state.ctrl_primary.value(seL4_GetMR(SEL4_VMENTER_CALL_CONTROL_PPC_MR));
state.ip.charge(seL4_GetMR(SEL4_VMENTER_CALL_EIP_MR));
state.ctrl_primary.charge(seL4_GetMR(SEL4_VMENTER_CALL_CONTROL_PPC_MR));
state.ip_len.value(seL4_GetMR(SEL4_VMENTER_FAULT_INSTRUCTION_LEN_MR));
state.qual_primary.value(seL4_GetMR(SEL4_VMENTER_FAULT_QUALIFICATION_MR));
state.qual_secondary.value(seL4_GetMR(SEL4_VMENTER_FAULT_GUEST_PHYSICAL_MR));
state.ip_len.charge(seL4_GetMR(SEL4_VMENTER_FAULT_INSTRUCTION_LEN_MR));
state.qual_primary.charge(seL4_GetMR(SEL4_VMENTER_FAULT_QUALIFICATION_MR));
state.qual_secondary.charge(seL4_GetMR(SEL4_VMENTER_FAULT_GUEST_PHYSICAL_MR));
state.flags.value(seL4_GetMR(SEL4_VMENTER_FAULT_RFLAGS_MR));
state.intr_state.value(seL4_GetMR(SEL4_VMENTER_FAULT_GUEST_INT_MR));
state.cr3.value(seL4_GetMR(SEL4_VMENTER_FAULT_CR3_MR));
state.flags.charge(seL4_GetMR(SEL4_VMENTER_FAULT_RFLAGS_MR));
state.intr_state.charge(seL4_GetMR(SEL4_VMENTER_FAULT_GUEST_INT_MR));
state.cr3.charge(seL4_GetMR(SEL4_VMENTER_FAULT_CR3_MR));
state.ax.value(seL4_GetMR(SEL4_VMENTER_FAULT_EAX));
state.bx.value(seL4_GetMR(SEL4_VMENTER_FAULT_EBX));
state.cx.value(seL4_GetMR(SEL4_VMENTER_FAULT_ECX));
state.dx.value(seL4_GetMR(SEL4_VMENTER_FAULT_EDX));
state.si.value(seL4_GetMR(SEL4_VMENTER_FAULT_ESI));
state.di.value(seL4_GetMR(SEL4_VMENTER_FAULT_EDI));
state.bp.value(seL4_GetMR(SEL4_VMENTER_FAULT_EBP));
state.ax.charge(seL4_GetMR(SEL4_VMENTER_FAULT_EAX));
state.bx.charge(seL4_GetMR(SEL4_VMENTER_FAULT_EBX));
state.cx.charge(seL4_GetMR(SEL4_VMENTER_FAULT_ECX));
state.dx.charge(seL4_GetMR(SEL4_VMENTER_FAULT_EDX));
state.si.charge(seL4_GetMR(SEL4_VMENTER_FAULT_ESI));
state.di.charge(seL4_GetMR(SEL4_VMENTER_FAULT_EDI));
state.bp.charge(seL4_GetMR(SEL4_VMENTER_FAULT_EBP));
_recent_gpr.eax = state.ax.value();
_recent_gpr.ebx = state.bx.value();
@ -597,144 +629,141 @@ struct Vcpu : Genode::Thread
_recent_gpr.edi = state.di.value();
_recent_gpr.ebp = state.bp.value();
state.sp.value(_read_vmcs(service, Vmcs::RSP));
state.dr7.value(_read_vmcs(service, Vmcs::DR7));
state.sp.charge(_read_vmcs(service, Vmcs::RSP));
state.dr7.charge(_read_vmcs(service, Vmcs::DR7));
/* r8 - r15 not supported on seL4 */
{
addr_t const cr0 = _read_vmcs(service, Vmcs::CR0);
addr_t const cr0_shadow = _read_vmcs(service, Vmcs::CR0_SHADOW);
state.cr0.value((cr0 & ~cr0_mask) | (cr0_shadow & cr0_mask));
state.cr0.charge((cr0 & ~cr0_mask) | (cr0_shadow & cr0_mask));
if (state.cr0.value() != cr0_shadow)
_write_vmcs(service, Vmcs::CR0_SHADOW, state.cr0.value());
}
/* cr2 not supported on seL4 */
state.cr2.value(state.cr2.value());
state.cr2.charge(state.cr2.value());
{
addr_t const cr4 = _read_vmcs(service, Vmcs::CR4);
addr_t const cr4_shadow = _read_vmcs(service, Vmcs::CR4_SHADOW);
state.cr4.value((cr4 & ~cr4_mask) | (cr4_shadow & cr4_mask));
state.cr4.charge((cr4 & ~cr4_mask) | (cr4_shadow & cr4_mask));
if (state.cr4.value() != cr4_shadow)
_write_vmcs(service, Vmcs::CR4_SHADOW, state.cr4.value());
}
typedef Genode::Vm_state::Segment Segment;
typedef Genode::Vm_state::Range Range;
typedef Genode::Vcpu_state::Segment Segment;
typedef Genode::Vcpu_state::Range Range;
state.cs.value(Segment{_read_vmcs_16(service, Vmcs::CS_SEL),
state.cs.charge(Segment{_read_vmcs_16(service, Vmcs::CS_SEL),
_convert_ar_16(_read_vmcs(service, Vmcs::CS_AR)),
_read_vmcs_32(service, Vmcs::CS_LIMIT),
_read_vmcs(service, Vmcs::CS_BASE)});
state.ss.value(Segment{_read_vmcs_16(service, Vmcs::SS_SEL),
state.ss.charge(Segment{_read_vmcs_16(service, Vmcs::SS_SEL),
_convert_ar_16(_read_vmcs(service, Vmcs::SS_AR)),
_read_vmcs_32(service, Vmcs::SS_LIMIT),
_read_vmcs(service, Vmcs::SS_BASE)});
state.es.value(Segment{_read_vmcs_16(service, Vmcs::ES_SEL),
state.es.charge(Segment{_read_vmcs_16(service, Vmcs::ES_SEL),
_convert_ar_16(_read_vmcs(service, Vmcs::ES_AR)),
_read_vmcs_32(service, Vmcs::ES_LIMIT),
_read_vmcs(service, Vmcs::ES_BASE)});
state.ds.value(Segment{_read_vmcs_16(service, Vmcs::DS_SEL),
state.ds.charge(Segment{_read_vmcs_16(service, Vmcs::DS_SEL),
_convert_ar_16(_read_vmcs(service, Vmcs::DS_AR)),
_read_vmcs_32(service, Vmcs::DS_LIMIT),
_read_vmcs(service, Vmcs::DS_BASE)});
state.fs.value(Segment{_read_vmcs_16(service, Vmcs::FS_SEL),
state.fs.charge(Segment{_read_vmcs_16(service, Vmcs::FS_SEL),
_convert_ar_16(_read_vmcs(service, Vmcs::FS_AR)),
_read_vmcs_32(service, Vmcs::FS_LIMIT),
_read_vmcs(service, Vmcs::FS_BASE)});
state.gs.value(Segment{_read_vmcs_16(service, Vmcs::GS_SEL),
state.gs.charge(Segment{_read_vmcs_16(service, Vmcs::GS_SEL),
_convert_ar_16(_read_vmcs(service, Vmcs::GS_AR)),
_read_vmcs_32(service, Vmcs::GS_LIMIT),
_read_vmcs(service, Vmcs::GS_BASE)});
state.tr.value(Segment{_read_vmcs_16(service, Vmcs::TR_SEL),
state.tr.charge(Segment{_read_vmcs_16(service, Vmcs::TR_SEL),
_convert_ar_16(_read_vmcs(service, Vmcs::TR_AR)),
_read_vmcs_32(service, Vmcs::TR_LIMIT),
_read_vmcs(service, Vmcs::TR_BASE)});
state.ldtr.value(Segment{_read_vmcs_16(service, Vmcs::LDTR_SEL),
state.ldtr.charge(Segment{_read_vmcs_16(service, Vmcs::LDTR_SEL),
_convert_ar_16(_read_vmcs(service, Vmcs::LDTR_AR)),
_read_vmcs_32(service, Vmcs::LDTR_LIMIT),
_read_vmcs(service, Vmcs::LDTR_BASE)});
state.idtr.value(Range{_read_vmcs(service, Vmcs::IDTR_BASE),
_read_vmcs_32(service, Vmcs::IDTR_LIMIT)});
state.idtr.charge(Range{ .limit = _read_vmcs_32(service, Vmcs::IDTR_LIMIT),
.base = _read_vmcs(service, Vmcs::IDTR_BASE) });
state.gdtr.value(Range{_read_vmcs(service, Vmcs::GDTR_BASE),
_read_vmcs_32(service, Vmcs::GDTR_LIMIT)});
state.gdtr.charge(Range{ .limit = _read_vmcs_32(service, Vmcs::GDTR_LIMIT),
.base = _read_vmcs(service, Vmcs::GDTR_BASE) });
state.sysenter_cs.value(_read_vmcs(service, Vmcs::SYSENTER_CS));
state.sysenter_sp.value(_read_vmcs(service, Vmcs::SYSENTER_SP));
state.sysenter_ip.value(_read_vmcs(service, Vmcs::SYSENTER_IP));
state.sysenter_cs.charge(_read_vmcs(service, Vmcs::SYSENTER_CS));
state.sysenter_sp.charge(_read_vmcs(service, Vmcs::SYSENTER_SP));
state.sysenter_ip.charge(_read_vmcs(service, Vmcs::SYSENTER_IP));
/* no support by seL4 to read this value */
state.ctrl_secondary.value(state.ctrl_secondary.value());
//state.ctrl_secondary.value(_read_vmcs(service, Vmcs::CTRL_1));
state.ctrl_secondary.charge(state.ctrl_secondary.value());
//state.ctrl_secondary.charge(_read_vmcs(service, Vmcs::CTRL_1));
if (state.exit_reason == VMEXIT_INVALID ||
state.exit_reason == VMEXIT_RECALL)
{
state.inj_info.value(_read_vmcs(service, Vmcs::INTR_INFO));
state.inj_error.value(_read_vmcs(service, Vmcs::INTR_ERROR));
state.inj_info.charge(_read_vmcs(service, Vmcs::INTR_INFO));
state.inj_error.charge(_read_vmcs(service, Vmcs::INTR_ERROR));
} else {
state.inj_info.value(_read_vmcs(service, Vmcs::IDT_INFO));
state.inj_error.value(_read_vmcs(service, Vmcs::IDT_ERROR));
state.inj_info.charge(_read_vmcs(service, Vmcs::IDT_INFO));
state.inj_error.charge(_read_vmcs(service, Vmcs::IDT_ERROR));
}
state.intr_state.value(_read_vmcs(service, Vmcs::STATE_INTR));
state.actv_state.value(_read_vmcs(service, Vmcs::STATE_ACTV));
state.intr_state.charge(_read_vmcs(service, Vmcs::STATE_INTR));
state.actv_state.charge(_read_vmcs(service, Vmcs::STATE_ACTV));
state.pdpte_0.value(_read_vmcs(service, Vmcs::PDPTE_0));
state.pdpte_1.value(_read_vmcs(service, Vmcs::PDPTE_1));
state.pdpte_2.value(_read_vmcs(service, Vmcs::PDPTE_2));
state.pdpte_3.value(_read_vmcs(service, Vmcs::PDPTE_3));
state.pdpte_0.charge(_read_vmcs(service, Vmcs::PDPTE_0));
state.pdpte_1.charge(_read_vmcs(service, Vmcs::PDPTE_1));
state.pdpte_2.charge(_read_vmcs(service, Vmcs::PDPTE_2));
state.pdpte_3.charge(_read_vmcs(service, Vmcs::PDPTE_3));
/* tsc and tsc_offset not supported by seL4 */
state.tsc.value(Trace::timestamp());
state.tsc_offset.value(_tsc_offset);
state.tsc.charge(Trace::timestamp());
state.tsc_offset.charge(_tsc_offset);
state.efer.value(_read_vmcs(service, Vmcs::EFER));
state.efer.charge(_read_vmcs(service, Vmcs::EFER));
/* XXX star, lstar, cstar, fmask, kernel_gs_base not supported by seL4 */
/* XXX tpr and tpr_threshold not supported by seL4 */
}
public:
Vcpu(Genode::Env &env, Genode::Signal_context_capability &cap,
Semaphore &handler_ready, Allocator &alloc,
Affinity::Location &location)
:
Thread(env, "vcpu_thread", STACK_SIZE, location, Weight(), env.cpu()),
_signal(cap),
_handler_ready(handler_ready), _alloc(alloc)
{ }
Allocator &allocator() { return _alloc; }
void start() override {
Thread::start();
_startup.block();
Affinity::Location _location(Vcpu_handler_base &handler) const
{
Thread * ep = reinterpret_cast<Thread *>(&handler.rpc_ep());
return ep->affinity();
}
Genode::Vm_session_client::Vcpu_id id() const { return _id; }
void id(Genode::Vm_session_client::Vcpu_id id) { _id = id; }
public:
void assign_ds_state(Region_map &rm, Dataspace_capability cap) {
_state = rm.attach(cap); }
void initial_resume()
Sel4_vcpu(Env &env, Vm_connection &vm,
Vcpu_handler_base &handler, Exit_config const &)
:
Thread(env, "vcpu_thread", STACK_SIZE, _location(handler),
Weight(), env.cpu()),
_vcpu_handler(handler)
{
Thread::start();
/* wait until thread is alive, e.g. Thread::cap() is valid */
_startup.block();
_rpc.construct(vm, this->cap(), *this);
/* signal about finished vCPU assignment */
_wake_up.up();
}
@ -762,72 +791,22 @@ struct Vcpu : Genode::Thread
_wake_up.up();
}
Vcpu_state & state() { return _state; }
Sel4_native_rpc * rpc() { return &*_rpc; }
};
Genode::Vm_session_client::Vcpu_id
Genode::Vm_session_client::create_vcpu(Allocator &alloc, Env &env,
Vm_handler_base &handler)
{
Thread * ep = reinterpret_cast<Thread *>(&handler._rpc_ep);
Affinity::Location location = ep->affinity();
/**************
** vCPU API **
**************/
/* create thread that switches modes between thread/cpu */
Vcpu * vcpu = new (alloc) Genode::Registered<Vcpu> (vcpus, env,
handler._cap,
handler._done,
alloc,
location);
void Vm_connection::Vcpu::run() { static_cast<Sel4_native_rpc &>(_native_vcpu).vcpu.resume(); }
void Vm_connection::Vcpu::pause() { static_cast<Sel4_native_rpc &>(_native_vcpu).vcpu.pause(); }
Vcpu_state & Vm_connection::Vcpu::state() { return static_cast<Sel4_native_rpc &>(_native_vcpu).vcpu.state(); }
try {
/* now it gets actually valid - vcpu->cap() becomes valid */
vcpu->start();
/* instruct core to let it become a vCPU */
vcpu->id(call<Rpc_create_vcpu>(vcpu->cap()));
call<Rpc_exception_handler>(handler._cap, vcpu->id());
vcpu->assign_ds_state(env.rm(), call<Rpc_cpu_state>(vcpu->id()));
} catch (...) {
destroy(alloc, vcpu);
throw;
}
vcpu->initial_resume();
return vcpu->id();
}
void Genode::Vm_session_client::run(Genode::Vm_session_client::Vcpu_id id)
{
vcpus.for_each([&] (Vcpu &vcpu) {
if (vcpu.id().id == id.id)
vcpu.resume();
});
}
void Vm_session_client::pause(Vm_session_client::Vcpu_id vcpu_id)
{
vcpus.for_each([&] (Vcpu &vcpu) {
if (vcpu.id().id != vcpu_id.id)
return;
vcpu.pause();
});
}
Genode::Dataspace_capability Genode::Vm_session_client::cpu_state(Vcpu_id vcpu_id)
{
Dataspace_capability cap;
cap = call<Rpc_cpu_state>(vcpu_id);
return cap;
}
Vm_session::~Vm_session()
{
vcpus.for_each([&] (Vcpu &vc) {
Allocator &alloc = vc.allocator();
destroy(alloc, &vc);
});
}
Vm_connection::Vcpu::Vcpu(Vm_connection &vm, Allocator &alloc,
Vcpu_handler_base &handler, Exit_config const &exit_config)
:
_native_vcpu(*((new (alloc) Sel4_vcpu(vm._env, vm, handler, exit_config))->rpc()))
{ }

View File

@ -0,0 +1,231 @@
/*
* \brief Virtual CPU context for x86
* \author Alexander Boettcher
* \author Christian Helmuth
* \date 2018-10-09
*/
/*
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__SPEC__X86__CPU__VM_STATE_H_
#define _INCLUDE__SPEC__X86__CPU__VM_STATE_H_
#include <base/stdint.h>
#include <util/noncopyable.h>
namespace Genode { struct Vcpu_state; }
/*
* The state of one virtual CPU (vCPU) as available via the VM session for x86
*
* The state object is designed for bidirectional transfer of register state,
* which means it reflects vCPU state on VM exits but also supports loading
* updating register state on VM entry. Therefore, each register contains not
* only the actual register value but also a 'charged' state.
*
* The hypervisor charges registers as requested by the VMM on VM exit with the
* current virtual CPU state. The VMM for its part charges registers it intends
* to update with new values before VM entry (e.g., after I/O emulation). Both
* parties are required to 'discharge()' the vCPU state explicitly if registers
* charged by the other party should not be considered on return. The common
* case is to discharge all registers, charge some updates and transfer
* execution to the other party.
*/
class Genode::Vcpu_state
{
private:
Vcpu_state & operator = (Vcpu_state const &) = default;
Vcpu_state(Vcpu_state const&) = delete;
public:
template <typename T>
class Register : Noncopyable
{
private:
friend class Vcpu_state;
T _value { };
bool _charged { false };
/*
* Trick used by Vcpu_state::discharge() to discharge all
* registers at once. Note, the register value is kept intact.
*/
Register & operator = (Register const &)
{
_charged = false;
return *this;
}
public:
bool charged() const { return _charged; }
T value() const { return _value; }
void charge(T const &value)
{
_charged = true;
_value = value;
}
};
struct Range
{
uint32_t limit;
addr_t base;
};
struct Segment
{
uint16_t sel, ar;
uint32_t limit;
addr_t base;
};
Register<addr_t> ax;
Register<addr_t> cx;
Register<addr_t> dx;
Register<addr_t> bx;
Register<addr_t> bp;
Register<addr_t> si;
Register<addr_t> di;
Register<addr_t> sp;
Register<addr_t> ip;
Register<addr_t> ip_len;
Register<addr_t> flags;
Register<Segment> es;
Register<Segment> ds;
Register<Segment> fs;
Register<Segment> gs;
Register<Segment> cs;
Register<Segment> ss;
Register<Segment> tr;
Register<Segment> ldtr;
Register<Range> gdtr;
Register<Range> idtr;
Register<addr_t> cr0;
Register<addr_t> cr2;
Register<addr_t> cr3;
Register<addr_t> cr4;
Register<addr_t> dr7;
Register<addr_t> sysenter_ip;
Register<addr_t> sysenter_sp;
Register<addr_t> sysenter_cs;
Register<uint64_t> qual_primary;
Register<uint64_t> qual_secondary;
Register<uint32_t> ctrl_primary;
Register<uint32_t> ctrl_secondary;
Register<uint32_t> inj_info;
Register<uint32_t> inj_error;
Register<uint32_t> intr_state;
Register<uint32_t> actv_state;
Register<uint64_t> tsc;
Register<uint64_t> tsc_offset;
Register<addr_t> efer;
Register<uint64_t> pdpte_0;
Register<uint64_t> pdpte_1;
Register<uint64_t> pdpte_2;
Register<uint64_t> pdpte_3;
Register<uint64_t> r8;
Register<uint64_t> r9;
Register<uint64_t> r10;
Register<uint64_t> r11;
Register<uint64_t> r12;
Register<uint64_t> r13;
Register<uint64_t> r14;
Register<uint64_t> r15;
Register<uint64_t> star;
Register<uint64_t> lstar;
Register<uint64_t> cstar;
Register<uint64_t> fmask;
Register<uint64_t> kernel_gs_base;
Register<uint32_t> tpr;
Register<uint32_t> tpr_threshold;
unsigned exit_reason;
class Fpu : Noncopyable
{
public:
struct State
{
uint8_t _buffer[512] { };
} __attribute__((aligned(16)));
private:
friend class Vcpu_state;
State _state { };
bool _charged { false };
/* see comment for Register::operator=() */
Fpu & operator = (Fpu const &)
{
_charged = false;
return *this;
}
public:
bool charged() const { return _charged; }
template <typename FN>
void with_state(FN const &fn) const
{
fn(_state);
}
template <typename FN>
void charge(FN const &fn)
{
_charged = true;
fn(_state);
}
};
Fpu fpu __attribute__((aligned(16)));
/*
* Registers transfered by hypervisor from guest on VM exit are charged.
* Discharged registers are not loaded into guest on VM entry.
*/
void discharge()
{
/* invoke operator= for all registers with all charged flags reset */
*this = Vcpu_state { };
}
};
#endif /* _INCLUDE__SPEC__X86__CPU__VCPU_STATE_H_ */

View File

@ -1,171 +0,0 @@
/*
* \brief CPU context of a virtual machine for x86
* \author Alexander Boettcher
* \date 2018-10-09
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__SPEC__X86__CPU__VM_STATE_H_
#define _INCLUDE__SPEC__X86__CPU__VM_STATE_H_
#include <base/stdint.h>
namespace Genode
{
struct Vm_state;
}
struct Genode::Vm_state
{
template <typename T>
class Register
{
private:
bool _valid;
T _value { };
public:
Register() : _valid(false) { }
Register(T value) : _valid(true), _value(value) { }
T value() const { return _value; }
void value(T value) { _value = value; _valid = true; }
bool valid() const { return _valid; }
void invalid() { _valid = false; }
Register &operator = (Register const &other)
{
_valid = other._valid;
/* keep original _value if other._valid is not valid */
if (_valid)
_value = other._value;
return *this;
}
};
struct Range {
addr_t base;
uint32_t limit;
};
struct Segment {
uint16_t sel, ar;
uint32_t limit;
addr_t base;
};
Register<addr_t> ax;
Register<addr_t> cx;
Register<addr_t> dx;
Register<addr_t> bx;
Register<addr_t> bp;
Register<addr_t> si;
Register<addr_t> di;
Register<addr_t> sp;
Register<addr_t> ip;
Register<addr_t> ip_len;
Register<addr_t> flags;
Register<Segment> es;
Register<Segment> ds;
Register<Segment> fs;
Register<Segment> gs;
Register<Segment> cs;
Register<Segment> ss;
Register<Segment> tr;
Register<Segment> ldtr;
Register<Range> gdtr;
Register<Range> idtr;
Register<addr_t> cr0;
Register<addr_t> cr2;
Register<addr_t> cr3;
Register<addr_t> cr4;
Register<addr_t> dr7;
Register<addr_t> sysenter_ip;
Register<addr_t> sysenter_sp;
Register<addr_t> sysenter_cs;
Register<uint64_t> qual_primary;
Register<uint64_t> qual_secondary;
Register<uint32_t> ctrl_primary;
Register<uint32_t> ctrl_secondary;
Register<uint32_t> inj_info;
Register<uint32_t> inj_error;
Register<uint32_t> intr_state;
Register<uint32_t> actv_state;
Register<uint64_t> tsc;
Register<uint64_t> tsc_offset;
Register<addr_t> efer;
Register<uint64_t> pdpte_0;
Register<uint64_t> pdpte_1;
Register<uint64_t> pdpte_2;
Register<uint64_t> pdpte_3;
Register<uint64_t> r8;
Register<uint64_t> r9;
Register<uint64_t> r10;
Register<uint64_t> r11;
Register<uint64_t> r12;
Register<uint64_t> r13;
Register<uint64_t> r14;
Register<uint64_t> r15;
Register<uint64_t> star;
Register<uint64_t> lstar;
Register<uint64_t> cstar;
Register<uint64_t> fmask;
Register<uint64_t> kernel_gs_base;
Register<uint32_t> tpr;
Register<uint32_t> tpr_threshold;
unsigned exit_reason;
class Fpu {
private :
uint8_t _value[512] { };
bool _valid { false };
public:
bool valid() const { return _valid; }
void invalid() { _valid = false; }
template <typename FUNC>
void value(FUNC const &fn) {
_valid = true;
fn(_value, sizeof(_value));
};
Fpu &operator = (Fpu const &)
{
_valid = false;
return *this;
}
} fpu __attribute__((aligned(16)));
};
#endif /* _INCLUDE__SPEC__X86__CPU__VM_STATE_H_ */

View File

@ -1,23 +0,0 @@
/*
* \brief VM-session capability type
* \author Stefan Kalkowski
* \date 2012-10-02
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__VM_SESSION__CAPABILITY_H_
#define _INCLUDE__VM_SESSION__CAPABILITY_H_
/* Genode includes */
#include <base/capability.h>
#include <vm_session/vm_session.h>
namespace Genode { typedef Capability<Vm_session> Vm_session_capability; }
#endif /* _INCLUDE__VM_SESSION__CAPABILITY_H_ */

View File

@ -1,63 +0,0 @@
/*
* \brief Client-side VM session interface
* \author Stefan Kalkowski
* \date 2012-10-02
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__VM_SESSION__CLIENT_H_
#define _INCLUDE__VM_SESSION__CLIENT_H_
/* Genode includes */
#include <vm_session/capability.h>
#include <vm_session/handler.h>
#include <base/rpc_client.h>
namespace Genode { struct Vm_session_client; class Allocator; class Vm_state; }
/**
* Client-side VM session interface
*/
struct Genode::Vm_session_client : Rpc_client<Vm_session>
{
/**
* Constructor
*/
explicit Vm_session_client(Vm_session_capability session)
: Rpc_client<Vm_session>(session) { }
/**************************
** Vm_session interface **
**************************/
Dataspace_capability cpu_state(Vcpu_id);
void run(Vcpu_id);
void pause(Vcpu_id);
void attach(Dataspace_capability ds, addr_t vm_addr,
Attach_attr attr = { .offset = 0,
.size = 0,
.executable = true,
.writeable = true } ) override
{
call<Rpc_attach>(ds, vm_addr, attr);
}
void detach(addr_t vm_addr, size_t size) override {
call<Rpc_detach>(vm_addr, size); }
void attach_pic(addr_t vm_addr) override {
call<Rpc_attach_pic>(vm_addr); }
Vcpu_id create_vcpu(Allocator &, Env &, Vm_handler_base &);
};
#endif /* _INCLUDE__VM_SESSION__CLIENT_H_ */

View File

@ -1,11 +1,15 @@
/*
* \brief Connection to VM a service
* \brief Connection to a VM service
* \author Stefan Kalkowski
* \author Christian Helmuth
* \date 2012-10-02
*
* The VM connection is the API for VM and vCPU handling and implemented
* for each platform specifically.
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
* Copyright (C) 2012-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -14,15 +18,22 @@
#ifndef _INCLUDE__VM_SESSION__CONNECTION_H_
#define _INCLUDE__VM_SESSION__CONNECTION_H_
#include <util/retry.h>
#include <vm_session/client.h>
#include <cpu_session/cpu_session.h>
#include <base/connection.h>
#include <base/rpc_client.h>
#include <vm_session/vm_session.h>
#include <cpu_session/cpu_session.h>
#include <util/retry.h>
#include <util/noncopyable.h>
namespace Genode { struct Vm_connection; }
namespace Genode {
struct Vm_connection;
struct Vcpu_handler_base;
struct Vcpu_state;
struct Allocator;
}
struct Genode::Vm_connection : Connection<Vm_session>, Vm_session_client
struct Genode::Vm_connection : Connection<Vm_session>, Rpc_client<Vm_session>
{
/**
* Issue session request
@ -37,6 +48,37 @@ struct Genode::Vm_connection : Connection<Vm_session>, Vm_session_client
priority, affinity, label);
}
/*
* VM-Exit state-transfer configuration
*
* Per default all Vcpu_state is transfered on each exit reason. The exit
* config enables omission of some state transfers on specific exit
* reasons.
*/
struct Exit_config
{
/* for example OMIT_FPU_ON_IRQ */
};
/**
* Virtual CPU
*
* A vCPU can be created only from a Vm_connection and is, thus, bounded to
* a VM until destroyed.
*/
struct Vcpu : Genode::Noncopyable
{
Native_vcpu &_native_vcpu;
Vcpu(Vm_connection &, Allocator &, Vcpu_handler_base &, Exit_config const &);
void run();
void pause();
Vcpu_state & state();
};
friend class Vcpu;
/**
* Constructor
*
@ -49,7 +91,7 @@ struct Genode::Vm_connection : Connection<Vm_session>, Vm_session_client
unsigned long affinity = 0)
:
Connection<Vm_session>(env, _session(env.parent(), label, priority, affinity)),
Vm_session_client(cap())
Rpc_client<Vm_session>(cap())
{ }
template <typename FUNC>
@ -64,6 +106,22 @@ struct Genode::Vm_connection : Connection<Vm_session>, Vm_session_client
[&] () { this->upgrade_ram(4096); }
);
}
/**************************
** Vm_session interface **
**************************/
void attach(Dataspace_capability ds, addr_t vm_addr, Attach_attr attr) override
{
with_upgrade([&] () {
call<Rpc_attach>(ds, vm_addr, attr); });
}
void detach(addr_t vm_addr, size_t size) override {
call<Rpc_detach>(vm_addr, size); }
void attach_pic(addr_t vm_addr) override {
call<Rpc_attach_pic>(vm_addr); }
};
#endif /* _INCLUDE__VM_SESSION__CONNECTION_H_ */

View File

@ -1,11 +1,12 @@
/*
* \brief Client-side VM session exception handler
* \brief Client-side VM session vCPU exception handler
* \author Alexander Boettcher
* \author Christian Helmuth
* \date 2018-09-29
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -17,44 +18,43 @@
#include <base/signal.h>
namespace Genode {
class Vm_state;
class Vm_handler_base;
template <typename, typename> class Vm_handler;
class Vcpu_state;
class Vcpu_handler_base;
template <typename, typename> class Vcpu_handler;
}
class Genode::Vm_handler_base : public Signal_dispatcher_base
class Genode::Vcpu_handler_base : public Signal_dispatcher_base
{
friend class Vm_session_client;
protected:
Rpc_entrypoint &_rpc_ep;
Signal_context_capability _cap {};
Genode::Semaphore _done { 0 };
Signal_context_capability _signal_cap { };
Genode::Semaphore _ready_semaphore { 0 };
public:
virtual bool config_vm_event(Genode::Vm_state &, unsigned) = 0;
Vm_handler_base(Rpc_entrypoint &rpc)
Vcpu_handler_base(Rpc_entrypoint &rpc)
: _rpc_ep(rpc) { }
Rpc_entrypoint & rpc_ep() { return _rpc_ep; }
Signal_context_capability signal_cap() { return _signal_cap; }
Genode::Semaphore & ready_semaphore() { return _ready_semaphore; }
};
template <typename T, typename EP = Genode::Entrypoint>
class Genode::Vm_handler : public Vm_handler_base
class Genode::Vcpu_handler : public Vcpu_handler_base
{
private:
EP &_ep;
T &_obj;
void (T::*_member) ();
void (T::*_config) (Genode::Vm_state &, unsigned const);
/*
* Noncopyable
*/
Vm_handler(Vm_handler const &);
Vm_handler &operator = (Vm_handler const &);
Vcpu_handler(Vcpu_handler const &);
Vcpu_handler &operator = (Vcpu_handler const &);
public:
@ -64,19 +64,17 @@ class Genode::Vm_handler : public Vm_handler_base
* \param obj,member object and method to call when
* the vm exception occurs
*/
Vm_handler(EP &ep, T &obj, void (T::*member)(),
void (T::*config)(Genode::Vm_state&, unsigned) = nullptr)
Vcpu_handler(EP &ep, T &obj, void (T::*member)())
:
Vm_handler_base(ep.rpc_ep()),
Vcpu_handler_base(ep.rpc_ep()),
_ep(ep),
_obj(obj),
_member(member),
_config(config)
_member(member)
{
_cap = ep.manage(*this);
_signal_cap = _ep.manage(*this);
}
~Vm_handler() { _ep.dissolve(*this); }
~Vcpu_handler() { _ep.dissolve(*this); }
/**
* Interface of Signal_dispatcher_base
@ -84,20 +82,10 @@ class Genode::Vm_handler : public Vm_handler_base
void dispatch(unsigned) override
{
(_obj.*_member)();
_done.up();
_ready_semaphore.up();
}
bool config_vm_event(Genode::Vm_state &state,
unsigned const vm_event) override
{
if (!_config)
return false;
(_obj.*_config)(state, vm_event);
return true;
}
operator Capability<Signal_context>() const { return _cap; }
operator Capability<Signal_context>() const { return _signal_cap; }
};
#endif /* _INCLUDE__VM_SESSION__HANDLER_H_ */

View File

@ -1,11 +1,13 @@
/*
* \brief VM-session interface
* \author Stefan Kalkowski
* \author Alexander Böttcher
* \author Christian Helmuth
* \date 2012-10-02
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
* Copyright (C) 2012-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -27,12 +29,6 @@ struct Genode::Vm_session : Session
{
static const char *service_name() { return "VM"; }
struct Vcpu_id
{
enum { INVALID = ~0U };
unsigned id { INVALID };
};
struct Attach_attr
{
addr_t offset;
@ -46,11 +42,6 @@ struct Genode::Vm_session : Session
class Invalid_dataspace : Exception { };
class Region_conflict : Exception { };
/**
* Destructor
*/
virtual ~Vm_session();
/**
* Attach dataspace to the guest-physical memory address space
*
@ -78,7 +69,6 @@ struct Genode::Vm_session : Session
*/
virtual void attach_pic(addr_t vm_addr) = 0;
/*****************************************
** Access to kernel-specific interface **
*****************************************/
@ -86,32 +76,30 @@ struct Genode::Vm_session : Session
/**
* Common base class of kernel-specific CPU interfaces
*/
struct Native_vcpu : Interface { };
struct Native_vcpu;
/*********************
** RPC declaration **
*********************/
GENODE_RPC(Rpc_cpu_state, Dataspace_capability, _cpu_state, Vcpu_id);
GENODE_RPC(Rpc_exception_handler, void, _exception_handler,
Signal_context_capability, Vcpu_id);
GENODE_RPC(Rpc_run, void, _run, Vcpu_id);
GENODE_RPC(Rpc_pause, void, _pause, Vcpu_id);
GENODE_RPC(Rpc_native_vcpu, Capability<Native_vcpu>, _native_vcpu, Vcpu_id);
GENODE_RPC_THROW(Rpc_attach, void, attach,
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps, Region_conflict,
Invalid_dataspace),
Dataspace_capability, addr_t, Attach_attr);
GENODE_RPC(Rpc_detach, void, detach, addr_t, size_t);
GENODE_RPC(Rpc_attach_pic, void, attach_pic, addr_t);
GENODE_RPC_THROW(Rpc_create_vcpu, Vcpu_id, _create_vcpu,
/**
* Create a new virtual CPU in the VM
*
* The vCPU inherits the affinity location (i.e., CPU) from the handler
* thread passed as parameter.
*/
GENODE_RPC_THROW(Rpc_create_vcpu, Capability<Native_vcpu>, create_vcpu,
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps),
Thread_capability);
GENODE_RPC_INTERFACE(Rpc_cpu_state, Rpc_exception_handler,
Rpc_run, Rpc_pause, Rpc_attach, Rpc_detach,
Rpc_attach_pic, Rpc_create_vcpu, Rpc_native_vcpu);
GENODE_RPC_INTERFACE(Rpc_attach, Rpc_detach, Rpc_attach_pic, Rpc_create_vcpu);
};
#endif /* _INCLUDE__VM_SESSION__VM_SESSION_H_ */

View File

@ -31,7 +31,6 @@ SRC_CC += stack_allocator.cc
SRC_CC += trace.cc
SRC_CC += root_proxy.cc
SRC_CC += env_session_id_space.cc
SRC_CC += vm_session.cc
SRC_CC += stack_protector.cc
INC_DIR += $(REP_DIR)/src/include $(BASE_DIR)/src/include

View File

@ -1,6 +1,7 @@
SRC_CC += default_log.cc
SRC_CC += env_deprecated.cc stack_area.cc env_reinit.cc main_thread_cap.cc
SRC_CC += rpc_cap_alloc.cc heartbeat.cc
SRC_CC += vm.cc
vpath %.cc $(REP_DIR)/src/lib/base
vpath %.cc $(BASE_DIR)/src/lib/base

View File

@ -63,8 +63,6 @@ _ZN6Genode10Ipc_serverC1Ev T
_ZN6Genode10Ipc_serverC2Ev T
_ZN6Genode10Ipc_serverD1Ev T
_ZN6Genode10Ipc_serverD2Ev T
_ZN6Genode10Vm_sessionD0Ev T
_ZN6Genode10Vm_sessionD2Ev T
_ZN6Genode11Sliced_heap4freeEPvm T
_ZN6Genode11Sliced_heap5allocEmPPv T
_ZN6Genode11Sliced_heapC1ERNS_13Ram_allocatorERNS_10Region_mapE T
@ -98,6 +96,11 @@ _ZN6Genode13Shared_objectC1ERNS_3EnvERNS_9AllocatorEPKcNS0_4BindENS0_4KeepE T
_ZN6Genode13Shared_objectC2ERNS_3EnvERNS_9AllocatorEPKcNS0_4BindENS0_4KeepE T
_ZN6Genode13Shared_objectD1Ev T
_ZN6Genode13Shared_objectD2Ev T
_ZN6Genode13Vm_connection4Vcpu3runEv T
_ZN6Genode13Vm_connection4Vcpu5pauseEv T
_ZN6Genode13Vm_connection4Vcpu5stateEv T
_ZN6Genode13Vm_connection4VcpuC1ERS0_RNS_9AllocatorERNS_17Vcpu_handler_baseERKNS0_11Exit_configE T
_ZN6Genode13Vm_connection4VcpuC2ERS0_RNS_9AllocatorERNS_17Vcpu_handler_baseERKNS0_11Exit_configE T
_ZN6Genode13sleep_foreverEv T
_ZN6Genode14Capability_map6insertEmm T
_ZN6Genode14Dynamic_linker23_for_each_loaded_objectERNS_3EnvERKNS0_11For_each_fnE T
@ -160,10 +163,6 @@ _ZN6Genode17Timeout_schedulerC2ERNS_11Time_sourceENS_12MicrosecondsE T
_ZN6Genode17Timeout_schedulerD0Ev T
_ZN6Genode17Timeout_schedulerD1Ev T
_ZN6Genode17Timeout_schedulerD2Ev T
_ZN6Genode17Vm_session_client11create_vcpuERNS_9AllocatorERNS_3EnvERNS_15Vm_handler_baseE T
_ZN6Genode17Vm_session_client3runENS_10Vm_session7Vcpu_idE T
_ZN6Genode17Vm_session_client5pauseENS_10Vm_session7Vcpu_idE T
_ZN6Genode17Vm_session_client9cpu_stateENS_10Vm_session7Vcpu_idE T
_ZN6Genode18Allocator_avl_base10_add_blockEPNS0_5BlockEmmb T
_ZN6Genode18Allocator_avl_base10alloc_addrEmm T
_ZN6Genode18Allocator_avl_base12remove_rangeEmm T
@ -188,7 +187,6 @@ _ZN6Genode18Signal_transmitter7contextENS_10CapabilityINS_14Signal_contextEEE T
_ZN6Genode18Signal_transmitter7contextEv T
_ZN6Genode18Signal_transmitterC1ENS_10CapabilityINS_14Signal_contextEEE T
_ZN6Genode18Signal_transmitterC2ENS_10CapabilityINS_14Signal_contextEEE T
_ZN6Genode18server_socket_pairEv T
_ZN6Genode20env_session_id_spaceEv T
_ZN6Genode25env_stack_area_region_mapE B 8
_ZN6Genode28env_stack_area_ram_allocatorE B 8

View File

@ -72,10 +72,11 @@ class Genode::Vm_root : public Root_component<Vm_session_component>
Ram_allocator &ram_alloc,
Region_map &local_rm,
Trace::Source_registry &trace_sources)
: Root_component<Vm_session_component>(&session_ep, &md_alloc),
_ram_allocator(ram_alloc),
_local_rm(local_rm),
_trace_sources(trace_sources)
:
Root_component<Vm_session_component>(&session_ep, &md_alloc),
_ram_allocator(ram_alloc),
_local_rm(local_rm),
_trace_sources(trace_sources)
{ }
};

View File

@ -1,11 +1,16 @@
/*
* \brief Core-specific instance of the VM session interface
* \brief Common functions for core VM-session component
* \author Alexander Boettcher
* \author Christian Helmuth
* \date 2018-08-26
*
* This file implements region-map functions for VM session, which
* includes the 'Region_map_detach' interface. The latter is used if
* an attached dataspace is destroyed.
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -106,6 +111,7 @@ void Vm_session_component::attach(Dataspace_capability const cap,
});
}
void Vm_session_component::detach(addr_t guest_phys, size_t size)
{
if (guest_phys & 0xffful) {
@ -133,6 +139,7 @@ void Vm_session_component::detach(addr_t guest_phys, size_t size)
_detach_vm_memory(guest_phys, size);
}
void Vm_session_component::detach(Region_map::Local_addr addr)
{
Rm_region *region = _map.metadata(addr);
@ -142,6 +149,7 @@ void Vm_session_component::detach(Region_map::Local_addr addr)
Genode::error(__PRETTY_FUNCTION__, " unknown region");
}
void Vm_session_component::unmap_region(addr_t base, size_t size)
{
Genode::error(__func__, " unimplemented ", base, " ", size);

View File

@ -0,0 +1,46 @@
/*
* \brief Generic VM-connection implementation
* \author Alexander Boettcher
* \author Christian Helmuth
* \date 2018-08-27
*/
/*
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <vm_session/connection.h>
using namespace Genode;
struct Vm_session::Native_vcpu { };
static Vm_session::Native_vcpu dummy;
void Vm_connection::Vcpu::run() { }
void Vm_connection::Vcpu::pause() { }
struct Genode::Vcpu_state { };
Vcpu_state & Vm_connection::Vcpu::state()
{
static char dummy[sizeof(Vcpu_state)];
return *(Vcpu_state *)dummy;
}
Vm_connection::Vcpu::Vcpu(Vm_connection &, Allocator &,
Vcpu_handler_base &, Exit_config const &)
:
_native_vcpu(dummy)
{
}

View File

@ -1,43 +0,0 @@
/*
* \brief Client-side VM session interface
* \author Alexander Boettcher
* \date 2018-08-27
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <vm_session/client.h>
using namespace Genode;
Vm_session::Vcpu_id
Vm_session_client::create_vcpu(Allocator &, Env &, Vm_handler_base &handler)
{
Thread * ep = reinterpret_cast<Thread *>(&handler._rpc_ep);
Vcpu_id vcpu_id { call<Rpc_create_vcpu>(ep->cap()) };
call<Rpc_exception_handler>(handler._cap, vcpu_id);
return vcpu_id;
}
void Vm_session_client::run(Vcpu_id const vcpu_id)
{
call<Rpc_run>(vcpu_id);
}
void Vm_session_client::pause(Vcpu_id const vcpu_id)
{
call<Rpc_pause>(vcpu_id);
}
Dataspace_capability Vm_session_client::cpu_state(Vcpu_id const vcpu_id)
{
return call<Rpc_cpu_state>(vcpu_id);
}
Vm_session::~Vm_session()
{ }

View File

@ -47,6 +47,7 @@ install_config {
<service name="VM"/>
</parent-provides>
<default-route>
<service name="VM"> <parent diag="yes"/> </service>
<any-service><parent/><any-child/></any-service>
</default-route>
<default caps="100"/>
@ -66,6 +67,30 @@ set boot_modules {
timer
test-vmm_x86
}
#
# Generate artificial platform info to allow for executing the scenario on
# base-linux
#
if {[have_spec linux]} {
set fd [open [run_dir]/genode/platform_info w]
puts $fd {
<platform_info>
<kernel name="nova"/>
<acpi revision="2" rsdt="0x2ffe20c5"/>
<affinity-space width="1" height="1"/>
<boot/>
<hardware>
<features svm="true" vmx="true"/>
<tsc invariant="false" freq_khz="2555589"/>
<cpus>
<cpu id="0" package="0" core="0" thread="0" family="0x10" model="0x2" stepping="0x3" platform="0x0" patch="0x0"/>
</cpus>
</hardware>
</platform_info>}
close $fd
}
build_boot_image $boot_modules
append qemu_args " -cpu phenom -smp 2"

View File

@ -19,6 +19,7 @@
#include <dataspace/client.h>
#include <rom_session/connection.h>
#include <vm_session/connection.h>
#include <vm_session/handler.h>
#include <util/noncopyable.h>
#include <cpu/vm_state_trustzone.h>
@ -69,9 +70,11 @@ class Genode::Vm_base : Noncopyable, Interface
Machine_type const _machine;
Board_revision const _board;
Ram const _ram;
Vm_connection _vm { _env };
Vm_session::Vcpu_id _vcpu_id;
Vm_state &_state { *(Vm_state*)_env.rm().attach(_vm.cpu_state(_vcpu_id)) };
Vm_connection _vm { _env };
Vm_connection::Exit_config _exit_config { };
Vm_connection::Vcpu _vcpu;
Vm_state &_state { *(Vm_state *)&_vcpu.state() };
void _load_kernel();
@ -92,10 +95,10 @@ class Genode::Vm_base : Noncopyable, Interface
Machine_type machine,
Board_revision board,
Allocator &alloc,
Vm_handler_base &handler);
Vcpu_handler_base &handler);
void run() { _vm.run(_vcpu_id); }
void pause() { _vm.pause(_vcpu_id); }
void run() { _vcpu.run(); }
void pause() { _vcpu.pause(); }
void start();
void dump();

View File

@ -44,7 +44,7 @@ class Main
Vm::Kernel_name const _kernel_name { "linux" };
Vm::Command_line const _cmd_line { "console=ttymxc0,115200" };
Attached_rom_dataspace _config { _env, "config" };
Vm_handler<Main> _exception_handler { _env.ep(), *this,
Vcpu_handler<Main> _exception_handler { _env.ep(), *this,
&Main::_handle_exception };
Heap _heap { &_env.ram(), &_env.rm() };

View File

@ -48,7 +48,7 @@ class Genode::Vm : public Vm_base
Machine_type mach,
Board_revision board,
Allocator &alloc,
Vm_handler_base &handler)
Vcpu_handler_base &handler)
: Vm_base(env, kernel, cmdl, ram, ram_sz, kernel_off, mach, board,
alloc, handler)
{ }

View File

@ -52,7 +52,7 @@ class Genode::Vm : public Vm_base
Machine_type mach,
Board_revision board,
Allocator &alloc,
Vm_handler_base &handler)
Vcpu_handler_base &handler)
: Vm_base(env, kernel, cmdl, ram, ram_sz, kernel_off, mach, board,
alloc, handler)
{ }

View File

@ -40,11 +40,11 @@ Vm_base::Vm_base(Env &env,
Machine_type machine,
Board_revision board,
Allocator &alloc,
Vm_handler_base &handler)
Vcpu_handler_base &handler)
:
_env(env), _kernel(kernel), _cmdline(cmdline), _kernel_off(kernel_off),
_machine(machine), _board(board), _ram(env, ram_base, ram_size),
_vcpu_id(_vm.create_vcpu(alloc, env, handler))
_vcpu(_vm, alloc, handler, _exit_config)
{
_state.irq_injection = 0;
}

View File

@ -169,13 +169,12 @@ void Cpu_base::_update_state()
_timer.cancel_timeout();
}
unsigned Cpu_base::cpu_id() const { return _vcpu_id.id; }
void Cpu_base::run() { _vm_session.run(_vcpu_id); }
void Cpu_base::pause() { _vm_session.pause(_vcpu_id); }
bool Cpu_base::active() const { return _active; }
Cpu_base::State & Cpu_base::state() const { return _state; }
Gic::Gicd_banked & Cpu_base::gic() { return _gic; }
unsigned Cpu_base::cpu_id() const { return _vcpu_id; }
void Cpu_base::run() { _vm_vcpu.run(); }
void Cpu_base::pause() { _vm_vcpu.pause(); }
bool Cpu_base::active() const { return _active; }
Cpu_base::State & Cpu_base::state() const { return _state; }
Gic::Gicd_banked & Cpu_base::gic() { return _gic; }
void Cpu_base::recall()
@ -190,14 +189,14 @@ Cpu_base::Cpu_base(Vm & vm,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep)
: _vm(vm),
Genode::Entrypoint & ep,
short const id)
: _vcpu_id(id),
_vm(vm),
_vm_session(vm_session),
_heap(heap),
_vm_handler(*this, ep, *this, &Cpu_base::_handle_nothing),
_vcpu_id(_vm_session.with_upgrade([&]() {
return _vm_session.create_vcpu(heap, env, _vm_handler);
})),
_state(*((State*)env.rm().attach(_vm_session.cpu_state(_vcpu_id)))),
_vm_vcpu(_vm_session, heap, _vm_handler, _exit_config),
_state(*((State*)(&_vm_vcpu.state()))),
_gic(*this, gic, bus),
_timer(env, ep, _gic.irq(VTIMER_IRQ), *this) { }

View File

@ -22,6 +22,7 @@
#include <cpu/vm_state_virtualization.h>
#include <util/mmio.h>
#include <vm_session/connection.h>
#include <vm_session/handler.h>
namespace Vmm {
class Vm;
@ -60,7 +61,8 @@ class Vmm::Cpu_base
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep);
Genode::Entrypoint & ep,
short const cpu_id);
unsigned cpu_id() const;
void run();
@ -88,9 +90,9 @@ class Vmm::Cpu_base
}
template <typename T>
struct Signal_handler : Genode::Vm_handler<Signal_handler<T>>
struct Signal_handler : Genode::Vcpu_handler<Signal_handler<T>>
{
using Base = Genode::Vm_handler<Signal_handler<T>>;
using Base = Genode::Vcpu_handler<Signal_handler<T>>;
Cpu_base & cpu;
T & obj;
@ -194,14 +196,16 @@ class Vmm::Cpu_base
return (r->_encoding > _encoding); }
};
bool _active { true };
Vm & _vm;
Genode::Vm_connection & _vm_session;
Genode::Heap & _heap;
Signal_handler<Cpu_base> _vm_handler;
Genode::Vm_session::Vcpu_id _vcpu_id;
State & _state;
Genode::Avl_tree<System_register> _reg_tree;
short _vcpu_id;
bool _active { true };
Vm & _vm;
Genode::Vm_connection & _vm_session;
Genode::Heap & _heap;
Signal_handler<Cpu_base> _vm_handler;
Genode::Vm_connection::Exit_config _exit_config { };
Genode::Vm_connection::Vcpu _vm_vcpu;
State & _state;
Genode::Avl_tree<System_register> _reg_tree;
/***********************

View File

@ -161,8 +161,9 @@ Cpu::Cpu(Vm & vm,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep)
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep),
Genode::Entrypoint & ep,
short const id)
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep, id),
_sr_midr (0, 0, 0, 0, "MIDR", false, 0x412fc0f1, _reg_tree),
_sr_mpidr (0, 0, 0, 5, "MPIDR", false, 1<<31|cpu_id(), _reg_tree),
_sr_mmfr0 (0, 0, 1, 4, "MMFR0", false, 0x10201105, _reg_tree),

View File

@ -28,7 +28,8 @@ class Vmm::Cpu : public Vmm::Cpu_base
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep);
Genode::Entrypoint & ep,
short cpu_id);
enum Exception_type {
NO_EXCEPTION,

View File

@ -212,8 +212,9 @@ Cpu::Cpu(Vm & vm,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep)
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep),
Genode::Entrypoint & ep,
short const id)
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep, id),
_sr_id_aa64afr0_el1 (3, 0, 0, 5, 4, "ID_AA64AFR0_EL1", false, 0x0, _reg_tree),
_sr_id_aa64afr1_el1 (3, 0, 0, 5, 5, "ID_AA64AFR1_EL1", false, 0x0, _reg_tree),
_sr_id_aa64dfr0_el1 (3, 0, 0, 5, 0, "ID_AA64DFR0_EL1", false, 0x6, _reg_tree),

View File

@ -28,7 +28,8 @@ class Vmm::Cpu : public Vmm::Cpu_base
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep);
Genode::Entrypoint & ep,
short cpu_id);
enum Exception_type {
AARCH64_SYNC = 0x400,

View File

@ -41,7 +41,7 @@ void Vm::_load_initrd()
Vmm::Cpu & Vm::boot_cpu()
{
if (!_cpus[0].constructed())
_cpus[0].construct(*this, _vm, _bus, _gic, _env, _heap, _env.ep());
_cpus[0].construct(*this, _vm, _bus, _gic, _env, _heap, _env.ep(), 0);
return *_cpus[0];
}
@ -59,7 +59,11 @@ Vm::Vm(Genode::Env & env)
_virtio_block("Block", VIRTIO_BLK_MMIO_START, VIRTIO_BLK_MMIO_SIZE,
VIRTIO_BLK_IRQ, boot_cpu(), _bus, _ram, env, _heap)
{
_vm.attach(_vm_ram.cap(), RAM_START);
_vm.attach(_vm_ram.cap(), RAM_START,
Genode::Vm_session::Attach_attr { .offset = 0,
.size = 0,
.executable = true,
.writeable = true });
_load_kernel();
_load_dtb();
@ -69,7 +73,7 @@ Vm::Vm(Genode::Env & env)
Genode::Affinity::Space space = _env.cpu().affinity_space();
Genode::Affinity::Location location(space.location_of_index(i));
_eps[i].construct(_env, STACK_SIZE, "vcpu ep", location);
_cpus[i].construct(*this, _vm, _bus, _gic, _env, _heap, *_eps[i]);
_cpus[i].construct(*this, _vm, _bus, _gic, _env, _heap, *_eps[i], i);
}
Genode::log("Start virtual machine ...");

View File

@ -1,17 +1,19 @@
/*
* \brief VM session interface test for x86
* \author Alexander Boettcher
* \author Christian Helmuth
* \date 2018-09-26
*
*/
/*
* Copyright (C) 2018-2019 Genode Labs GmbH
* Copyright (C) 2018-2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/attached_dataspace.h>
#include <base/attached_rom_dataspace.h>
#include <base/component.h>
@ -20,59 +22,70 @@
#include <timer_session/connection.h>
#include <util/reconstructible.h>
#include <vm_session/connection.h>
#include <vm_session/vm_session.h>
#include <vm_session/handler.h>
#include <cpu/vcpu_state.h>
#include <cpu/vm_state.h>
namespace Vmm {
using namespace Genode;
class Vm;
class Vm;
class Vcpu;
class Main;
namespace Intel_exit {
enum {
CPUID = 0x0a,
HLT = 0x0c,
INVALID_STATE = 0x21,
EPT = 0x30
/*
* Note, the test implementation requires the exit values to be disjunct
* between Intel and AMD due to conditional not checking the used harwdare
* platform.
*/
enum class Exit : unsigned {
INTEL_CPUID = 0x0a,
INTEL_HLT = 0x0c,
INTEL_INVALID_STATE = 0x21,
INTEL_EPT = 0x30,
AMD_PF = 0x4e,
AMD_HLT = 0x78,
AMD_TRIPLE_FAULT = 0x7f,
AMD_NPT = 0xfc,
/* synthetic exits */
STARTUP = 0xfe,
PAUSED = 0xff
};
Vm_connection::Exit_config const exit_config {
/* ... */
};
}
namespace Amd_exit {
enum {
PF = 0x4e,
HLT = 0x78,
TRIPLE_FAULT = 0x7f,
NPT = 0xfc
};
}
class Vcpu {
class Vmm::Vcpu
{
private:
Vm &_vm;
Genode::Vm_connection &_vm_con;
Genode::Vm_handler<Vcpu> _handler;
Genode::Vm_session_client::Vcpu_id _vcpu;
Genode::Attached_dataspace _state_ds;
Genode::Vm_state &_state;
unsigned const _id;
bool const _svm;
bool const _vmx;
bool _svm;
bool _vmx;
Vm &_vm;
Vm_connection &_vm_con;
Vcpu_handler<Vcpu> _handler;
Vm_connection::Vcpu _vcpu;
/* test state start - some status information to steer the test */
unsigned _exit_count { 0 };
unsigned _pause_count { 0 };
unsigned _timer_count { 0 };
unsigned _pause_at_timer { 0 };
enum {
/* some status information to steer the test */
enum class State {
INITIAL,
HALTED,
PAUSED,
UNKNOWN,
RUNNING
} _test_state { INITIAL };
/* test state end */
} _test_state { State::INITIAL };
void _handle_vm_exception();
/* test state end */
unsigned _exit_count { 0 };
unsigned _pause_count { 0 };
unsigned _timer_count { 0 };
unsigned _pause_at_timer { 0 };
void _handle_vcpu_exit();
void _cpu_init()
{
@ -82,95 +95,85 @@ class Vcpu {
/* http://www.sandpile.org/x86/initial.htm */
typedef Genode::Vm_state::Segment Segment;
typedef Genode::Vm_state::Range Range;
typedef Vcpu_state::Segment Segment;
typedef Vcpu_state::Range Range;
_state = Genode::Vm_state {};
Vcpu_state &state { _vcpu.state() };
_state.flags.value(2);
_state.ip. value(0xfff0);
_state.cr0. value(0x10);
_state.cs. value(Segment{0xf000, 0x93, 0xffff, 0xffff0000});
_state.ss. value(Segment{0, 0x93, _state.cs.value().limit, 0});
_state.dx. value(0x600);
_state.es. value(Segment{0, _state.ss.value().ar,
_state.cs.value().limit, 0});
_state.ds. value(Segment{0, _state.ss.value().ar,
_state.cs.value().limit, 0});
_state.fs. value(Segment{0, _state.ss.value().ar,
_state.cs.value().limit, 0});
_state.gs. value(Segment{0, _state.ss.value().ar,
_state.cs.value().limit, 0});
_state.tr. value(Segment{0, 0x8b, 0xffff, 0});
_state.ldtr. value(Segment{0, 0x1000, _state.tr.value().limit, 0});
_state.gdtr. value(Range {0, 0xffff});
_state.idtr. value(Range {0, _state.gdtr.value().limit});
_state.dr7. value(0x400);
state.flags.charge(2);
state.ip. charge(0xfff0);
state.cr0. charge(0x10);
state.cs. charge(Segment{0xf000, 0x93, 0xffff, 0xffff0000});
state.ss. charge(Segment{0, 0x93, state.cs.value().limit, 0});
state.dx. charge(0x600);
state.es. charge(Segment{0, state.ss.value().ar,
state.cs.value().limit, 0});
state.ds. charge(Segment{0, state.ss.value().ar,
state.cs.value().limit, 0});
state.fs. charge(Segment{0, state.ss.value().ar,
state.cs.value().limit, 0});
state.gs. charge(Segment{0, state.ss.value().ar,
state.cs.value().limit, 0});
state.tr. charge(Segment{0, 0x8b, 0xffff, 0});
state.ldtr. charge(Segment{0, 0x1000, state.tr.value().limit, 0});
state.gdtr. charge(Range {0, 0xffff});
state.idtr. charge(Range {0, state.gdtr.value().limit});
state.dr7. charge(0x400);
if (_vmx) {
_state.ctrl_primary.value(INTEL_CTRL_PRIMARY_HLT);
state.ctrl_primary.charge(INTEL_CTRL_PRIMARY_HLT);
/* required for seL4 */
_state.ctrl_secondary.value(INTEL_CTRL_SECOND_UG);
state.ctrl_secondary.charge(INTEL_CTRL_SECOND_UG);
}
if (_svm) {
/* required for native AMD hardware (!= Qemu) for NOVA */
_state.ctrl_secondary.value(AMD_CTRL_SECOND_VMRUN);
}
}
void _exit_config(Genode::Vm_state &state, unsigned exit)
{
/* touch the register state required for the specific vm exit */
state.ip.value(0);
if (exit == Intel_exit::EPT || exit == Amd_exit::NPT ||
exit == Amd_exit::PF)
{
state.qual_primary.value(0);
state.qual_secondary.value(0);
state.ctrl_secondary.charge(AMD_CTRL_SECOND_VMRUN);
}
}
public:
Vcpu(Genode::Entrypoint &ep, Genode::Vm_connection &vm_con,
Genode::Allocator &alloc, Genode::Env &env, Vm &vm,
bool svm, bool vmx)
Vcpu(unsigned const id, Entrypoint &ep, Vm_connection &vm_con,
Allocator &alloc, Vm &vm, bool svm, bool vmx)
:
_id(id), _svm(svm), _vmx(vmx),
_vm(vm), _vm_con(vm_con),
_handler(ep, *this, &Vcpu::_handle_vm_exception, &Vcpu::_exit_config),
/* construct vcpu */
_vcpu(_vm_con.with_upgrade([&]() {
return _vm_con.create_vcpu(alloc, env, _handler); })),
/* get state of vcpu */
_state_ds(env.rm(), _vm_con.cpu_state(_vcpu)),
_state(*_state_ds.local_addr<Genode::Vm_state>()),
_svm(svm), _vmx(vmx)
_handler(ep, *this, &Vcpu::_handle_vcpu_exit),
_vcpu(_vm_con, alloc, _handler, exit_config)
{
Genode::log("vcpu ", _vcpu.id, " : created");
log("vcpu ", _id, " : created");
}
Genode::Vm_session_client::Vcpu_id id() const { return _vcpu; }
unsigned id() const { return _id; }
void run() { _vcpu.run(); }
void pause() { _vcpu.pause(); }
void skip_instruction(unsigned bytes)
{
_state = Genode::Vm_state {};
_state.ip.value(_state.ip.value() + bytes);
_vcpu.state().ip.charge(_vcpu.state().ip.value() + bytes);
}
void force_fpu_state_transfer()
{
/* force FPU-state transfer on next entry */
_vcpu.state().fpu.charge([] (Vcpu_state::Fpu::State &) {
/* don't change state */ });
}
/*
* state information and state requests to steer the test
*/
bool halted() const {
return _test_state == HALTED; }
return _test_state == State::HALTED; }
bool paused_1st() const {
return _test_state == PAUSED && _pause_count == 1; }
return _test_state == State::PAUSED && _pause_count == 1; }
bool paused_2nd() const {
return _test_state == PAUSED && _pause_count == 2; }
return _test_state == State::PAUSED && _pause_count == 2; }
bool paused_3rd() const {
return _test_state == PAUSED && _pause_count == 3; }
return _test_state == State::PAUSED && _pause_count == 3; }
bool paused_4th() const {
return _test_state == PAUSED && _pause_count == 4; }
return _test_state == State::PAUSED && _pause_count == 4; }
void break_endless_loop()
{
@ -188,42 +191,41 @@ class Vcpu {
return true;
}
void claim_state_unknown() { _test_state = UNKNOWN; }
void claim_state_unknown() { _test_state = State::UNKNOWN; }
void timer_triggered() { _timer_count ++; }
void timer_triggered() { _timer_count++; }
};
class Vm {
class Vmm::Vm
{
private:
enum { STACK_SIZE = 2*1024*sizeof(long) };
Genode::Heap _heap;
Genode::Vm_connection _vm_con;
bool _svm;
bool _vmx;
Genode::Entrypoint &_ep_first; /* running on first CPU */
Genode::Entrypoint _ep_second; /* running on second CPU */
Vcpu _vcpu0;
Vcpu _vcpu1;
Vcpu _vcpu2;
Vcpu _vcpu3;
Genode::Dataspace_capability _memory; /* guest memory */
Heap _heap;
Vm_connection _vm_con;
bool const _svm;
bool const _vmx;
Entrypoint &_ep_first; /* running on first CPU */
Entrypoint _ep_second; /* running on second CPU */
Vcpu _vcpu0;
Vcpu _vcpu1;
Vcpu _vcpu2;
Vcpu _vcpu3;
Dataspace_capability _memory; /* guest memory */
/* just to trigger some events after some time */
Timer::Connection _timer;
Genode::Signal_handler<Vm> _timer_handler;
Timer::Connection _timer;
Signal_handler<Vm> _timer_handler;
/* trigger destruction of _vm session to test this case also */
Genode::Signal_context_capability _signal_destruction;
Signal_context_capability _signal_destruction;
void _handle_timer();
bool _cpu_name(char const * name)
static bool _cpu_name(char const * name)
{
using Genode::uint32_t;
uint32_t cpuid = 0, edx = 0, ebx = 0, ecx = 0;
asm volatile ("cpuid" : "+a" (cpuid), "=d" (edx), "=b"(ebx), "=c"(ecx));
@ -232,14 +234,14 @@ class Vm {
ecx == *reinterpret_cast<uint32_t const *>(name + 8);
}
bool _amd() { return _cpu_name("AuthenticAMD"); }
bool _intel() { return _cpu_name("GenuineIntel"); }
static bool _amd() { return _cpu_name("AuthenticAMD"); }
static bool _intel() { return _cpu_name("GenuineIntel"); }
/* lookup which hardware assisted feature the CPU supports */
bool _vm_feature(Genode::Env &env, char const *name)
static bool _vm_feature(Env &env, char const *name)
{
try {
Genode::Attached_rom_dataspace info { env, "platform_info"};
Attached_rom_dataspace info { env, "platform_info"};
return info.xml().sub_node("hardware").sub_node("features").attribute_value(name, false);
} catch (...) { }
@ -249,7 +251,7 @@ class Vm {
public:
Vm(Genode::Env &env, Genode::Signal_context_capability destruct_cap)
Vm(Env &env, Signal_context_capability destruct_cap)
:
_heap(env.ram(), env.rm()),
_vm_con(env),
@ -258,26 +260,24 @@ class Vm {
_ep_first(env.ep()),
_ep_second(env, STACK_SIZE, "second ep",
env.cpu().affinity_space().location_of_index(1)),
_vcpu0(_ep_first, _vm_con, _heap, env, *this, _svm, _vmx),
_vcpu1(_ep_first, _vm_con, _heap, env, *this, _svm, _vmx),
_vcpu2(_ep_second, _vm_con, _heap, env, *this, _svm, _vmx),
_vcpu3(_ep_second, _vm_con, _heap, env, *this, _svm, _vmx),
_vcpu0(0, _ep_first, _vm_con, _heap, *this, _svm, _vmx),
_vcpu1(1, _ep_first, _vm_con, _heap, *this, _svm, _vmx),
_vcpu2(2, _ep_second, _vm_con, _heap, *this, _svm, _vmx),
_vcpu3(3, _ep_second, _vm_con, _heap, *this, _svm, _vmx),
_memory(env.ram().alloc(4096)),
_timer(env),
_timer_handler(_ep_first, *this, &Vm::_handle_timer),
_signal_destruction(destruct_cap)
{
if (!_svm && !_vmx) {
Genode::error("no SVM nor VMX support detected");
error("no SVM nor VMX support detected");
throw 0;
}
/* prepare guest memory with some instructions for testing */
Genode::uint8_t * guest = env.rm().attach(_memory);
#if 0
*(guest + 0xff0) = 0x0f; /* CPUID instruction */
*(guest + 0xff1) = 0xa2;
#endif
uint8_t * guest = env.rm().attach(_memory);
// *(guest + 0xff0) = 0x0f; /* CPUID instruction */
// *(guest + 0xff1) = 0xa2;
*(guest + 0xff0) = 0xf4; /* HLT instruction */
*(guest + 0xff1) = 0xf4; /* HLT instruction */
*(guest + 0xff2) = 0xeb; /* JMP - endless loop to RIP */
@ -285,19 +285,19 @@ class Vm {
*(guest + 0xff4) = 0xf4; /* HLT instruction */
env.rm().detach(guest);
Genode::log ("let vCPUs run - first EP");
_vm_con.run(_vcpu0.id());
_vm_con.run(_vcpu1.id());
log ("let vCPUs run - first EP");
_vcpu0.run();
_vcpu1.run();
Genode::log ("let vCPUs run - second EP");
_vm_con.run(_vcpu2.id());
_vm_con.run(_vcpu3.id());
log ("let vCPUs run - second EP");
_vcpu2.run();
_vcpu3.run();
_timer.sigh(_timer_handler);
_timer.trigger_periodic(1000 * 1000 /* in us */);
}
Genode::Dataspace_capability handle_guest_memory_exit()
Dataspace_capability handle_guest_memory_exit()
{
/*
* A real VMM would now have to lookup the right dataspace for
@ -312,7 +312,7 @@ class Vm {
/**
* Handle timer events - used to trigger some pause/resume on vCPU for testing
*/
void Vm::_handle_timer()
void Vmm::Vm::_handle_timer()
{
_vcpu0.timer_triggered();
_vcpu1.timer_triggered();
@ -326,16 +326,16 @@ void Vm::_handle_timer()
*/
if (_vcpu2.halted()) {
/* test to trigger a Genode signal even if we're already blocked */
_vm_con.pause(_vcpu2.id());
_vcpu2.pause();
}
if (_vcpu2.paused_1st()) {
Genode::log(Genode::Thread::myself()->name(), " : request resume of vcpu ", _vcpu2.id().id);
log(Thread::myself()->name(), " : request resume of vcpu ", _vcpu2.id());
/* continue after first paused state */
_vm_con.run(_vcpu2.id());
_vcpu2.run();
} else if (_vcpu2.paused_2nd()) {
Genode::log(Genode::Thread::myself()->name(), " : request resume of vcpu ", _vcpu2.id().id);
log(Thread::myself()->name(), " : request resume of vcpu ", _vcpu2.id());
/* skip over next hlt instructions after second paused state */
_vcpu2.skip_instruction(1*1 /* 1x hlt instruction size */);
@ -344,7 +344,7 @@ void Vm::_handle_timer()
_vcpu2.claim_state_unknown();
/* the next instruction is again a hlt */
_vm_con.run(_vcpu2.id());
_vcpu2.run();
}
/*
@ -352,25 +352,27 @@ void Vm::_handle_timer()
* are on same physical CPU.
*/
if (_vcpu1.pause_endless_loop()) {
Genode::log("pause endless loop");
log("pause endless loop");
/* guest in endless jmp loop - request to stop it asap */
_vm_con.pause(_vcpu1.id());
_vcpu1.pause();
return;
}
if (_vcpu1.halted()) {
Genode::log(Genode::Thread::myself()->name(), " : request pause of vcpu ", _vcpu1.id().id);
log(Thread::myself()->name(), " : request pause of vcpu ", _vcpu1.id());
/* test to trigger a Genode signal even if we're already blocked */
_vm_con.pause(_vcpu1.id());
_vcpu1.pause();
}
if (_vcpu1.paused_1st()) {
Genode::log(Genode::Thread::myself()->name(), " : request resume (A) of vcpu ", _vcpu1.id().id);
log(Thread::myself()->name(), " : request resume (A) of vcpu ", _vcpu1.id());
_vcpu1.force_fpu_state_transfer();
/* continue after first paused state */
_vm_con.run(_vcpu1.id());
_vcpu1.run();
} else if (_vcpu1.paused_2nd()) {
Genode::log(Genode::Thread::myself()->name(), " : request resume (B) of vcpu ", _vcpu1.id().id);
log(Thread::myself()->name(), " : request resume (B) of vcpu ", _vcpu1.id());
/* skip over next 2 hlt instructions after second paused state */
_vcpu1.skip_instruction(2*1 /* 2x hlt instruction size */);
@ -379,139 +381,143 @@ void Vm::_handle_timer()
_vcpu1.claim_state_unknown();
/* the next instruction is actually a jmp endless loop */
_vm_con.run(_vcpu1.id());
_vcpu1.run();
/* request on the next timeout to stop the jmp endless loop */
_vcpu1.break_endless_loop();
} else if (_vcpu1.paused_3rd()) {
Genode::log(Genode::Thread::myself()->name(), " : request resume (C) of vcpu ", _vcpu1.id().id);
log(Thread::myself()->name(), " : request resume (C) of vcpu ", _vcpu1.id());
_vcpu1.skip_instruction(1*2 /* 1x jmp endless loop size */);
_vm_con.run(_vcpu1.id());
_vcpu1.run();
} else if (_vcpu1.paused_4th()) {
Genode::log("vcpu test finished - de-arm timer");
log("vcpu test finished - de-arm timer");
_timer.trigger_periodic(0);
/* trigger destruction of VM session */
Genode::Signal_transmitter(_signal_destruction).submit();
Signal_transmitter(_signal_destruction).submit();
}
}
/**
* Handle VM exits ...
*/
void Vcpu::_handle_vm_exception()
void Vmm::Vcpu::_handle_vcpu_exit()
{
using namespace Genode;
enum { VMEXIT_STARTUP = 0xfe, VMEXIT_PAUSED = 0xff };
Vcpu_state &state { _vcpu.state() };
unsigned const exit = _state.exit_reason;
Exit const exit { state.exit_reason };
_state = Vm_state {};
state.discharge();
_exit_count++;
log("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - ",
"reason ", Hex(exit), " handled by '",
Genode::Thread::myself()->name(), "'");
log("vcpu ", _id, " : ", _exit_count, ". vm exit - ",
"reason ", Hex((unsigned)exit), " handled by '",
Thread::myself()->name(), "'");
if (exit == VMEXIT_STARTUP) {
switch (exit) {
case Exit::STARTUP:
_cpu_init();
}
break;
if (exit == Intel_exit::INVALID_STATE) {
error("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - "
case Exit::INTEL_INVALID_STATE:
error("vcpu ", _id, " : ", _exit_count, ". vm exit - "
" halting vCPU - invalid guest state");
_test_state = UNKNOWN;
_test_state = State::UNKNOWN;
return;
}
if (exit == Amd_exit::TRIPLE_FAULT) {
error("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - "
case Exit::AMD_TRIPLE_FAULT:
error("vcpu ", _id, " : ", _exit_count, ". vm exit - "
" halting vCPU - triple fault");
_test_state = UNKNOWN;
_test_state = State::UNKNOWN;
return;
}
if (exit == VMEXIT_PAUSED) {
log("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - "
" due to pause() request - ip=", Hex(_state.ip.value()));
_pause_count ++;
_test_state = PAUSED;
case Exit::PAUSED:
log("vcpu ", _id, " : ", _exit_count, ". vm exit - "
" due to pause() request - ip=", Hex(state.ip.value()));
_pause_count++;
_test_state = State::PAUSED;
return;
}
if (exit == Intel_exit::HLT || exit == Amd_exit::HLT) {
log("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - "
" halting vCPU - guest called HLT - ip=", Hex(_state.ip.value()));
_test_state = HALTED;
case Exit::INTEL_HLT:
case Exit::AMD_HLT:
log("vcpu ", _id, " : ", _exit_count, ". vm exit - "
" halting vCPU - guest called HLT - ip=", Hex(state.ip.value()));
_test_state = State::HALTED;
return;
}
if (exit == Intel_exit::EPT ||
exit == Amd_exit::NPT || exit == Amd_exit::PF)
{
addr_t const guest_fault_addr = _state.qual_secondary.value();
addr_t const guest_map_addr = _state.qual_secondary.value() & ~0xFFFUL;
case Exit::INTEL_EPT:
case Exit::AMD_NPT:
case Exit::AMD_PF: {
addr_t const guest_fault_addr = state.qual_secondary.value();
addr_t const guest_map_addr = state.qual_secondary.value() & ~0xFFFUL;
log("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - "
" guest fault address: ", Hex(guest_fault_addr));
log("vcpu ", _id, " : ", _exit_count, ". vm exit - "
" guest fault address: ", Hex(guest_fault_addr));
Dataspace_capability cap = _vm.handle_guest_memory_exit();
if (!cap.valid()) {
error("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - "
" halting vCPU - guest memory lookup failed");
_test_state = UNKNOWN;
/* no memory - we halt the vcpu */
return;
}
if (guest_fault_addr != 0xfffffff0UL) {
error("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - "
" unknown guest fault address");
return;
Dataspace_capability cap = _vm.handle_guest_memory_exit();
if (!cap.valid()) {
error("vcpu ", _id, " : ", _exit_count, ". vm exit - "
" halting vCPU - guest memory lookup failed");
_test_state = State::UNKNOWN;
/* no memory - we halt the vcpu */
return;
}
if (guest_fault_addr != 0xfffffff0UL) {
error("vcpu ", _id, " : ", _exit_count, ". vm exit - "
" unknown guest fault address");
return;
}
_vm_con.attach(cap, guest_map_addr, { 0, 0, true, true });
}
_vm_con.attach(cap, guest_map_addr);
default: break;
}
if (_exit_count >= 5) {
error("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - "
error("vcpu ", _id, " : ", _exit_count, ". vm exit - "
" halting vCPU - unknown state");
_test_state = UNKNOWN;
_test_state = State::UNKNOWN;
return;
}
log("vcpu ", _vcpu.id, " : ", _exit_count, ". vm exit - resume vcpu");
log("vcpu ", _id, " : ", _exit_count, ". vm exit - resume vcpu");
_test_state = RUNNING;
_vm_con.run(_vcpu);
_test_state = State::RUNNING;
_vcpu.run();
}
class Vmm {
class Vmm::Main
{
private:
Genode::Signal_handler<Vmm> _destruct_handler;
Genode::Reconstructible<Vm> _vm;
Signal_handler<Main> _destruct_handler;
Reconstructible<Vm> _vm;
void _destruct()
{
Genode::log("destruct vm session");
log("destruct vm session");
_vm.destruct();
Genode::log("vmm test finished");
log("vmm test finished");
}
public:
Vmm(Genode::Env &env)
Main(Env &env)
:
_destruct_handler(env.ep(), *this, &Vmm::_destruct),
_destruct_handler(env.ep(), *this, &Main::_destruct),
_vm(env, _destruct_handler)
{
}
};
void Component::construct(Genode::Env & env) { static Vmm vmm(env); }
void Component::construct(Genode::Env & env) { static Vmm::Main main(env); }

View File

@ -253,7 +253,7 @@ append config_of_app {
for { set i 1} { $i <= $use_vms } { incr i} {
append config_of_app "
<start name=\"vbox${i}\" priority=\"-2\" caps=\"800\">"
<start name=\"vbox${i}\" priority=\"-2\" caps=\"1200\">"
append_if [expr $use_vbox5] config_of_app "
<binary name=\"$virtualbox5_binary\" />"

View File

@ -222,7 +222,7 @@ append config {
</start>}
append_if [expr $use_gui] config {
<start name="vbox2" priority="-2" caps="800">
<start name="vbox2" priority="-2" caps="1200">
<binary name="} $virtualbox_binary {"/>
<resource name="RAM" quantum="448M"/>
<config vbox_file="test.vbox" vm_name="TestVM">
@ -245,7 +245,7 @@ append_if [expr $use_gui] config {
</start>}
append config {
<start name="vbox1" priority="-2" caps="800">
<start name="vbox1" priority="-2" caps="1200">
<binary name="} $virtualbox_binary {"/>
<resource name="RAM" quantum="448M"/>
<config vbox_file="test.vbox" vm_name="TestVM">

View File

@ -33,7 +33,8 @@
#include <util/misc_math.h>
#include <vm_session/connection.h>
#include <cpu/vm_state.h>
#include <vm_session/handler.h>
#include <cpu/vcpu_state.h>
/* os includes */
#include <nic_session/connection.h>
@ -156,14 +157,14 @@ class Vcpu : public StaticReceiver<Vcpu>
private:
Genode::Vm_connection &_vm_con;
Genode::Vm_handler<Vcpu> _handler;
Genode::Vcpu_handler<Vcpu> _handler;
bool const _vmx;
bool const _svm;
bool const _map_small;
bool const _rdtsc_exit;
Genode::Vm_session_client::Vcpu_id _id;
Genode::Attached_dataspace _state_ds;
Genode::Vm_state &_state;
Genode::Vm_connection::Exit_config _exit_config { };
Genode::Vm_connection::Vcpu _vm_vcpu;
Genode::Vcpu_state &_state;
Seoul::Guest_memory &_guest_memory;
Synced_motherboard &_motherboard;
@ -183,18 +184,12 @@ class Vcpu : public StaticReceiver<Vcpu>
bool vmx, bool svm, bool map_small, bool rdtsc)
:
_vm_con(vm_con),
_handler(ep, *this, &Vcpu::_handle_vm_exception,
vmx ? &Vcpu::exit_config_intel :
svm ? &Vcpu::exit_config_amd : nullptr),
_handler(ep, *this, &Vcpu::_handle_vm_exception),
// vmx ? &Vcpu::exit_config_intel :
// svm ? &Vcpu::exit_config_amd : nullptr),
_vmx(vmx), _svm(svm), _map_small(map_small), _rdtsc_exit(rdtsc),
/* construct vcpu */
_id(_vm_con.with_upgrade([&]() {
return _vm_con.create_vcpu(alloc, env, _handler);
})),
/* get state of vcpu */
_state_ds(env.rm(), _vm_con.cpu_state(_id)),
_state(*_state_ds.local_addr<Genode::Vm_state>()),
_vm_vcpu(_vm_con, alloc, _handler, _exit_config),
_state(_vm_vcpu.state()),
_guest_memory(guest_memory),
_motherboard(motherboard),
_vcpu(vcpu_mutex, unsynchronized_vcpu)
@ -208,15 +203,13 @@ class Vcpu : public StaticReceiver<Vcpu>
unsynchronized_vcpu->executor.add(this, receive_static<CpuMessage>);
/* let vCPU run */
_vm_con.run(id());
_vm_vcpu.run();
}
Genode::Vm_session_client::Vcpu_id id() const { return _id; }
void block() { _block.down(); }
void unblock() { _block.up(); }
void recall() { _vm_con.pause(id()); }
void recall() { _vm_vcpu.pause(); }
void _handle_vm_exception()
{
@ -269,11 +262,10 @@ class Vcpu : public StaticReceiver<Vcpu>
}
/* resume */
_vm_con.run(id());
_vm_vcpu.run();
}
void exit_config_intel(Genode::Vm_state &state, unsigned exit)
void exit_config_intel(Genode::Vcpu_state &state, unsigned exit)
{
CpuState dummy_state;
unsigned mtd = 0;
@ -331,7 +323,7 @@ class Vcpu : public StaticReceiver<Vcpu>
Seoul::write_vm_state(dummy_state, mtd, state);
}
void exit_config_amd(Genode::Vm_state &state, unsigned exit)
void exit_config_amd(Genode::Vcpu_state &state, unsigned exit)
{
CpuState dummy_state;
unsigned mtd = 0;
@ -482,7 +474,7 @@ class Vcpu : public StaticReceiver<Vcpu>
if (need_unmap)
Logging::panic("_handle_map_memory: need_unmap not handled, yet\n");
assert(_state.inj_info.valid());
assert(_state.inj_info.charged());
/* EPT violation during IDT vectoring? */
if (_state.inj_info.value() & 0x80000000U) {
@ -500,9 +492,9 @@ class Vcpu : public StaticReceiver<Vcpu>
/* convert Seoul state to Genode VM state */
Seoul::write_vm_state(_seoul_state, _win.mtr_out, _state);
// _state.inj_info.value(_state.inj_info.value() & ~0x80000000U);
// _state.inj_info.charge(_state.inj_info.value() & ~0x80000000U);
} else
_state = Genode::Vm_state {}; /* reset */
_state.discharge(); /* reset */
_vm_con.with_upgrade([&]() {
if (_map_small)
@ -549,7 +541,7 @@ class Vcpu : public StaticReceiver<Vcpu>
void _svm_startup()
{
_handle_vcpu(NO_SKIP, CpuMessage::TYPE_CHECK_IRQ);
_state.ctrl_primary.value(_rdtsc_exit ? (1U << 14) : 0);
_state.ctrl_primary.charge(_rdtsc_exit ? (1U << 14) : 0);
}
void _svm_npt()
@ -566,23 +558,23 @@ class Vcpu : public StaticReceiver<Vcpu>
void _svm_invalid()
{
_handle_vcpu(NO_SKIP, CpuMessage::TYPE_SINGLE_STEP);
_state.ctrl_primary.value(1 << 18 /* cpuid */ | (_rdtsc_exit ? (1U << 14) : 0));
_state.ctrl_secondary.value(1 << 0 /* vmrun */);
_state.ctrl_primary.charge(1 << 18 /* cpuid */ | (_rdtsc_exit ? (1U << 14) : 0));
_state.ctrl_secondary.charge(1 << 0 /* vmrun */);
}
void _svm_ioio()
{
if (_state.qual_primary.value() & 0x4) {
Genode::log("invalid gueststate");
_state = Genode::Vm_state {}; /* reset */
_state.ctrl_secondary.value(0);
_state.discharge(); /* reset */
_state.ctrl_secondary.charge(0);
} else {
unsigned order = ((_state.qual_primary.value() >> 4) & 7) - 1;
if (order > 2)
order = 2;
_state.ip_len.value(_state.qual_secondary.value() - _state.ip.value());
_state.ip_len.charge(_state.qual_secondary.value() - _state.ip.value());
_handle_io(_state.qual_primary.value() & 1, order,
_state.qual_primary.value() >> 16);
@ -591,19 +583,19 @@ class Vcpu : public StaticReceiver<Vcpu>
void _svm_cpuid()
{
_state.ip_len.value(2);
_state.ip_len.charge(2);
_handle_vcpu(SKIP, CpuMessage::TYPE_CPUID);
}
void _svm_hlt()
{
_state.ip_len.value(1);
_state.ip_len.charge(1);
_vmx_hlt();
}
void _svm_rdtsc()
{
_state.ip_len.value(2);
_state.ip_len.charge(2);
_handle_vcpu(SKIP, CpuMessage::TYPE_RDTSC);
}
@ -644,8 +636,8 @@ class Vcpu : public StaticReceiver<Vcpu>
void _vmx_vmcall()
{
_state = Genode::Vm_state {}; /* reset */
_state.ip.value(_state.ip.value() + _state.ip_len.value());
_state.discharge(); /* reset */
_state.ip.charge(_state.ip.value() + _state.ip_len.value());
}
void _vmx_pause()
@ -663,15 +655,15 @@ class Vcpu : public StaticReceiver<Vcpu>
void _vmx_invalid()
{
_state.flags.value(_state.flags.value() | 2);
_state.flags.charge(_state.flags.value() | 2);
_handle_vcpu(NO_SKIP, CpuMessage::TYPE_SINGLE_STEP);
}
void _vmx_startup()
{
_handle_vcpu(NO_SKIP, CpuMessage::TYPE_HLT);
_state.ctrl_primary.value(_rdtsc_exit ? (1U << 12) : 0);
_state.ctrl_secondary.value(0);
_state.ctrl_primary.charge(_rdtsc_exit ? (1U << 12) : 0);
_state.ctrl_secondary.charge(0);
}
void _vmx_ioio()
@ -679,9 +671,9 @@ class Vcpu : public StaticReceiver<Vcpu>
unsigned order = 0U;
if (_state.qual_primary.value() & 0x10) {
Logging::printf("invalid gueststate\n");
assert(_state.flags.valid());
_state = Genode::Vm_state {}; /* reset */
_state.flags.value(_state.flags.value() & ~2U);
assert(_state.flags.charged());
_state.discharge(); /* reset */
_state.flags.charge(_state.flags.value() & ~2U);
} else {
order = _state.qual_primary.value() & 7;
if (order > 2) order = 2;

View File

@ -15,130 +15,156 @@
#include "state.h"
void Seoul::write_vm_state(CpuState &seoul, unsigned mtr, Genode::Vm_state &state)
void Seoul::write_vm_state(CpuState &seoul, unsigned mtr, Genode::Vcpu_state &state)
{
state = Genode::Vm_state {}; /* reset */
state.discharge(); /* reset */
if (mtr & MTD_GPR_ACDB) {
state.ax.value(seoul.rax);
state.cx.value(seoul.rcx);
state.dx.value(seoul.rdx);
state.bx.value(seoul.rbx);
state.ax.charge(seoul.rax);
state.cx.charge(seoul.rcx);
state.dx.charge(seoul.rdx);
state.bx.charge(seoul.rbx);
mtr &= ~MTD_GPR_ACDB;
}
if (mtr & MTD_GPR_BSD) {
state.di.value(seoul.rdix);
state.si.value(seoul.rsix);
state.bp.value(seoul.rbpx);
state.di.charge(seoul.rdix);
state.si.charge(seoul.rsix);
state.bp.charge(seoul.rbpx);
mtr &= ~MTD_GPR_BSD;
}
if (mtr & MTD_RIP_LEN) {
state.ip.value(seoul.ripx);
state.ip_len.value(seoul.inst_len);
state.ip.charge(seoul.ripx);
state.ip_len.charge(seoul.inst_len);
mtr &= ~MTD_RIP_LEN;
}
if (mtr & MTD_RSP) {
state.sp.value(seoul.rspx);
state.sp.charge(seoul.rspx);
mtr &= ~MTD_RSP;
}
if (mtr & MTD_RFLAGS) {
state.flags.value(seoul.rflx);
state.flags.charge(seoul.rflx);
mtr &= ~MTD_RFLAGS;
}
if (mtr & MTD_DR) {
state.dr7.value(seoul.dr7);
state.dr7.charge(seoul.dr7);
mtr &= ~MTD_DR;
}
if (mtr & MTD_CR) {
state.cr0.value(seoul.cr0);
state.cr2.value(seoul.cr2);
state.cr3.value(seoul.cr3);
state.cr4.value(seoul.cr4);
state.cr0.charge(seoul.cr0);
state.cr2.charge(seoul.cr2);
state.cr3.charge(seoul.cr3);
state.cr4.charge(seoul.cr4);
mtr &= ~MTD_CR;
}
typedef Genode::Vm_state::Segment Segment;
typedef Genode::Vm_state::Range Range;
typedef Genode::Vcpu_state::Segment Segment;
typedef Genode::Vcpu_state::Range Range;
if (mtr & MTD_CS_SS) {
state.cs.value(Segment{seoul.cs.sel, seoul.cs.ar, seoul.cs.limit, seoul.cs.base});
state.ss.value(Segment{seoul.ss.sel, seoul.ss.ar, seoul.ss.limit, seoul.ss.base});
state.cs.charge(Segment { .sel = seoul.cs.sel,
.ar = seoul.cs.ar,
.limit = seoul.cs.limit,
.base = seoul.cs.base });
state.ss.charge(Segment { .sel = seoul.ss.sel,
.ar = seoul.ss.ar,
.limit = seoul.ss.limit,
.base = seoul.ss.base });
mtr &= ~MTD_CS_SS;
}
if (mtr & MTD_DS_ES) {
state.es.value(Segment{seoul.es.sel, seoul.es.ar, seoul.es.limit, seoul.es.base});
state.ds.value(Segment{seoul.ds.sel, seoul.ds.ar, seoul.ds.limit, seoul.ds.base});
state.es.charge(Segment { .sel = seoul.es.sel,
.ar = seoul.es.ar,
.limit = seoul.es.limit,
.base = seoul.es.base });
state.ds.charge(Segment { .sel = seoul.ds.sel,
.ar = seoul.ds.ar,
.limit = seoul.ds.limit,
.base = seoul.ds.base });
mtr &= ~MTD_DS_ES;
}
if (mtr & MTD_FS_GS) {
state.fs.value(Segment{seoul.fs.sel, seoul.fs.ar, seoul.fs.limit, seoul.fs.base});
state.gs.value(Segment{seoul.gs.sel, seoul.gs.ar, seoul.gs.limit, seoul.gs.base});
state.fs.charge(Segment { .sel = seoul.fs.sel,
.ar = seoul.fs.ar,
.limit = seoul.fs.limit,
.base = seoul.fs.base });
state.gs.charge(Segment { .sel = seoul.gs.sel,
.ar = seoul.gs.ar,
.limit = seoul.gs.limit,
.base = seoul.gs.base });
mtr &= ~MTD_FS_GS;
}
if (mtr & MTD_TR) {
state.tr.value(Segment{seoul.tr.sel, seoul.tr.ar, seoul.tr.limit, seoul.tr.base});
state.tr.charge(Segment { .sel = seoul.tr.sel,
.ar = seoul.tr.ar,
.limit = seoul.tr.limit,
.base = seoul.tr.base });
mtr &= ~MTD_TR;
}
if (mtr & MTD_LDTR) {
state.ldtr.value(Segment{seoul.ld.sel, seoul.ld.ar, seoul.ld.limit, seoul.ld.base});
state.ldtr.charge(Segment { .sel = seoul.ld.sel,
.ar = seoul.ld.ar,
.limit = seoul.ld.limit,
.base = seoul.ld.base });
mtr &= ~MTD_LDTR;
}
if (mtr & MTD_GDTR) {
state.gdtr.value(Range{seoul.gd.base, seoul.gd.limit});
state.gdtr.charge(Range { .limit = seoul.gd.limit,
.base = seoul.gd.base });
mtr &= ~MTD_GDTR;
}
if (mtr & MTD_IDTR) {
state.idtr.value(Range{seoul.id.base, seoul.id.limit});
state.idtr.charge(Range{ .limit = seoul.id.limit,
.base = seoul.id.base });
mtr &= ~MTD_IDTR;
}
if (mtr & MTD_SYSENTER) {
state.sysenter_cs.value(seoul.sysenter_cs);
state.sysenter_sp.value(seoul.sysenter_esp);
state.sysenter_ip.value(seoul.sysenter_eip);
state.sysenter_cs.charge(seoul.sysenter_cs);
state.sysenter_sp.charge(seoul.sysenter_esp);
state.sysenter_ip.charge(seoul.sysenter_eip);
mtr &= ~MTD_SYSENTER;
}
if (mtr & MTD_QUAL) {
state.qual_primary.value(seoul.qual[0]);
state.qual_secondary.value(seoul.qual[1]);
state.qual_primary.charge(seoul.qual[0]);
state.qual_secondary.charge(seoul.qual[1]);
/* not read by any kernel */
mtr &= ~MTD_QUAL;
}
if (mtr & MTD_CTRL) {
state.ctrl_primary.value(seoul.ctrl[0]);
state.ctrl_secondary.value(seoul.ctrl[1]);
state.ctrl_primary.charge(seoul.ctrl[0]);
state.ctrl_secondary.charge(seoul.ctrl[1]);
mtr &= ~MTD_CTRL;
}
if (mtr & MTD_INJ) {
state.inj_info.value(seoul.inj_info);
state.inj_error.value(seoul.inj_error);
state.inj_info.charge(seoul.inj_info);
state.inj_error.charge(seoul.inj_error);
mtr &= ~MTD_INJ;
}
if (mtr & MTD_STATE) {
state.intr_state.value(seoul.intr_state);
state.actv_state.value(seoul.actv_state);
state.intr_state.charge(seoul.intr_state);
state.actv_state.charge(seoul.actv_state);
mtr &= ~MTD_STATE;
}
if (mtr & MTD_TSC) {
state.tsc.value(seoul.tsc_value);
state.tsc_offset.value(seoul.tsc_off);
state.tsc.charge(seoul.tsc_value);
state.tsc_offset.charge(seoul.tsc_off);
mtr &= ~MTD_TSC;
}
@ -146,15 +172,15 @@ void Seoul::write_vm_state(CpuState &seoul, unsigned mtr, Genode::Vm_state &stat
Genode::error("state transfer incomplete ", Genode::Hex(mtr));
}
unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
unsigned Seoul::read_vm_state(Genode::Vcpu_state &state, CpuState &seoul)
{
unsigned mtr = 0;
if (state.ax.valid() || state.cx.valid() ||
state.dx.valid() || state.bx.valid()) {
if (state.ax.charged() || state.cx.charged() ||
state.dx.charged() || state.bx.charged()) {
if (!state.ax.valid() || !state.cx.valid() ||
!state.dx.valid() || !state.bx.valid())
if (!state.ax.charged() || !state.cx.charged() ||
!state.dx.charged() || !state.bx.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_GPR_ACDB;
@ -165,9 +191,9 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.rbx = state.bx.value();
}
if (state.bp.valid() || state.di.valid() || state.si.valid()) {
if (state.bp.charged() || state.di.charged() || state.si.charged()) {
if (!state.bp.valid() || !state.di.valid() || !state.si.valid())
if (!state.bp.charged() || !state.di.charged() || !state.si.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_GPR_BSD;
@ -176,18 +202,18 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.rbpx = state.bp.value();
}
if (state.flags.valid()) {
if (state.flags.charged()) {
mtr |= MTD_RFLAGS;
seoul.rflx = state.flags.value();
}
if (state.sp.valid()) {
if (state.sp.charged()) {
mtr |= MTD_RSP;
seoul.rspx = state.sp.value();
}
if (state.ip.valid() || state.ip_len.valid()) {
if (!state.ip.valid() || !state.ip_len.valid())
if (state.ip.charged() || state.ip_len.charged()) {
if (!state.ip.charged() || !state.ip_len.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_RIP_LEN;
@ -195,22 +221,22 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.inst_len = state.ip_len.value();
}
if (state.dr7.valid()) {
if (state.dr7.charged()) {
mtr |= MTD_DR;
seoul.dr7 = state.dr7.value();
}
#if 0
if (state.r8.valid() || state.r9.valid() ||
state.r10.valid() || state.r11.valid() ||
state.r12.valid() || state.r13.valid() ||
state.r14.valid() || state.r15.valid()) {
if (state.r8.charged() || state.r9.charged() ||
state.r10.charged() || state.r11.charged() ||
state.r12.charged() || state.r13.charged() ||
state.r14.charged() || state.r15.charged()) {
Genode::warning("r8-r15 not supported");
}
#endif
if (state.cr0.valid() || state.cr2.valid() ||
state.cr3.valid() || state.cr4.valid()) {
if (state.cr0.charged() || state.cr2.charged() ||
state.cr3.charged() || state.cr4.charged()) {
mtr |= MTD_CR;
@ -220,8 +246,8 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.cr4 = state.cr4.value();
}
if (state.cs.valid() || state.ss.valid()) {
if (!state.cs.valid() || !state.ss.valid())
if (state.cs.charged() || state.ss.charged()) {
if (!state.cs.charged() || !state.ss.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_CS_SS;
@ -237,8 +263,8 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.ss.base = state.ss.value().base;
}
if (state.es.valid() || state.ds.valid()) {
if (!state.es.valid() || !state.ds.valid())
if (state.es.charged() || state.ds.charged()) {
if (!state.es.charged() || !state.ds.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_DS_ES;
@ -254,8 +280,8 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.ds.base = state.ds.value().base;
}
if (state.fs.valid() || state.gs.valid()) {
if (!state.fs.valid() || !state.gs.valid())
if (state.fs.charged() || state.gs.charged()) {
if (!state.fs.charged() || !state.gs.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_FS_GS;
@ -271,7 +297,7 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.gs.base = state.gs.value().base;
}
if (state.tr.valid()) {
if (state.tr.charged()) {
mtr |= MTD_TR;
seoul.tr.sel = state.tr.value().sel;
seoul.tr.ar = state.tr.value().ar;
@ -279,7 +305,7 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.tr.base = state.tr.value().base;
}
if (state.ldtr.valid()) {
if (state.ldtr.charged()) {
mtr |= MTD_LDTR;
seoul.ld.sel = state.ldtr.value().sel;
seoul.ld.ar = state.ldtr.value().ar;
@ -287,23 +313,23 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.ld.base = state.ldtr.value().base;
}
if (state.gdtr.valid()) {
if (state.gdtr.charged()) {
mtr |= MTD_GDTR;
seoul.gd.limit = state.gdtr.value().limit;
seoul.gd.base = state.gdtr.value().base;
}
if (state.idtr.valid()) {
if (state.idtr.charged()) {
mtr |= MTD_IDTR;
seoul.id.limit = state.idtr.value().limit;
seoul.id.base = state.idtr.value().base;
}
if (state.sysenter_cs.valid() || state.sysenter_sp.valid() ||
state.sysenter_ip.valid()) {
if (state.sysenter_cs.charged() || state.sysenter_sp.charged() ||
state.sysenter_ip.charged()) {
if (!state.sysenter_cs.valid() || !state.sysenter_sp.valid() ||
!state.sysenter_ip.valid())
if (!state.sysenter_cs.charged() || !state.sysenter_sp.charged() ||
!state.sysenter_ip.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_SYSENTER;
@ -313,8 +339,8 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.sysenter_eip = state.sysenter_ip.value();
}
if (state.ctrl_primary.valid() || state.ctrl_secondary.valid()) {
if (!state.ctrl_primary.valid() || !state.ctrl_secondary.valid())
if (state.ctrl_primary.charged() || state.ctrl_secondary.charged()) {
if (!state.ctrl_primary.charged() || !state.ctrl_secondary.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_CTRL;
@ -323,8 +349,8 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.ctrl[1] = state.ctrl_secondary.value();
}
if (state.inj_info.valid() || state.inj_error.valid()) {
if (!state.inj_info.valid() || !state.inj_error.valid())
if (state.inj_info.charged() || state.inj_error.charged()) {
if (!state.inj_info.charged() || !state.inj_error.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_INJ;
@ -333,8 +359,8 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.inj_error = state.inj_error.value();
}
if (state.intr_state.valid() || state.actv_state.valid()) {
if (!state.intr_state.valid() || !state.actv_state.valid())
if (state.intr_state.charged() || state.actv_state.charged()) {
if (!state.intr_state.charged() || !state.actv_state.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_STATE;
@ -343,8 +369,8 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.actv_state = state.actv_state.value();
}
if (state.tsc.valid() || state.tsc_offset.valid()) {
if (!state.tsc.valid() || !state.tsc_offset.valid())
if (state.tsc.charged() || state.tsc_offset.charged()) {
if (!state.tsc.charged() || !state.tsc_offset.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_TSC;
@ -353,8 +379,8 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.tsc_off = state.tsc_offset.value();
}
if (state.qual_primary.valid() || state.qual_secondary.valid()) {
if (!state.qual_primary.valid() || !state.qual_secondary.valid())
if (state.qual_primary.charged() || state.qual_secondary.charged()) {
if (!state.qual_primary.charged() || !state.qual_secondary.charged())
Genode::warning("missing state ", __LINE__);
mtr |= MTD_QUAL;
@ -363,23 +389,23 @@ unsigned Seoul::read_vm_state(Genode::Vm_state &state, CpuState &seoul)
seoul.qual[1] = state.qual_secondary.value();
}
#if 0
if (state.efer.valid()) {
if (state.efer.charged()) {
Genode::warning("efer not supported by Seoul");
}
if (state.pdpte_0.valid() || state.pdpte_1.valid() ||
state.pdpte_2.valid() || state.pdpte_3.valid()) {
if (state.pdpte_0.charged() || state.pdpte_1.charged() ||
state.pdpte_2.charged() || state.pdpte_3.charged()) {
Genode::warning("pdpte not supported by Seoul");
}
if (state.star.valid() || state.lstar.valid() || state.cstar.valid() ||
state.fmask.valid() || state.kernel_gs_base.valid()) {
if (state.star.charged() || state.lstar.charged() || state.cstar.charged() ||
state.fmask.charged() || state.kernel_gs_base.charged()) {
Genode::warning("star, lstar, cstar, fmask, kernel_gs not supported by Seoul");
}
if (state.tpr.valid() || state.tpr_threshold.valid()) {
if (state.tpr.charged() || state.tpr_threshold.charged()) {
Genode::warning("tpr not supported by Seoul");
}
#endif

View File

@ -14,13 +14,13 @@
#ifndef _STATE_H_
#define _STATE_H_
#include <cpu/vm_state.h>
#include <cpu/vcpu_state.h>
#include <nul/vcpu.h>
namespace Seoul {
void write_vm_state(CpuState &, unsigned mtr, Genode::Vm_state &);
unsigned read_vm_state(Genode::Vm_state &, CpuState &);
void write_vm_state(CpuState &, unsigned mtr, Genode::Vcpu_state &);
unsigned read_vm_state(Genode::Vcpu_state &, CpuState &);
}
#endif /* _STATE_H_ */

View File

@ -7,7 +7,7 @@
/*
* Copyright (C) 2006-2013 Oracle Corporation
* Copyright (C) 2013-2019 Genode Labs GmbH
* Copyright (C) 2013-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
@ -381,7 +381,7 @@ int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned uOperation,
if (ns_diff > RT_NS_1SEC)
warning(" more than 1 sec vcpu halt ", ns_diff, " ns");
Vcpu_handler *vcpu_handler = lookup_vcpu_handler(idCpu);
::Vcpu_handler *vcpu_handler = lookup_vcpu_handler(idCpu);
Assert(vcpu_handler);
vcpu_handler->halt(ns_diff);
@ -869,6 +869,8 @@ class Pgm_guard
#include "PGMInline.h"
Genode::Vm_connection::Exit_config const Vcpu_handler::_exit_config { /* ... */ };
int Vcpu_handler::map_memory(Genode::Vm_connection &vm_session,
RTGCPHYS const GCPhys, RTGCUINT vbox_fault_reason)
{

View File

@ -2,10 +2,11 @@
* \brief Genode specific VirtualBox SUPLib supplements
* \author Norman Feske
* \author Alexander Boettcher
* \author Christian Helmuth
*/
/*
* Copyright (C) 2013-2019 Genode Labs GmbH
* Copyright (C) 2013-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
@ -33,7 +34,7 @@
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)
static inline bool svm_save_state(Genode::Vcpu_state * state, VM * pVM, PVMCPU pVCpu)
{
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
@ -76,16 +77,18 @@ static inline bool svm_save_state(Genode::Vm_state * state, VM * pVM, PVMCPU pVC
#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});
state->REG.charge(Segment { .sel = pCtx->REG.Sel, \
.ar = sel_ar_conv_to_genode(pCtx->REG.Attr.u), \
.limit = pCtx->REG.u32Limit, \
.base = pCtx->REG.u64Base });
static inline bool svm_load_state(Genode::Vm_state * state, VM * pVM, PVMCPU pVCpu)
static inline bool svm_load_state(Genode::Vcpu_state * state, VM * pVM, PVMCPU pVCpu)
{
typedef Genode::Vm_state::Segment Segment;
typedef Genode::Vcpu_state::Segment Segment;
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
state->efer.value(state->efer.value() | MSR_K6_EFER_SVME);
state->efer.charge(state->efer.value() | MSR_K6_EFER_SVME);
GENODE_WRITE_SELREG(es);
GENODE_WRITE_SELREG(ds);

View File

@ -6,7 +6,7 @@
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
* Copyright (C) 2013-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
@ -20,8 +20,7 @@
#include <rom_session/connection.h>
#include <timer_session/connection.h>
#include <vm_session/connection.h>
#include <cpu/vm_state.h>
#include <cpu/vcpu_state.h>
/* VirtualBox includes */
#include "PGMInternal.h" /* enable access to pgm.s.* */
@ -65,10 +64,12 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
{
protected:
static Genode::Vm_connection::Exit_config const _exit_config;
Genode::Entrypoint _ep;
Genode::Blockade _blockade_emt { };
Genode::Semaphore _sem_handler;
Genode::Vm_state *_state { nullptr };
Genode::Vcpu_state *_state { nullptr };
pthread_cond_t _cond_wait;
pthread_mutex_t _mutex;
@ -158,13 +159,14 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
void switch_to_hw(PCPUMCTX pCtx)
{
using Genode::Vcpu_state;
again:
/* write FPU state */
_state->fpu.value([&] (uint8_t *fpu, size_t const size) {
if (size < sizeof(X86FXSTATE))
Genode::error("fpu state too small");
Genode::memcpy(fpu, pCtx->pXStateR3, sizeof(X86FXSTATE));
AssertCompile(sizeof(Vcpu_state::Fpu::State) >= sizeof(X86FXSTATE));
_state->fpu.charge([&] (Vcpu_state::Fpu::State &fpu) {
::memcpy(&fpu, pCtx->pXStateR3, sizeof(X86FXSTATE));
});
Assert(_vm_state == IRQ_WIN || _vm_state == PAUSED || _vm_state == NPT_EPT);
@ -180,14 +182,12 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
_next_state = RUN;
/* write FPU state of vCPU to pCtx */
_state->fpu.value([&] (uint8_t *fpu, size_t const size) {
if (size < sizeof(X86FXSTATE))
Genode::error("fpu state too small");
Genode::memcpy(pCtx->pXStateR3, fpu, sizeof(X86FXSTATE));
_state->fpu.with_state([&] (Vcpu_state::Fpu::State const &fpu) {
::memcpy(pCtx->pXStateR3, &fpu, sizeof(X86FXSTATE));
});
if (_vm_state == IRQ_WIN) {
*_state = Genode::Vm_state {}; /* reset */
_state->discharge();
_irq_window_pthread();
goto again;
} else
@ -202,7 +202,7 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
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 */
_state->discharge();
goto again;
}
}
@ -267,8 +267,8 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
/* 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());
_state->discharge();
_state->inj_info.charge(_state->inj_info.value());
_irq_win = true;
return /* no-wait */ false;
}
@ -279,48 +279,50 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
inline bool vbox_to_state(VM *pVM, PVMCPU pVCpu)
{
typedef Genode::Vm_state::Range Range;
typedef Genode::Vcpu_state::Range Range;
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
_state->ip.value(pCtx->rip);
_state->sp.value(pCtx->rsp);
_state->ip.charge(pCtx->rip);
_state->sp.charge(pCtx->rsp);
_state->ax.value(pCtx->rax);
_state->bx.value(pCtx->rbx);
_state->cx.value(pCtx->rcx);
_state->dx.value(pCtx->rdx);
_state->ax.charge(pCtx->rax);
_state->bx.charge(pCtx->rbx);
_state->cx.charge(pCtx->rcx);
_state->dx.charge(pCtx->rdx);
_state->bp.value(pCtx->rbp);
_state->si.value(pCtx->rsi);
_state->di.value(pCtx->rdi);
_state->bp.charge(pCtx->rbp);
_state->si.charge(pCtx->rsi);
_state->di.charge(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->r8.charge(pCtx->r8);
_state->r9.charge(pCtx->r9);
_state->r10.charge(pCtx->r10);
_state->r11.charge(pCtx->r11);
_state->r12.charge(pCtx->r12);
_state->r13.charge(pCtx->r13);
_state->r14.charge(pCtx->r14);
_state->r15.charge(pCtx->r15);
_state->flags.value(pCtx->rflags.u);
_state->flags.charge(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->sysenter_cs.charge(pCtx->SysEnter.cs);
_state->sysenter_sp.charge(pCtx->SysEnter.esp);
_state->sysenter_ip.charge(pCtx->SysEnter.eip);
_state->dr7.value(pCtx->dr[7]);
_state->dr7.charge(pCtx->dr[7]);
_state->cr0.value(pCtx->cr0);
_state->cr2.value(pCtx->cr2);
_state->cr3.value(pCtx->cr3);
_state->cr4.value(pCtx->cr4);
_state->cr0.charge(pCtx->cr0);
_state->cr2.charge(pCtx->cr2);
_state->cr3.charge(pCtx->cr3);
_state->cr4.charge(pCtx->cr4);
_state->idtr.value(Range{pCtx->idtr.pIdt, pCtx->idtr.cbIdt});
_state->gdtr.value(Range{pCtx->gdtr.pGdt, pCtx->gdtr.cbGdt});
_state->idtr.charge(Range { .limit = pCtx->idtr.cbIdt,
.base = pCtx->idtr.pIdt });
_state->gdtr.charge(Range { .limit = pCtx->gdtr.cbGdt,
.base = pCtx->gdtr.pGdt });
_state->efer.value(CPUMGetGuestEFER(pVCpu));
_state->efer.charge(CPUMGetGuestEFER(pVCpu));
/*
* Update the PDPTE registers if necessary
@ -337,17 +339,17 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
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->pdpte_0.charge(pdpte[0]);
_state->pdpte_1.charge(pdpte[1]);
_state->pdpte_2.charge(pdpte[2]);
_state->pdpte_3.charge(pdpte[3]);
}
_state->star.value(pCtx->msrSTAR);
_state->lstar.value(pCtx->msrLSTAR);
_state->cstar.value(pCtx->msrCSTAR);
_state->fmask.value(pCtx->msrSFMASK);
_state->kernel_gs_base.value(pCtx->msrKERNELGSBASE);
_state->star.charge(pCtx->msrSTAR);
_state->lstar.charge(pCtx->msrLSTAR);
_state->cstar.charge(pCtx->msrCSTAR);
_state->fmask.charge(pCtx->msrSFMASK);
_state->kernel_gs_base.charge(pCtx->msrKERNELGSBASE);
/* from HMVMXR0.cpp */
bool interrupt_pending = false;
@ -355,16 +357,16 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
uint8_t pending_interrupt = 0;
PDMApicGetTPR(pVCpu, &tpr, &interrupt_pending, &pending_interrupt);
_state->tpr.value(tpr);
_state->tpr_threshold.value(0);
_state->tpr.charge(tpr);
_state->tpr_threshold.charge(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);
_state->tpr_threshold.charge(pending_priority);
else
_state->tpr_threshold.value(tpr_priority);
_state->tpr_threshold.charge(tpr_priority);
}
return true;
@ -490,7 +492,7 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
_irq_request++;
unsigned const vector = 0;
_state->inj_info.value(REQ_IRQWIN_EXIT | vector);
_state->inj_info.charge(REQ_IRQWIN_EXIT | vector);
return true;
}
@ -556,7 +558,7 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
if (!TRPMHasTrap(pVCpu)) {
_irq_drop++;
/* happens if PDMApicSetTPR (see above) mask IRQ */
_state->inj_info.value(IRQ_INJ_NONE);
_state->inj_info.charge(IRQ_INJ_NONE);
Genode::error("virq window pthread aaaaaaa while loop");
return;
}
@ -595,8 +597,8 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
Event.n.u3Type = SVM_EVENT_EXTERNAL_IRQ;
_state->inj_info.value(Event.u);
_state->inj_error.value(Event.n.u32ErrorCode);
_state->inj_info.charge(Event.u);
_state->inj_error.charge(Event.n.u32ErrorCode);
_last_inj_info = _state->inj_info.value();
_last_inj_error = _state->inj_error.value();
@ -673,8 +675,8 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
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 bool hw_load_state(Genode::Vcpu_state *, VM *, PVMCPU) = 0;
virtual bool hw_save_state(Genode::Vcpu_state *, VM *, PVMCPU) = 0;
virtual int vm_exit_requires_instruction_emulation(PCPUMCTX) = 0;
virtual void pause_vm() = 0;
@ -791,11 +793,11 @@ class Vcpu_handler : public Genode::List<Vcpu_handler>::Element
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]);
_state->inj_info.charge(IRQ_INJ_NONE);
_state->intr_state.charge(next_utcb.intr_state);
_state->actv_state.charge(ACTIVITY_STATE_ACTIVE);
_state->ctrl_primary.charge(next_utcb.ctrl[0]);
_state->ctrl_secondary.charge(next_utcb.ctrl[1]);
/* Transfer vCPU state from vbox to Genode format */
if (!vbox_to_state(pVM, pVCpu) ||

View File

@ -1,11 +1,13 @@
/*
* \brief Genode specific VirtualBox SUPLib supplements
* \author Alexander Boettcher
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-18
*/
/*
* Copyright (C) 2013-2019 Genode Labs GmbH
* Copyright (C) 2013-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
@ -14,11 +16,8 @@
#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 includes */
#include <vm_session/handler.h>
/* Genode's VirtualBox includes */
#include "vcpu.h"
@ -28,12 +27,10 @@ class Vcpu_handler_svm : public Vcpu_handler
{
private:
Genode::Vm_handler<Vcpu_handler_svm> _handler;
Genode::Vcpu_handler<Vcpu_handler_svm> _handler;
Genode::Vm_connection &_vm_session;
Genode::Vm_session_client::Vcpu_id _vcpu;
Genode::Attached_dataspace _state_ds;
Genode::Vm_connection &_vm_connection;
Genode::Vm_connection::Vcpu _vcpu;
void _svm_default() { _default_handler(); }
void _svm_vintr() { _irq_window(); }
@ -82,7 +79,7 @@ class Vcpu_handler_svm : public Vcpu_handler
| SVM_CTRL2_INTERCEPT_MWAIT;
}
void _handle_vm_exception()
void _handle_exit()
{
unsigned const exit = _state->exit_reason;
bool recall_wait = true;
@ -141,37 +138,13 @@ class Vcpu_handler_svm : public Vcpu_handler
pause_vm(); /* cause pause exit */
}
void run_vm() { _vm_session.run(_vcpu); }
void pause_vm() { _vm_session.pause(_vcpu); }
void run_vm() { _vcpu.run(); }
void pause_vm() { _vcpu.pause(); }
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_INVLPGA:
case SVM_EXIT_IOIO:
case SVM_EXIT_VINTR:
case SVM_EXIT_READ_CR0 ... SVM_EXIT_WRITE_CR15:
case SVM_EXIT_RDTSC:
case SVM_EXIT_RDTSCP:
case SVM_EXIT_MSR:
case SVM_NPT:
case SVM_EXIT_HLT:
case SVM_EXIT_CPUID:
case SVM_EXIT_WBINVD:
case VCPU_STARTUP:
/* todo - touch all members */
Genode::memset(&state, ~0U, sizeof(state));
break;
default:
break;
}
return map_memory(_vm_connection, gp_attach_addr, vbox_errorcode);
}
public:
@ -179,32 +152,28 @@ class Vcpu_handler_svm : public Vcpu_handler
Vcpu_handler_svm(Genode::Env &env, size_t stack_size,
Genode::Affinity::Location location,
unsigned int cpu_id,
Genode::Vm_connection &vm_session,
Genode::Vm_connection &vm_connection,
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))
_handler(_ep, *this, &Vcpu_handler_svm::_handle_exit),
_vm_connection(vm_connection),
_vcpu(_vm_connection, alloc, _handler, _exit_config)
{
_state = _state_ds.local_addr<Genode::Vm_state>();
/* get state of vcpu */
_state = &_vcpu.state();
_vm_session.run(_vcpu);
_vcpu.run();
/* sync with initial startup exception */
_blockade_emt.block();
}
bool hw_save_state(Genode::Vm_state *state, VM * pVM, PVMCPU pVCpu) {
bool hw_save_state(Genode::Vcpu_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) {
bool hw_load_state(Genode::Vcpu_state *state, VM * pVM, PVMCPU pVCpu) {
return svm_load_state(state, pVM, pVCpu);
}

View File

@ -3,10 +3,11 @@
* \author Alexander Boettcher
* \author Norman Feske
* \author Christian Helmuth
* \date 2013-11-18
*/
/*
* Copyright (C) 2013-2019 Genode Labs GmbH
* Copyright (C) 2013-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
@ -15,11 +16,8 @@
#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>
/* Genode includes */
#include <vm_session/handler.h>
/* libc includes */
#include <stdlib.h>
@ -36,12 +34,10 @@ class Vcpu_handler_vmx : public Vcpu_handler
{
private:
Genode::Vm_handler<Vcpu_handler_vmx> _handler;
Genode::Vcpu_handler<Vcpu_handler_vmx> _handler;
Genode::Vm_connection &_vm_session;
Genode::Vm_session_client::Vcpu_id _vcpu;
Genode::Attached_dataspace _state_ds;
Genode::Vm_connection &_vm_connection;
Genode::Vm_connection::Vcpu _vcpu;
template <unsigned X>
void _vmx_ept()
@ -124,7 +120,7 @@ class Vcpu_handler_vmx : public Vcpu_handler
void _vmx_mov_crx() { _default_handler(); return; }
void _handle_vm_exception()
void _handle_exit()
{
unsigned const exit = _state->exit_reason;
bool recall_wait = true;
@ -183,46 +179,13 @@ class Vcpu_handler_vmx : public Vcpu_handler
pause_vm(); /* cause pause exit */
}
void run_vm() { _vm_session.run(_vcpu); }
void pause_vm() { _vm_session.pause(_vcpu); }
void run_vm() { _vcpu.run(); }
void pause_vm() { _vcpu.pause(); }
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 VMX_EXIT_XSETBV:
case VCPU_STARTUP:
case RECALL:
/* todo - touch all members */
Genode::memset(&state, ~0U, sizeof(state));
break;
default:
break;
}
return map_memory(_vm_connection, gp_attach_addr, vbox_errorcode);
}
public:
@ -230,32 +193,28 @@ class Vcpu_handler_vmx : public Vcpu_handler
Vcpu_handler_vmx(Genode::Env &env, size_t stack_size,
Genode::Affinity::Location location,
unsigned int cpu_id,
Genode::Vm_connection &vm_session,
Genode::Vm_connection &vm_connection,
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))
Vcpu_handler(env, stack_size, location, cpu_id),
_handler(_ep, *this, &Vcpu_handler_vmx::_handle_exit),
_vm_connection(vm_connection),
_vcpu(_vm_connection, alloc, _handler, _exit_config)
{
_state = _state_ds.local_addr<Genode::Vm_state>();
/* get state of vcpu */
_state = &_vcpu.state();
_vm_session.run(_vcpu);
_vcpu.run();
/* sync with initial startup exception */
_blockade_emt.block();
}
bool hw_save_state(Genode::Vm_state *state, VM * pVM, PVMCPU pVCpu) {
bool hw_save_state(Genode::Vcpu_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) {
bool hw_load_state(Genode::Vcpu_state * state, VM * pVM, PVMCPU pVCpu) {
return vmx_load_state(state, pVM, pVCpu);
}

View File

@ -2,10 +2,11 @@
* \brief Genode specific VirtualBox SUPLib supplements
* \author Norman Feske
* \author Alexander Boettcher
* \author Christian Helmuth
*/
/*
* Copyright (C) 2013-2019 Genode Labs GmbH
* Copyright (C) 2013-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
@ -30,7 +31,7 @@
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)
static inline bool vmx_save_state(Genode::Vcpu_state * state, VM * pVM, PVMCPU pVCpu)
{
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
@ -62,16 +63,16 @@ 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});
state->REG.charge( Segment{ .sel = pCtx->REG.Sel, \
.ar = sel_ar_conv_to_genode(pCtx->REG.Attr.u ? : VMCS_SEG_UNUSABLE), \
.limit = pCtx->REG.u32Limit, \
.base = pCtx->REG.u64Base });
static inline bool vmx_load_state(Genode::Vm_state * state, VM * pVM, PVMCPU pVCpu)
static inline bool vmx_load_state(Genode::Vcpu_state * state, VM * pVM, PVMCPU pVCpu)
{
PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
typedef Genode::Vm_state::Segment Segment;
typedef Genode::Vcpu_state::Segment Segment;
GENODE_WRITE_SELREG(es);
GENODE_WRITE_SELREG(ds);
@ -84,16 +85,22 @@ static inline bool vmx_load_state(Genode::Vm_state * state, VM * pVM, PVMCPU pVC
/* ldtr */
if (pCtx->ldtr.Sel == 0) {
state->ldtr.value(Segment{0, sel_ar_conv_to_genode(0x82), 0, 0});
state->ldtr.charge(Segment { .sel = 0,
.ar = sel_ar_conv_to_genode(0x82),
.limit = 0,
.base = 0 });
} else {
state->ldtr.value(Segment{pCtx->ldtr.Sel,
sel_ar_conv_to_genode(pCtx->ldtr.Attr.u),
pCtx->ldtr.u32Limit, pCtx->ldtr.u64Base});
state->ldtr.charge(Segment { .sel = pCtx->ldtr.Sel,
.ar = sel_ar_conv_to_genode(pCtx->ldtr.Attr.u),
.limit = pCtx->ldtr.u32Limit,
.base = 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});
state->tr.charge(Segment { .sel = pCtx->tr.Sel,
.ar = sel_ar_conv_to_genode(pCtx->tr.Attr.u),
.limit = pCtx->tr.u32Limit,
.base = pCtx->tr.u64Base });
return true;
}