vm_session: track dataspaces used by attach

Track the dataspaces used by attach and add handling of flushing VM space
when dataspace gets destroyed (not triggered via the vm_session interface).

Issue #3111
This commit is contained in:
Alexander Boettcher 2019-04-02 17:41:30 +02:00 committed by Christian Helmuth
parent 169c51d50d
commit 450c8dc149
23 changed files with 556 additions and 216 deletions

View File

@ -39,6 +39,7 @@ SRC_CC += stack_area.cc \
thread_start.cc \
trace_session_component.cc \
vm_session_component.cc \
vm_session_common.cc \
heartbeat.cc
INC_DIR += $(REP_DIR)/src/core/include \
@ -72,5 +73,6 @@ vpath core_rpc_cap_alloc.cc $(GEN_CORE_DIR)
vpath core_region_map.cc $(GEN_CORE_DIR)
vpath platform_rom_modules.cc $(GEN_CORE_DIR)
vpath heartbeat.cc $(GEN_CORE_DIR)
vpath vm_session_common.cc $(GEN_CORE_DIR)
vpath %.cc $(REP_DIR)/src/core
vpath %.cc $(REP_DIR)/src/lib/base

View File

@ -21,55 +21,12 @@
#include <vm_session/vm_session.h>
/* Core includes */
#include <dataspace_component.h>
#include <cap_mapping.h>
#include <dataspace_component.h>
#include <region_map_component.h>
namespace Genode { class Vm_session_component; struct Vcpu; }
class Genode::Vm_session_component
:
private Ram_quota_guard,
private Cap_quota_guard,
public Rpc_object<Vm_session, Vm_session_component>
{
private:
Rpc_entrypoint &_ep;
Constrained_ram_allocator _constrained_md_ram_alloc;
Sliced_heap _sliced_heap;
List<Vcpu> _vcpus { };
Cap_mapping _task_vcpu { true };
unsigned _id_alloc { 0 };
protected:
Ram_quota_guard &_ram_quota_guard() { return *this; }
Cap_quota_guard &_cap_quota_guard() { return *this; }
public:
using Ram_quota_guard::upgrade;
using Cap_quota_guard::upgrade;
Vm_session_component(Rpc_entrypoint &, Resources, Label const &,
Diag, Ram_allocator &ram, Region_map &);
~Vm_session_component();
/**************************
** Vm session interface **
**************************/
Dataspace_capability _cpu_state(Vcpu_id);
void _exception_handler(Signal_context_capability, Vcpu_id) { }
void _run(Vcpu_id) { }
void _pause(Vcpu_id) { }
void attach(Dataspace_capability, addr_t) override;
void attach_pic(addr_t) override { }
void detach(addr_t, size_t) override { }
void _create_vcpu(Thread_capability);
};
struct Genode::Vcpu : Genode::List<Vcpu>::Element
{
private:
@ -92,4 +49,66 @@ struct Genode::Vcpu : Genode::List<Vcpu>::Element
Cap_mapping &recall_cap() { return _recall; }
};
class Genode::Vm_session_component
:
private Ram_quota_guard,
private Cap_quota_guard,
public Rpc_object<Vm_session, Vm_session_component>,
public Region_map_detach
{
private:
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 _sliced_heap;
Slab _slab { max(sizeof(Vcpu), sizeof(Rm_region)),
4096 - Sliced_heap::meta_data_size(),
nullptr, &_sliced_heap };
Avl_region _map { &_slab };
List<Vcpu> _vcpus { };
Cap_mapping _task_vcpu { true };
unsigned _id_alloc { 0 };
void _attach_vm_memory(Dataspace_component &, addr_t, bool, bool);
void _detach_vm_memory(addr_t, size_t);
protected:
Ram_quota_guard &_ram_quota_guard() { return *this; }
Cap_quota_guard &_cap_quota_guard() { return *this; }
public:
using Ram_quota_guard::upgrade;
using Cap_quota_guard::upgrade;
Vm_session_component(Rpc_entrypoint &, Resources, Label const &,
Diag, Ram_allocator &ram, Region_map &);
~Vm_session_component();
/*********************************
** Region_map_detach interface **
*********************************/
void detach(Region_map::Local_addr) override;
void unmap_region(addr_t, size_t) override;
/**************************
** Vm session interface **
**************************/
Dataspace_capability _cpu_state(Vcpu_id);
void _exception_handler(Signal_context_capability, Vcpu_id) { }
void _run(Vcpu_id) { }
void _pause(Vcpu_id) { }
void attach(Dataspace_capability, addr_t) override;
void attach_pic(addr_t) override { }
void detach(addr_t, size_t) override;
void _create_vcpu(Thread_capability);
};
#endif /* _CORE__VM_SESSION_COMPONENT_H_ */

View File

