base: refactor VM/vCPU API

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

View File

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

View File

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

View File

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

View File

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