mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-20 06:07:59 +00:00
parent
6e8728f2d3
commit
219809ffed
@ -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
|
||||
|
@ -1,3 +1,5 @@
|
||||
LIBS += timeout
|
||||
|
||||
vpath vm.cc $(REP_DIR)/src/lib/base/x86
|
||||
|
||||
include $(REP_DIR)/lib/mk/base-foc.inc
|
||||
|
@ -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 &);
|
||||
|
||||
|
||||
/************************
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
31
repos/base-foc/src/include/foc_native_vcpu/foc_native_vcpu.h
Normal file
31
repos/base-foc/src/include/foc_native_vcpu/foc_native_vcpu.h
Normal 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_ */
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
29
repos/base-hw/src/include/hw_native_vcpu/hw_native_vcpu.h
Normal file
29
repos/base-hw/src/include/hw_native_vcpu/hw_native_vcpu.h
Normal 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_ */
|
91
repos/base-hw/src/lib/base/vm.cc
Normal file
91
repos/base-hw/src/lib/base/vm.cc
Normal 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))
|
||||
{ }
|
@ -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()
|
||||
{ }
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_ */
|
796
repos/base-nova/src/lib/base/vm.cc
Normal file
796
repos/base-nova/src/lib/base/vm.cc
Normal 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))
|
||||
{ }
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
@ -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
|
||||
|
@ -1,3 +1,5 @@
|
||||
LIBS += timeout
|
||||
|
||||
vpath vm.cc $(REP_DIR)/src/lib/base/x86
|
||||
|
||||
include $(REP_DIR)/lib/mk/base-sel4.inc
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
|
@ -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_ */
|
@ -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()))
|
||||
{ }
|
231
repos/base/include/spec/x86/cpu/vcpu_state.h
Normal file
231
repos/base/include/spec/x86/cpu/vcpu_state.h
Normal 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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
||||
|
@ -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_ */
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
46
repos/base/src/lib/base/vm.cc
Normal file
46
repos/base/src/lib/base/vm.cc
Normal 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)
|
||||
{
|
||||
}
|
@ -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()
|
||||
{ }
|
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -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() };
|
||||
|
@ -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)
|
||||
{ }
|
||||
|
@ -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)
|
||||
{ }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) { }
|
||||
|
@ -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;
|
||||
|
||||
|
||||
/***********************
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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 ...");
|
||||
|
@ -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); }
|
||||
|
@ -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\" />"
|
||||
|
||||
|
@ -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">
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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_ */
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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) ||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user