@ -47,19 +47,30 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
l4_msgtag_t msg = l4_factory_create_vm(L4_BASE_FACTORY_CAP,
_task_vcpu.local.data()->kcap());
if (l4_error(msg)) {
_cap_quota_guard().replenish(Cap_quota{1});
Genode::error("create_vm failed ", l4_error(msg));
throw 1;
throw Service_denied();
}
/* configure managed VM area */
_map.add_range(0, 0UL - 0x1000);
_map.add_range(0UL - 0x1000, 0x1000);
}
Vm_session_component::~Vm_session_component()
{
_cap_quota_guard().replenish(Cap_quota{1});
for (;Vcpu * vcpu = _vcpus.first();) {
_vcpus.remove(vcpu);
destroy(_sliced_heap, vcpu);
destroy(_slab, vcpu);
}
/* detach all regions */
while (true) {
addr_t out_addr = 0;
if (!_map.any_block_addr(&out_addr))
break;
detach(out_addr);
}
}
@ -106,7 +117,7 @@ void Vm_session_component::_create_vcpu(Thread_capability cap)
/* allocate vCPU object */
Vcpu * vcpu = nullptr;
try {
vcpu = new (_sliced_heap) Vcpu(_constrained_md_ram_alloc,
vcpu = new (_slab) Vcpu(_constrained_md_ram_alloc,
_cap_quota_guard(),
Vcpu_id {_id_alloc});
@ -122,12 +133,12 @@ void Vm_session_component::_create_vcpu(Thread_capability cap)
});
} catch (int) {
if (vcpu)
destroy(_sliced_heap, vcpu);
destroy(_slab, vcpu);
return;
} catch (...) {
if (vcpu)
destroy(_sliced_heap, vcpu);
destroy(_slab, vcpu);
throw;
}
@ -151,41 +162,53 @@ Dataspace_capability Vm_session_component::_cpu_state(Vcpu_id const vcpu_id)
return Dataspace_capability();
}
void Vm_session_component::attach(Dataspace_capability cap, addr_t guest_phys)
void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc,
addr_t const guest_phys,
bool const executable,
bool const writeable)
{
if (!cap.valid())
throw Invalid_dataspace();
Flexpage_iterator flex(dsc.phys_addr(), dsc.size(),
guest_phys, dsc.size(), guest_phys);
/* check dataspace validity */
_ep.apply(cap, [&] (Dataspace_component *ptr) {
if (!ptr)
throw Invalid_dataspace();
using namespace Fiasco;
Dataspace_component &dsc = *ptr;
uint8_t flags = L4_FPAGE_RO;
if (dsc.writable() && writeable)
if (executable)
flags = L4_FPAGE_RWX;
else
flags = L4_FPAGE_RW;
else
if (executable)
flags = L4_FPAGE_RX;
/* unsupported - deny otherwise arbitrary physical memory can be mapped to a VM */
if (dsc.managed())
throw Invalid_dataspace();
Flexpage page = flex.page();
while (page.valid()) {
l4_fpage_t fp = l4_fpage(page.addr, page.log2_order, flags);
l4_msgtag_t msg = l4_task_map(_task_vcpu.local.data()->kcap(),
L4_BASE_TASK_CAP, fp,
l4_map_obj_control(page.hotspot,
L4_MAP_ITEM_MAP));
Flexpage_iterator flex(dsc.phys_addr(), dsc.size(),
guest_phys, dsc.size(), guest_phys);
if (l4_error(msg))
Genode::error("task map failed ", l4_error(msg));
page = flex.page();
}
}
void Vm_session_component::_detach_vm_memory(addr_t guest_phys, size_t size)
{
Flexpage_iterator flex(guest_phys, size, guest_phys, size, 0);
Flexpage page = flex.page();
while (page.valid()) {
using namespace Fiasco;
uint8_t const flags = dsc.writable() ? L4_FPAGE_RWX : L4_FPAGE_RX;
l4_task_unmap(_task_vcpu.local.data()->kcap(),
l4_fpage(page.addr, page.log2_order, L4_FPAGE_RWX),
L4_FP_ALL_SPACES);
Flexpage page = flex.page();
while (page.valid()) {
l4_fpage_t fp = l4_fpage(page.addr, page.log2_order, flags);
l4_msgtag_t msg = l4_task_map(_task_vcpu.local.data()->kcap(),
L4_BASE_TASK_CAP, fp,
l4_map_obj_control(page.hotspot,
L4_MAP_ITEM_MAP));
if (l4_error(msg))
Genode::error("task map failed ", l4_error(msg));
page = flex.page();
}
});
page = flex.page();
}
}

View File

@ -191,6 +191,7 @@ struct Vcpu : Genode::Thread
Signal_context_capability _signal;
Semaphore _wake_up { 0 };
Semaphore &_handler_ready;
Allocator &_alloc;
Vm_session_client::Vcpu_id _id;
addr_t _state { 0 };
addr_t _task { 0 };
@ -1137,12 +1138,16 @@ struct Vcpu : Genode::Thread
Vcpu(Env &env, Signal_context_capability &cap,
Semaphore &handler_ready,
Vm_session_client::Vcpu_id &id, enum Virt type)
Vm_session_client::Vcpu_id &id, enum Virt type,
Allocator &alloc)
:
Thread(env, "vcpu_thread", STACK_SIZE), _signal(cap),
_handler_ready(handler_ready), _id(id), _vm_type(type)
_handler_ready(handler_ready), _alloc(alloc),
_id(id), _vm_type(type)
{ }
Allocator &allocator() const { return _alloc; }
bool match(Vm_session_client::Vcpu_id id) { return id.id == _id.id; }
Genode::Vm_session_client::Vcpu_id id() const { return _id; }
@ -1218,7 +1223,8 @@ Vm_session_client::create_vcpu(Allocator &alloc, Env &env,
/* create thread that switches modes between thread/cpu */
Vcpu * vcpu = new (alloc) Registered<Vcpu>(vcpus, env, handler._cap,
handler._done, id, vm_type);
handler._done, id, vm_type,
alloc);
try {
/* now it gets actually valid - vcpu->cap() becomes valid */
@ -1271,3 +1277,11 @@ Dataspace_capability Vm_session_client::cpu_state(Vcpu_id vcpu_id)
return cap;
}
Vm_session::~Vm_session()
{
vcpus.for_each([&] (Vcpu &vc) {
Allocator &alloc = vc.allocator();
destroy(alloc, &vc);
});
}

