2018-09-26 11:00:01 +02:00
|
|
|
/*
|
|
|
|
* \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.
|
|
|
|
*/
|
|
|
|
|
2019-04-02 17:41:30 +02:00
|
|
|
/* base includes */
|
|
|
|
#include <util/flex_iterator.h>
|
|
|
|
|
2018-09-26 11:00:01 +02:00
|
|
|
/* core includes */
|
|
|
|
#include <core_env.h>
|
|
|
|
#include <vm_session_component.h>
|
|
|
|
#include <pd_session_component.h>
|
|
|
|
#include <cpu_thread_component.h>
|
|
|
|
#include <arch_kernel_object.h>
|
|
|
|
|
2019-04-02 17:41:30 +02:00
|
|
|
|
2018-09-26 11:00:01 +02:00
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
void Vm_session_component::Vcpu::_free_up()
|
|
|
|
{
|
|
|
|
if (_ds_cap.valid())
|
|
|
|
_ram_alloc.free(_ds_cap);
|
|
|
|
|
|
|
|
if (_notification.value()) {
|
|
|
|
int ret = seL4_CNode_Delete(seL4_CapInitThreadCNode,
|
|
|
|
_notification.value(), 32);
|
|
|
|
if (ret == seL4_NoError)
|
|
|
|
platform_specific().core_sel_alloc().free(_notification);
|
|
|
|
else
|
|
|
|
Genode::error(__func__, " cnode delete error ", ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Vm_session_component::Vcpu::Vcpu(Constrained_ram_allocator &ram_alloc,
|
|
|
|
Cap_quota_guard &cap_alloc,
|
|
|
|
Vcpu_id const vcpu_id,
|
|
|
|
seL4_Untyped const service)
|
|
|
|
:
|
|
|
|
_ram_alloc(ram_alloc),
|
|
|
|
_ds_cap (_ram_alloc.alloc(4096, Cache_attribute::CACHED)),
|
|
|
|
_vcpu_id(vcpu_id)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
/* notification cap */
|
|
|
|
Cap_quota_guard::Reservation caps(cap_alloc, Cap_quota{1});
|
|
|
|
|
|
|
|
_notification = platform_specific().core_sel_alloc().alloc();
|
|
|
|
create<Notification_kobj>(service,
|
|
|
|
platform_specific().core_cnode().sel(),
|
|
|
|
_notification);
|
|
|
|
|
|
|
|
caps.acknowledge();
|
|
|
|
} catch (...) {
|
|
|
|
_free_up();
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
|
|
|
|
Resources resources,
|
|
|
|
Label const &,
|
|
|
|
Diag,
|
|
|
|
Ram_allocator &ram,
|
2019-01-11 15:41:29 +01:00
|
|
|
Region_map &local_rm,
|
2019-04-17 14:16:27 +02:00
|
|
|
unsigned,
|
|
|
|
Trace::Source_registry &)
|
2018-09-26 11:00:01 +02:00
|
|
|
try
|
|
|
|
:
|
|
|
|
Ram_quota_guard(resources.ram_quota),
|
|
|
|
Cap_quota_guard(resources.cap_quota),
|
|
|
|
_ep(ep),
|
|
|
|
_constrained_md_ram_alloc(ram, _ram_quota_guard(), _cap_quota_guard()),
|
2019-04-02 17:41:30 +02:00
|
|
|
_heap(_constrained_md_ram_alloc, local_rm),
|
2018-09-26 11:00:01 +02:00
|
|
|
_pd_id(Platform_pd::pd_id_alloc().alloc()),
|
|
|
|
_vm_page_table(platform_specific().core_sel_alloc().alloc()),
|
|
|
|
_vm_space(_vm_page_table,
|
|
|
|
platform_specific().core_sel_alloc(),
|
|
|
|
platform().ram_alloc(),
|
|
|
|
platform_specific().top_cnode(),
|
|
|
|
platform_specific().core_cnode(),
|
|
|
|
platform_specific().phys_cnode(),
|
|
|
|
_pd_id, _page_table_registry, "VM")
|
|
|
|
{
|
|
|
|
Platform &platform = platform_specific();
|
|
|
|
Range_allocator &phys_alloc = platform.ram_alloc();
|
|
|
|
|
|
|
|
/* _pd_id && _vm_page_table */
|
|
|
|
Cap_quota_guard::Reservation caps(_cap_quota_guard(), Cap_quota{2});
|
|
|
|
/* ept object requires a page taken directly from core's phys_alloc */
|
|
|
|
/* notifications requires a page taken directly from core's phys_alloc */
|
|
|
|
Ram_quota_guard::Reservation ram(_ram_quota_guard(), Ram_quota{2 * 4096});
|
|
|
|
|
|
|
|
try {
|
|
|
|
_ept._phys = Untyped_memory::alloc_page(phys_alloc);
|
|
|
|
_ept._service = Untyped_memory::untyped_sel(_ept._phys).value();
|
|
|
|
|
|
|
|
create<Ept_kobj>(_ept._service, platform.core_cnode().sel(),
|
|
|
|
_vm_page_table);
|
|
|
|
} catch (...) {
|
|
|
|
throw Service_denied();
|
|
|
|
}
|
|
|
|
|
|
|
|
long ret = seL4_X86_ASIDPool_Assign(platform.asid_pool().value(),
|
|
|
|
_vm_page_table.value());
|
|
|
|
if (ret != seL4_NoError)
|
|
|
|
throw Service_denied();
|
|
|
|
|
|
|
|
try {
|
|
|
|
_notifications._phys = Untyped_memory::alloc_page(phys_alloc);
|
|
|
|
_notifications._service = Untyped_memory::untyped_sel(_notifications._phys).value();
|
|
|
|
} catch (...) {
|
|
|
|
throw Service_denied();
|
|
|
|
}
|
|
|
|
|
2019-04-02 17:41:30 +02:00
|
|
|
/* configure managed VM area */
|
|
|
|
_map.add_range(0, 0UL - 0x1000);
|
|
|
|
_map.add_range(0UL - 0x1000, 0x1000);
|
|
|
|
|
2018-09-26 11:00:01 +02:00
|
|
|
caps.acknowledge();
|
|
|
|
ram.acknowledge();
|
|
|
|
} catch (...) {
|
|
|
|
|
|
|
|
if (_notifications._service)
|
|
|
|
Untyped_memory::free_page(platform().ram_alloc(), _notifications._phys);
|
|
|
|
|
|
|
|
if (_ept._service) {
|
|
|
|
int ret = seL4_CNode_Delete(seL4_CapInitThreadCNode,
|
|
|
|
_vm_page_table.value(), 32);
|
|
|
|
if (ret == seL4_NoError)
|
|
|
|
Untyped_memory::free_page(platform().ram_alloc(), _ept._phys);
|
|
|
|
|
|
|
|
if (ret != seL4_NoError)
|
|
|
|
error(__FUNCTION__, ": could not free ASID entry, "
|
|
|
|
"leaking physical memory ", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_vm_page_table.value())
|
|
|
|
platform_specific().core_sel_alloc().free(_vm_page_table);
|
|
|
|
|
|
|
|
if (_pd_id)
|
|
|
|
Platform_pd::pd_id_alloc().free(_pd_id);
|
|
|
|
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vm_session_component::~Vm_session_component()
|
|
|
|
{
|
|
|
|
for (;Vcpu * vcpu = _vcpus.first();) {
|
|
|
|
_vcpus.remove(vcpu);
|
2019-04-02 17:41:30 +02:00
|
|
|
destroy(_heap, vcpu);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* detach all regions */
|
|
|
|
while (true) {
|
|
|
|
addr_t out_addr = 0;
|
|
|
|
|
|
|
|
if (!_map.any_block_addr(&out_addr))
|
|
|
|
break;
|
|
|
|
|
|
|
|
detach(out_addr);
|
2018-09-26 11:00:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_vm_page_table.value())
|
|
|
|
platform_specific().core_sel_alloc().free(_vm_page_table);
|
|
|
|
|
|
|
|
if (_pd_id)
|
|
|
|
Platform_pd::pd_id_alloc().free(_pd_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Vm_session_component::_create_vcpu(Thread_capability cap)
|
|
|
|
{
|
|
|
|
if (!cap.valid())
|
|
|
|
return;
|
|
|
|
|
|
|
|
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 */
|
2019-04-02 17:41:30 +02:00
|
|
|
auto free_up = [&] () { if (vcpu) destroy(_heap, vcpu); };
|
2018-09-26 11:00:01 +02:00
|
|
|
|
|
|
|
try {
|
2019-04-02 17:41:30 +02:00
|
|
|
vcpu = new (_heap) Vcpu(_constrained_md_ram_alloc,
|
2018-09-26 11:00:01 +02:00
|
|
|
_cap_quota_guard(),
|
|
|
|
Vcpu_id{_id_alloc},
|
|
|
|
_notifications._service);
|
|
|
|
|
|
|
|
Platform_thread &pthread = thread->platform_thread();
|
|
|
|
pthread.setup_vcpu(_vm_page_table, vcpu->notification_cap());
|
|
|
|
|
|
|
|
int ret = seL4_TCB_BindNotification(pthread.tcb_sel().value(),
|
|
|
|
vcpu->notification_cap().value());
|
|
|
|
if (ret != seL4_NoError)
|
|
|
|
throw 0;
|
|
|
|
} catch (Out_of_ram) {
|
|
|
|
free_up();
|
|
|
|
throw;
|
|
|
|
} catch (Out_of_caps) {
|
|
|
|
free_up();
|
|
|
|
throw;
|
|
|
|
} catch (...) {
|
|
|
|
Genode::error("unexpected exception occurred");
|
|
|
|
free_up();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_vcpus.insert(vcpu);
|
|
|
|
_id_alloc++;
|
|
|
|
};
|
|
|
|
|
|
|
|
_ep.apply(cap, lambda);
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2019-04-02 17:41:30 +02:00
|
|
|
void Vm_session_component::_pause(Vcpu_id const vcpu_id)
|
2018-09-26 11:00:01 +02:00
|
|
|
{
|
2019-04-02 17:41:30 +02:00
|
|
|
Vcpu * vcpu = _lookup(vcpu_id);
|
|
|
|
if (!vcpu)
|
|
|
|
return;
|
2018-09-26 11:00:01 +02:00
|
|
|
|
2019-04-02 17:41:30 +02:00
|
|
|
vcpu->signal();
|
|
|
|
}
|
2018-09-26 11:00:01 +02:00
|
|
|
|
2019-04-02 17:41:30 +02:00
|
|
|
void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc,
|
|
|
|
addr_t const guest_phys,
|
2018-11-14 14:57:45 +01:00
|
|
|
Attach_attr const attribute)
|
2019-04-02 17:41:30 +02:00
|
|
|
{
|
2018-11-14 14:57:45 +01:00
|
|
|
Flexpage_iterator flex(dsc.phys_addr() + attribute.offset, attribute.size,
|
|
|
|
guest_phys, attribute.size, guest_phys);
|
2018-09-26 11:00:01 +02:00
|
|
|
|
2018-11-14 14:57:45 +01:00
|
|
|
Flexpage page = flex.page();
|
|
|
|
while (page.valid()) {
|
|
|
|
enum { NO_FLUSH = false, FLUSH = true };
|
|
|
|
try {
|
2019-04-11 14:28:36 +02:00
|
|
|
_vm_space.alloc_guest_page_tables(page.hotspot, 1 << page.log2_order);
|
2018-11-14 14:57:45 +01:00
|
|
|
_vm_space.map_guest(page.addr, page.hotspot,
|
|
|
|
(1 << page.log2_order) / 4096,
|
|
|
|
dsc.cacheability(),
|
|
|
|
dsc.writable() && attribute.writeable,
|
|
|
|
attribute.executable, NO_FLUSH);
|
|
|
|
} catch (Page_table_registry::Mapping_cache_full full) {
|
2019-04-11 14:28:36 +02:00
|
|
|
if (full.reason == Page_table_registry::Mapping_cache_full::MEMORY) {
|
|
|
|
if (_ram_quota_guard().limit().value > 4 * 1024 * 1024)
|
|
|
|
/* we get in trouble in core if we use too much memory */
|
|
|
|
throw Vm_space::Selector_allocator::Out_of_indices();
|
2018-11-14 14:57:45 +01:00
|
|
|
throw Out_of_ram();
|
2019-04-11 14:28:36 +02:00
|
|
|
}
|
2018-11-14 14:57:45 +01:00
|
|
|
if (full.reason == Page_table_registry::Mapping_cache_full::CAPS)
|
|
|
|
throw Out_of_caps();
|
|
|
|
return;
|
2019-04-11 14:28:36 +02:00
|
|
|
} catch (Vm_space::Selector_allocator::Out_of_indices) {
|
|
|
|
Genode::warning("run out of indices - flush all - cap=",
|
|
|
|
_cap_quota_guard().used(), "/",
|
|
|
|
_cap_quota_guard().avail(), "/",
|
|
|
|
_cap_quota_guard().limit(), " ram=",
|
|
|
|
_ram_quota_guard().used(), "/",
|
|
|
|
_ram_quota_guard().avail(), "/",
|
|
|
|
_ram_quota_guard().limit(), " guest=",
|
|
|
|
Genode::Hex(0UL - _map.avail()));
|
|
|
|
|
2018-11-14 14:57:45 +01:00
|
|
|
_vm_space.map_guest(page.addr, page.hotspot,
|
|
|
|
(1 << page.log2_order) / 4096,
|
|
|
|
dsc.cacheability(),
|
|
|
|
dsc.writable() && attribute.writeable,
|
|
|
|
attribute.executable, FLUSH);
|
2019-04-11 14:28:36 +02:00
|
|
|
|
|
|
|
/* drop all attachment to limit ram usage of this session */
|
|
|
|
while (true) {
|
|
|
|
addr_t out_addr = 0;
|
|
|
|
|
|
|
|
if (!_map.any_block_addr(&out_addr))
|
|
|
|
break;
|
|
|
|
|
|
|
|
detach(out_addr);
|
|
|
|
}
|
|
|
|
} catch (...) {
|
|
|
|
// Alloc_page_table_failed
|
|
|
|
Genode::error("alloc_guest_page_table exception");
|
|
|
|
return;
|
2018-11-14 14:57:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
page = flex.page();
|
|
|
|
}
|
2018-09-26 11:00:01 +02:00
|
|
|
}
|
|
|
|
|
2019-04-02 17:41:30 +02:00
|
|
|
void Vm_session_component::_detach_vm_memory(addr_t guest_phys, size_t size)
|
2018-09-26 11:00:01 +02:00
|
|
|
{
|
2019-04-02 17:41:30 +02:00
|
|
|
Flexpage_iterator flex(guest_phys, size, guest_phys, size, 0);
|
|
|
|
Flexpage page = flex.page();
|
2018-09-26 11:00:01 +02:00
|
|
|
|
2019-04-02 17:41:30 +02:00
|
|
|
while (page.valid()) {
|
|
|
|
_vm_space.unmap(page.addr, (1 << page.log2_order) / 4096);
|
|
|
|
|
|
|
|
page = flex.page();
|
|
|
|
}
|
2018-09-26 11:00:01 +02:00
|
|
|
}
|