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:
committed by
Christian Helmuth
parent
169c51d50d
commit
450c8dc149
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user