View File

@ -15,6 +15,7 @@ SRC_CC += kernel/vm_thread_on.cc
SRC_CC += spec/arm_v7/virtualization/kernel/vm.cc
SRC_CC += spec/arm_v7/vm_session_component.cc
SRC_CC += spec/arm_v7/virtualization/vm_session_component.cc
SRC_CC += vm_session_common.cc
# add assembly sources
SRC_S += spec/arm_v7/virtualization/exception_vector.s

View File

@ -16,6 +16,7 @@ SRC_CC += kernel/vm_thread_on.cc
SRC_CC += spec/arm_v7/virtualization/kernel/vm.cc
SRC_CC += spec/arm_v7/vm_session_component.cc
SRC_CC += spec/arm_v7/virtualization/vm_session_component.cc
SRC_CC += vm_session_common.cc
# add assembly sources
SRC_S += spec/arm_v7/virtualization/exception_vector.s

View File

@ -53,18 +53,12 @@ void Vm_session_component::_attach(addr_t phys_addr, addr_t vm_addr, size_t size
}
void Vm_session_component::attach(Dataspace_capability ds_cap, addr_t vm_addr)
void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc,
addr_t const vm_addr,
bool const /* executable */,
bool const /* writeable */)
{
/* check dataspace validity */
_ds_ep->apply(ds_cap, [&] (Dataspace_component *dsc) {
if (!dsc) throw Invalid_dataspace();
/* unsupported - deny otherwise arbitrary physical memory can be mapped to a VM */
if (dsc->managed())
throw Invalid_dataspace();
_attach(dsc->phys_addr(), vm_addr, dsc->size());
});
_attach(dsc.phys_addr(), vm_addr, dsc.size());
}
@ -75,9 +69,10 @@ void Vm_session_component::attach_pic(addr_t vm_addr)
}
void Vm_session_component::detach(addr_t vm_addr, size_t size) {
_table.remove_translation(vm_addr, size, _table_array.alloc()); }
void Vm_session_component::_detach_vm_memory(addr_t vm_addr, size_t size)
{
_table.remove_translation(vm_addr, size, _table_array.alloc());
}
void * Vm_session_component::_alloc_table()
@ -102,8 +97,9 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ds_ep,
:
Ram_quota_guard(resources.ram_quota),
Cap_quota_guard(resources.cap_quota),
_ds_ep(&ds_ep),
_ep(ds_ep),
_constrained_md_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()),
_sliced_heap(_constrained_md_ram_alloc, region_map),
_region_map(region_map),
_table(*construct_at<Table>(_alloc_table())),
_table_array(*(new (cma()) Array([this] (void * virt) {
@ -117,11 +113,25 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ds_ep,
_constrained_md_ram_alloc.free(_ds_cap);
throw;
}
/* configure managed VM area */
_map.add_range(0, 0UL - 0x1000);
_map.add_range(0UL - 0x1000, 0x1000);
}
Vm_session_component::~Vm_session_component()
{
/* detach all regions */
while (true) {
addr_t out_addr = 0;
if (!_map.any_block_addr(&out_addr))
break;
detach(out_addr);
}
/* free region in allocator */
if (_ds_cap.valid()) {
_region_map.detach(_ds_addr);

View File

@ -16,6 +16,7 @@
/* Genode includes */
#include <base/allocator.h>
#include <base/allocator_avl.h>
#include <base/session_object.h>
#include <vm_session/vm_session.h>
#include <dataspace/capability.h>
@ -23,6 +24,7 @@
/* Core includes */
#include <object.h>
#include <region_map_component.h>
#include <translation_table.h>
#include <kernel/vm.h>
@ -35,10 +37,13 @@ class Genode::Vm_session_component
private Ram_quota_guard,
private Cap_quota_guard,
public Rpc_object<Vm_session, Vm_session_component>,
public Region_map_detach,
private Kernel_object<Kernel::Vm>
{
private:
typedef Allocator_avl_tpl<Rm_region> Avl_region;
/*
* Noncopyable
*/
@ -48,8 +53,10 @@ class Genode::Vm_session_component
using Table = Hw::Level_1_stage_2_translation_table;
using Array = Table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
Rpc_entrypoint *_ds_ep;
Rpc_entrypoint &_ep;
Constrained_ram_allocator _constrained_md_ram_alloc;
Sliced_heap _sliced_heap;
Avl_region _map { &_sliced_heap };
Region_map &_region_map;
Ram_dataspace_capability _ds_cap { };
Region_map::Local_addr _ds_addr { 0 };
@ -64,6 +71,9 @@ class Genode::Vm_session_component
void * _alloc_table();
void _attach(addr_t phys_addr, addr_t vm_addr, size_t size);
void _attach_vm_memory(Dataspace_component &, addr_t, bool, bool);
void _detach_vm_memory(addr_t, size_t);
protected:
Ram_quota_guard &_ram_quota_guard() { return *this; }
@ -79,6 +89,12 @@ class Genode::Vm_session_component
Diag, Ram_allocator &ram, Region_map &);
~Vm_session_component();
/*********************************
** Region_map_detach interface **
*********************************/
void detach(Region_map::Local_addr) override;
void unmap_region(addr_t, size_t) override;
/**************************
** Vm session interface **

View File

@ -42,6 +42,7 @@ SRC_CC += stack_area.cc \
signal_transmitter_noinit.cc \
signal_receiver.cc \
vm_session_component.cc \
vm_session_common.cc \
heartbeat.cc
INC_DIR = $(REP_DIR)/src/core/include \
@ -75,4 +76,5 @@ vpath dump_alloc.cc $(GEN_CORE_DIR)
vpath platform_rom_modules.cc $(GEN_CORE_DIR)
vpath stack_area.cc $(GEN_CORE_DIR)
vpath heartbeat.cc $(GEN_CORE_DIR)
vpath vm_session_common.cc $(GEN_CORE_DIR)
vpath %.cc $(REP_DIR)/src/core

View File

@ -25,10 +25,14 @@ class Genode::Vm_session_component
:
private Ram_quota_guard,
private Cap_quota_guard,
public Rpc_object<Vm_session, Vm_session_component>
public Rpc_object<Vm_session, Vm_session_component>,
public Region_map_detach
{
private:
typedef Constrained_ram_allocator Con_ram_allocator;
typedef Allocator_avl_tpl<Rm_region> Avl_region;
class Vcpu : public List<Vcpu>::Element {
public:
@ -68,12 +72,16 @@ class Genode::Vm_session_component
static addr_t invalid() { return ~0UL; }
};
Rpc_entrypoint &_ep;
Constrained_ram_allocator _constrained_md_ram_alloc;
Sliced_heap _sliced_heap;
addr_t _pd_sel { 0 };
unsigned _id_alloc { 0 };
unsigned _priority;
Rpc_entrypoint &_ep;
Con_ram_allocator _constrained_md_ram_alloc;
Sliced_heap _sliced_heap;
Slab _slab { max(sizeof(Vcpu), sizeof(Rm_region)),
4096 - Sliced_heap::meta_data_size(),
nullptr, &_sliced_heap };
Avl_region _map { &_slab };
addr_t _pd_sel { 0 };
unsigned _id_alloc { 0 };
unsigned _priority;
List<Vcpu> _vcpus { };
@ -85,6 +93,9 @@ class Genode::Vm_session_component
return nullptr;
}
void _attach_vm_memory(Dataspace_component &, addr_t, bool, bool);
void _detach_vm_memory(addr_t, size_t);
protected:
Ram_quota_guard &_ram_quota_guard() { return *this; }
@ -99,6 +110,13 @@ class Genode::Vm_session_component
Diag, Ram_allocator &ram, Region_map &);
~Vm_session_component();
/*********************************
** Region_map_detach interface **
*********************************/
void detach(Region_map::Local_addr) override;
void unmap_region(addr_t, size_t) override;
/**************************
** Vm session interface **
**************************/
@ -110,7 +128,7 @@ class Genode::Vm_session_component
void _pause(Vcpu_id) { }
void attach(Dataspace_capability, addr_t) override;
void attach_pic(addr_t) override {}
void detach(addr_t, size_t) override { }
void detach(addr_t, size_t) override;
void _create_vcpu(Thread_capability);
};

View File

@ -150,9 +150,9 @@ void Vm_session_component::_create_vcpu(Thread_capability cap)
return;
/* allocate vCPU object */
Vcpu &vcpu = *new (_sliced_heap) Vcpu(_constrained_md_ram_alloc,
_cap_quota_guard(),
Vcpu_id {_id_alloc});
Vcpu &vcpu = *new (_slab) Vcpu(_constrained_md_ram_alloc,
_cap_quota_guard(),
Vcpu_id {_id_alloc});
/* we ran out of caps in core */
if (!vcpu.ds_cap().valid())
@ -168,7 +168,7 @@ void Vm_session_component::_create_vcpu(Thread_capability cap)
if (res != Nova::NOVA_OK) {
error("create_sm = ", res);
destroy(_sliced_heap, &vcpu);
destroy(_slab, &vcpu);
return;
}
@ -181,7 +181,7 @@ void Vm_session_component::_create_vcpu(Thread_capability cap)
if (res != Nova::NOVA_OK) {
error("create_ec = ", res);
destroy(_sliced_heap, &vcpu);
destroy(_slab, &vcpu);
return;
}
@ -203,7 +203,7 @@ void Vm_session_component::_create_vcpu(Thread_capability cap)
if (res != Nova::NOVA_OK)
{
error("map sm ", res, " ", _id_alloc);
destroy(_sliced_heap, &vcpu);
destroy(_slab, &vcpu);
return;
}
@ -288,10 +288,8 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
_cap_quota_guard().withdraw(Cap_quota{1});
_pd_sel = cap_map().insert();
if (!_pd_sel || _pd_sel == Vcpu::invalid()) {
_cap_quota_guard().replenish(Cap_quota{1});
if (!_pd_sel || _pd_sel == Vcpu::invalid())
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 };
@ -301,70 +299,85 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
if (res != Nova::NOVA_OK) {
error("create_pd = ", res);
cap_map().remove(_pd_sel, 0, true);
_cap_quota_guard().replenish(Cap_quota{1});
throw Service_denied();
}
/* configure managed VM area */
_map.add_range(0, 0UL - 0x1000);
_map.add_range(0UL - 0x1000, 0x1000);
}
Vm_session_component::~Vm_session_component()
{
for (;Vcpu * vcpu = _vcpus.first();) {
_vcpus.remove(vcpu);
destroy(_sliced_heap, vcpu);
destroy(_slab, vcpu);
}
if (_pd_sel && _pd_sel != Vcpu::invalid()) {
/* 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);
_cap_quota_guard().replenish(Cap_quota{1});
}
void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc,
addr_t const guest_phys,
bool const executable,
bool const writeable)
{
using Nova::Utcb;
Utcb & utcb = *reinterpret_cast<Utcb *>(Thread::myself()->utcb());
addr_t const src_pd = platform_specific().core_pd_sel();
Flexpage_iterator flex(dsc.phys_addr(), dsc.size(),
guest_phys, dsc.size(), guest_phys);
Flexpage page = flex.page();
while (page.valid()) {
Nova::Rights const map_rights (true, dsc.writable() && writeable,
executable);
Nova::Mem_crd const mem(page.addr >> 12, page.log2_order - 12,
map_rights);
utcb.set_msg_word(0);
/* ignore return value as one item always fits into the utcb */
bool const ok = utcb.append_item(mem, 0, true, true);
(void)ok;
/* receive window in destination pd */
Nova::Mem_crd crd_mem(page.hotspot >> 12, page.log2_order - 12,
map_rights);
/* asynchronously map memory */
uint8_t res = _with_kernel_quota_upgrade(_pd_sel, [&] {
return Nova::delegate(src_pd, _pd_sel, crd_mem); });
if (res != Nova::NOVA_OK)
error("could not map VM memory ", res);
page = flex.page();
}
}
void Vm_session_component::attach(Dataspace_capability cap, addr_t guest_phys)
void Vm_session_component::_detach_vm_memory(addr_t guest_phys, size_t size)
{
if (!cap.valid())
throw Invalid_dataspace();
Nova::Rights const revoke_rwx(true, true, true);
/* check dataspace validity */
_ep.apply(cap, [&] (Dataspace_component *ptr) {
if (!ptr)
throw Invalid_dataspace();
Flexpage_iterator flex(guest_phys, size, guest_phys, size, 0);
Flexpage page = flex.page();
Dataspace_component &dsc = *ptr;
while (page.valid()) {
Nova::Mem_crd mem(page.addr >> 12, page.log2_order - 12, revoke_rwx);
Nova::revoke(mem, true, true, _pd_sel);
/* unsupported - deny otherwise arbitrary physical memory can be mapped to a VM */
if (dsc.managed())
throw Invalid_dataspace();
using Nova::Utcb;
Utcb & utcb = *reinterpret_cast<Utcb *>(Thread::myself()->utcb());
addr_t const src_pd = platform_specific().core_pd_sel();
Flexpage_iterator flex(dsc.phys_addr(), dsc.size(),
guest_phys, dsc.size(), guest_phys);
Flexpage page = flex.page();
while (page.valid()) {
Nova::Rights const map_rights (true, dsc.writable(), true);
Nova::Mem_crd const mem(page.addr >> 12, page.log2_order - 12,
map_rights);
utcb.set_msg_word(0);
/* ignore return value as one item always fits into the utcb */
bool const ok = utcb.append_item(mem, 0, true, true);
(void)ok;
/* receive window in destination pd */
Nova::Mem_crd crd_mem(page.hotspot >> 12, page.log2_order - 12,
map_rights);
/* asynchronously map memory */
uint8_t res = _with_kernel_quota_upgrade(_pd_sel, [&] {
return Nova::delegate(src_pd, _pd_sel, crd_mem); });
if (res != Nova::NOVA_OK)
error("could not map VM memory ", res);
page = flex.page();
}
});
page = flex.page();
}
}

View File

@ -36,6 +36,7 @@ struct Vcpu {
private:
Signal_dispatcher_base &_obj;
Allocator &_alloc;
Vm_session_client::Vcpu_id _id;
addr_t _state { 0 };
void *_ep_handler { nullptr };
@ -412,10 +413,13 @@ struct Vcpu {
public:
Vcpu(Vm_handler_base &o, unsigned id) : _obj(o), _id({id}) { }
Vcpu(Vm_handler_base &o, unsigned 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; }
@ -684,7 +688,7 @@ Vm_session_client::create_vcpu(Allocator &alloc, Env &env,
Thread * ep = reinterpret_cast<Thread *>(&handler._rpc_ep);
call<Rpc_create_vcpu>(ep->cap());
Vcpu * vcpu = new (alloc) Registered<Vcpu> (vcpus, handler, vcpu_id++);
Vcpu * vcpu = new (alloc) Registered<Vcpu> (vcpus, handler, vcpu_id++, alloc);
vcpu->assign_ds_state(env.rm(), call<Rpc_cpu_state>(vcpu->id()));
Signal_context_capability dontcare_exit;
@ -744,3 +748,11 @@ Dataspace_capability Vm_session_client::cpu_state(Vcpu_id vcpu_id)
return cap;
}
Vm_session::~Vm_session()
{
vcpus.for_each([&] (Vcpu &vc) {
Allocator &alloc = vc.allocator();
destroy(alloc, &vc);
});
}

View File

@ -1,5 +1,6 @@
GEN_SRC_CC = \
spec/x86/io_port_session_component.cc
spec/x86/io_port_session_component.cc \
vm_session_common.cc
REP_SRC_CC = \
spec/x86/io_port_session_support.cc \

View File

@ -1,5 +1,6 @@
GEN_SRC_CC = \
spec/x86/io_port_session_component.cc
spec/x86/io_port_session_component.cc \
vm_session_common.cc
REP_SRC_CC = \
spec/x86/io_port_session_support.cc \

View File

@ -25,7 +25,8 @@ class Genode::Vm_session_component
:
private Ram_quota_guard,
private Cap_quota_guard,
public Rpc_object<Vm_session, Vm_session_component>
public Rpc_object<Vm_session, Vm_session_component>,
public Region_map_detach
{
private:
@ -52,9 +53,13 @@ class Genode::Vm_session_component
Cap_sel notification_cap() const { return _notification; }
};
typedef Allocator_avl_tpl<Rm_region> Avl_region;
Rpc_entrypoint &_ep;
Constrained_ram_allocator _constrained_md_ram_alloc;
Sliced_heap _sliced_heap;
Heap _heap;
Avl_region _map { &_heap };
List<Vcpu> _vcpus { };
unsigned _id_alloc { 0 };
unsigned _pd_id { 0 };
@ -78,6 +83,9 @@ class Genode::Vm_session_component
return nullptr;
}
void _attach_vm_memory(Dataspace_component &, addr_t, bool, bool);
void _detach_vm_memory(addr_t, size_t);
protected:
Ram_quota_guard &_ram_quota_guard() { return *this; }
@ -92,6 +100,13 @@ class Genode::Vm_session_component
Diag, Ram_allocator &ram, Region_map &);
~Vm_session_component();
/*********************************
** Region_map_detach interface **
*********************************/
void detach(Region_map::Local_addr) override;
void unmap_region(addr_t, size_t) override;
/**************************
** Vm session interface **
**************************/
@ -103,7 +118,7 @@ class Genode::Vm_session_component
void _pause(Vcpu_id);
void attach(Dataspace_capability, addr_t) override;
void attach_pic(addr_t) override {}
void detach(addr_t, size_t) override {}
void detach(addr_t, size_t) override;
void _create_vcpu(Thread_capability);
};

View File

@ -11,6 +11,9 @@
* under the terms of the GNU Affero General Public License version 3.
*/
/* base includes */
#include <util/flex_iterator.h>
/* core includes */
#include <core_env.h>
#include <vm_session_component.h>
@ -18,6 +21,7 @@
#include <cpu_thread_component.h>
#include <arch_kernel_object.h>
using namespace Genode;
void Vm_session_component::Vcpu::_free_up()
@ -73,6 +77,7 @@ try
_ep(ep),
_constrained_md_ram_alloc(ram, _ram_quota_guard(), _cap_quota_guard()),
_sliced_heap(_constrained_md_ram_alloc, local_rm),
_heap(_constrained_md_ram_alloc, local_rm),
_pd_id(Platform_pd::pd_id_alloc().alloc()),
_vm_page_table(platform_specific().core_sel_alloc().alloc()),
_vm_space(_vm_page_table,
@ -114,6 +119,10 @@ try
throw Service_denied();
}
/* configure managed VM area */
_map.add_range(0, 0UL - 0x1000);
_map.add_range(0UL - 0x1000, 0x1000);
caps.acknowledge();
ram.acknowledge();
} catch (...) {
@ -145,7 +154,17 @@ Vm_session_component::~Vm_session_component()
{
for (;Vcpu * vcpu = _vcpus.first();) {
_vcpus.remove(vcpu);
destroy(_sliced_heap, 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 (_vm_page_table.value())
@ -168,10 +187,10 @@ void Vm_session_component::_create_vcpu(Thread_capability cap)
Vcpu * vcpu = nullptr;
/* code to revert partial allocations in case of Out_of_ram/_quota */
auto free_up = [&] () { if (vcpu) destroy(_sliced_heap, vcpu); };
auto free_up = [&] () { if (vcpu) destroy(_heap, vcpu); };
try {
vcpu = new (_sliced_heap) Vcpu(_constrained_md_ram_alloc,
vcpu = new (_heap) Vcpu(_constrained_md_ram_alloc,
_cap_quota_guard(),
Vcpu_id{_id_alloc},
_notifications._service);
@ -211,32 +230,6 @@ Dataspace_capability Vm_session_component::_cpu_state(Vcpu_id const vcpu_id)
return vcpu->ds_cap();
}
void Vm_session_component::attach(Dataspace_capability cap, addr_t guest_phys)
{
if (!cap.valid())
throw Invalid_dataspace();
/* check dataspace validity */
_ep.apply(cap, [&] (Dataspace_component *ptr) {
if (!ptr)
throw Invalid_dataspace();
Dataspace_component &dsc = *ptr;
/* unsupported - deny otherwise arbitrary physical memory can be mapped to a VM */
if (dsc.managed())
throw Invalid_dataspace();
_vm_space.alloc_guest_page_tables(guest_phys, dsc.size());
enum { FLUSHABLE = true, EXECUTABLE = true };
_vm_space.map_guest(dsc.phys_addr(), guest_phys, dsc.size() >> 12,
dsc.cacheability(),
dsc.writable(), EXECUTABLE, FLUSHABLE);
});
}
void Vm_session_component::_pause(Vcpu_id const vcpu_id)
{
Vcpu * vcpu = _lookup(vcpu_id);
@ -245,3 +238,29 @@ void Vm_session_component::_pause(Vcpu_id const vcpu_id)
vcpu->signal();
}
void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc,
addr_t const guest_phys,
bool const executable,
bool const writeable)
{
_vm_space.alloc_guest_page_tables(guest_phys, dsc.size());
enum { FLUSHABLE = true };
_vm_space.map_guest(dsc.phys_addr(), guest_phys, dsc.size() >> 12,
dsc.cacheability(),
dsc.writable() && writeable,
executable, FLUSHABLE);
}
void Vm_session_component::_detach_vm_memory(addr_t guest_phys, size_t size)
{
Flexpage_iterator flex(guest_phys, size, guest_phys, size, 0);
Flexpage page = flex.page();
while (page.valid()) {
_vm_space.unmap(page.addr, (1 << page.log2_order) / 4096);
page = flex.page();
}
}

View File

@ -41,6 +41,7 @@ struct Vcpu : Genode::Thread
Signal_context_capability &_signal;
Semaphore _wake_up { 0 };
Semaphore &_handler_ready;
Allocator &_alloc;
Lock _startup { Genode::Lock::LOCKED };
Vm_session_client::Vcpu_id _id;
addr_t _state { 0 };
@ -740,12 +741,14 @@ struct Vcpu : Genode::Thread
public:
Vcpu(Genode::Env &env, Genode::Signal_context_capability &cap,
Semaphore &handler_ready, unsigned id)
Semaphore &handler_ready, unsigned id, Allocator &alloc)
:
Thread(env, "vcpu_thread", STACK_SIZE), _signal(cap),
_handler_ready(handler_ready), _id({id})
_handler_ready(handler_ready), _alloc(alloc), _id({id})
{ }
Allocator &allocator() { return _alloc; }
void start() override {
Thread::start();
_startup.lock();
@ -795,7 +798,7 @@ Genode::Vm_session_client::create_vcpu(Allocator &alloc, Env &env,
Vcpu * vcpu = new (alloc) Genode::Registered<Vcpu> (vcpus, env,
handler._cap,
handler._done,
vcpu_id);
vcpu_id, alloc);
try {
/* now it gets actually valid - vcpu->cap() becomes valid */
@ -843,3 +846,11 @@ Genode::Dataspace_capability Genode::Vm_session_client::cpu_state(Vcpu_id vcpu_i
return cap;
}
Vm_session::~Vm_session()
{
vcpus.for_each([&] (Vcpu &vc) {
Allocator &alloc = vc.allocator();
destroy(alloc, &vc);
});
}

View File

@ -32,11 +32,12 @@ struct Genode::Vm_session : Session
enum { CAP_QUOTA = 3 };
class Invalid_dataspace : Exception { };
class Region_conflict : Exception { };
/**
* Destructor
*/
virtual ~Vm_session() { }
virtual ~Vm_session();
/**
* Attach dataspace to the guest-physical memory address space
@ -75,8 +76,9 @@ struct Genode::Vm_session : Session
GENODE_RPC(Rpc_run, void, _run, Vcpu_id);
GENODE_RPC(Rpc_pause, void, _pause, Vcpu_id);
GENODE_RPC_THROW(Rpc_attach, void, attach,
GENODE_TYPE_LIST(Invalid_dataspace),
Dataspace_capability, addr_t);
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps, Region_conflict,
Invalid_dataspace),
Dataspace_capability, addr_t);
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, void, _create_vcpu,

View File

@ -69,6 +69,8 @@ _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
@ -380,6 +382,7 @@ _ZTIN10__cxxabiv120__si_class_type_infoE D 24
_ZTIN10__cxxabiv121__vmi_class_type_infoE D 24
_ZTIN10__cxxabiv123__fundamental_type_infoE D 24
_ZTIN5Timer10ConnectionE D 88
_ZTIN6Genode10Vm_sessionE D 24
_ZTIN6Genode11Sliced_heapE D 24
_ZTIN6Genode14Rpc_entrypointE D 56
_ZTIN6Genode14Signal_contextE D 56
@ -537,6 +540,7 @@ _ZTVN10__cxxabiv120__si_class_type_infoE D 88
_ZTVN10__cxxabiv121__vmi_class_type_infoE D 88
_ZTVN10__cxxabiv123__fundamental_type_infoE D 64
_ZTVN5Timer10ConnectionE D 320
_ZTVN6Genode10Vm_sessionE D 56
_ZTVN6Genode11Sliced_heapE D 72
_ZTVN6Genode14Rpc_entrypointE D 80
_ZTVN6Genode14Signal_contextE D 32

View File

@ -0,0 +1,132 @@
/*
* \brief Core-specific instance of the VM session interface
* \author Alexander Boettcher
* \date 2018-08-26
*/
/*
* 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.
*/
/* Base includes */
#include <util/flex_iterator.h>
/* Core includes */
#include <cpu_thread_component.h>
#include <dataspace_component.h>
#include <vm_session_component.h>
using Genode::addr_t;
using Genode::Vm_session_component;
void Vm_session_component::attach(Dataspace_capability const cap,
addr_t const guest_phys)
{
if (!cap.valid())
throw Invalid_dataspace();
/* check dataspace validity */
_ep.apply(cap, [&] (Dataspace_component *ptr) {
if (!ptr)
throw Invalid_dataspace();
Dataspace_component &dsc = *ptr;
/* unsupported - deny otherwise arbitrary physical memory can be mapped to a VM */
if (dsc.managed())
throw Invalid_dataspace();
bool const writeable = true;
bool const executable = true;
unsigned const offset = 0;
switch (_map.alloc_addr(dsc.size(), guest_phys).value) {
case Range_allocator::Alloc_return::OUT_OF_METADATA:
throw Out_of_ram();
case Range_allocator::Alloc_return::RANGE_CONFLICT:
{
Rm_region *region_ptr = _map.metadata((void *)guest_phys);
if (!region_ptr)
throw Region_conflict();
Rm_region &region = *region_ptr;
if (!(cap == region.dataspace().cap()))
throw Region_conflict();
if (guest_phys < region.base() ||
guest_phys > region.base() + region.size() - 1)
throw Region_conflict();
/* re-attach all */
break;
}
case Range_allocator::Alloc_return::OK:
{
/* store attachment info in meta data */
try {
_map.construct_metadata((void *)guest_phys,
guest_phys, dsc.size(),
dsc.writable() && writeable,
dsc, offset, *this, executable);
} catch (Allocator_avl_tpl<Rm_region>::Assign_metadata_failed) {
error("failed to store attachment info");
throw Invalid_dataspace();
}
Rm_region &region = *_map.metadata((void *)guest_phys);
/* inform dataspace about attachment */
dsc.attached_to(region);
break;
}
};
/* kernel specific code to attach memory to guest */
_attach_vm_memory(dsc, guest_phys, executable, writeable);
});
}
void Vm_session_component::detach(addr_t guest_phys, size_t size)
{
if (guest_phys & 0xffful) {
size += 0x1000 - (guest_phys & 0xffful);
guest_phys &= ~0xffful;
}
if (size & 0xffful)
size = align_addr(size, 12);
if (!size)
return;
{
Rm_region *region = _map.metadata(reinterpret_cast<void *>(guest_phys));
if (region && guest_phys == region->base() && region->size() <= size) {
/* inform dataspace */
region->dataspace().detached_from(*region);
/* cleanup metadata */
_map.free(reinterpret_cast<void *>(region->base()));
}
}
/* kernel specific code to detach memory from guest */
_detach_vm_memory(guest_phys, size);
}
void Vm_session_component::detach(Region_map::Local_addr addr)
{
Rm_region *region = _map.metadata(addr);
if (region)
detach(region->base(), region->size());
else
Genode::error(__PRETTY_FUNCTION__, " unknown region");
}
void Vm_session_component::unmap_region(addr_t base, size_t size)
{
Genode::error(__func__, " unimplemented ", base, " ", size);
}

View File

@ -39,3 +39,6 @@ Dataspace_capability Vm_session_client::cpu_state(Vcpu_id const vcpu_id)
{
return call<Rpc_cpu_state>(vcpu_id);
}
Vm_session::~Vm_session()
{ }

View File

@ -140,7 +140,7 @@ unify_output "vcpu 3 : XX. vm exit - resume vcpu" ""
trim_lines
set output_3 $output
puts "comparing output ..."
puts "\ncomparing output ..."
puts $output_0
set output $output_0

View File

@ -18,6 +18,7 @@
#include <base/heap.h>
#include <base/signal.h>
#include <timer_session/connection.h>
#include <util/reconstructible.h>
#include <vm_session/connection.h>
#include <vm_session/vm_session.h>
@ -198,8 +199,8 @@ class Vm {
enum { STACK_SIZE = 2*1024*sizeof(long) };
Genode::Vm_connection _vm_con;
Genode::Heap _heap;
Genode::Vm_connection _vm_con;
bool _svm;
bool _vmx;
Genode::Entrypoint &_ep_first; /* running on first CPU */
@ -214,6 +215,9 @@ class Vm {
Timer::Connection _timer;
Genode::Signal_handler<Vm> _timer_handler;
/* trigger destruction of _vm session to test this case also */
Genode::Signal_context_capability _signal_destruction;
void _handle_timer();
bool _cpu_name(char const * name)
@ -245,10 +249,10 @@ class Vm {
public:
Vm(Genode::Env &env)
Vm(Genode::Env &env, Genode::Signal_context_capability destruct_cap)
:
_vm_con(env),
_heap(env.ram(), env.rm()),
_vm_con(env),
_svm(_amd() && _vm_feature(env, "svm")),
_vmx(_intel() && _vm_feature(env, "vmx")),
_ep_first(env.ep()),
@ -260,7 +264,8 @@ class Vm {
_vcpu3(_ep_second, _vm_con, _heap, env, *this, _svm, _vmx),
_memory(env.ram().alloc(4096)),
_timer(env),
_timer_handler(_ep_first, *this, &Vm::_handle_timer)
_timer_handler(_ep_first, *this, &Vm::_handle_timer),
_signal_destruction(destruct_cap)
{
if (!_svm && !_vmx) {
Genode::error("no SVM nor VMX support detected");
@ -384,7 +389,11 @@ void Vm::_handle_timer()
_vcpu1.skip_instruction(1*2 /* 1x jmp endless loop size */);
_vm_con.run(_vcpu1.id());
} else if (_vcpu1.paused_4th()) {
Genode::log("vmm test finished");
Genode::log("vcpu test finished - de-arm timer");
_timer.trigger_periodic(0);
/* trigger destruction of VM session */
Genode::Signal_transmitter(_signal_destruction).submit();
}
}
@ -479,18 +488,30 @@ void Vcpu::_handle_vm_exception()
_vm_con.run(_vcpu);
}
class Vmm {
private:
Vm _vm;
Genode::Signal_handler<Vmm> _destruct_handler;
Genode::Reconstructible<Vm> _vm;
void _destruct()
{
Genode::log("destruct vm session");
_vm.destruct();
Genode::log("vmm test finished");
}
public:
Vmm(Genode::Env &env)
: _vm(env)
{ }
:
_destruct_handler(env.ep(), *this, &Vmm::_destruct),
_vm(env, _destruct_handler)
{
}
};
void Component::construct(Genode::Env & env) { static Vmm vmm(env); }