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

@@ -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);
});
}