foc/x86: implement vm_session interface

Issue #3111
This commit is contained in:
Alexander Boettcher 2018-09-26 14:48:25 +02:00 committed by Norman Feske
parent 3a1bbfad28
commit b75307b070
20 changed files with 1774 additions and 42 deletions

View File

@ -21,7 +21,7 @@ CONFIG_IA32=y
CONFIG_PF_PC=y
# CONFIG_PF_UX is not set
CONFIG_ABI_VF=y
# CONFIG_CPU_VIRT is not set
CONFIG_CPU_VIRT=y
CONFIG_SCHED_APIC=y
# CONFIG_WORKAROUND_AMD_FPU_LEAK is not set
CONFIG_REGPARM3=y

View File

@ -20,7 +20,7 @@ CONFIG_AMD64=y
# CONFIG_MIPS is not set
CONFIG_PF_PC=y
CONFIG_ABI_VF=y
# CONFIG_CPU_VIRT is not set
CONFIG_CPU_VIRT=y
CONFIG_SCHED_APIC=y
# CONFIG_WORKAROUND_AMD_FPU_LEAK is not set

View File

@ -48,7 +48,7 @@ namespace Fiasco {
static constexpr l4_cap_idx_t THREAD_AREA_BASE = 0xcUL << L4_CAP_SHIFT;
/* size of one thread slot */
static constexpr l4_cap_idx_t THREAD_AREA_SLOT = 0x3UL << L4_CAP_SHIFT;
static constexpr l4_cap_idx_t THREAD_AREA_SLOT = 0x5UL << L4_CAP_SHIFT;
/* offset to the ipc gate cap selector in the slot */
static constexpr l4_cap_idx_t THREAD_GATE_CAP = 0;
@ -59,6 +59,12 @@ namespace Fiasco {
/* offset to the irq cap selector in the slot */
static constexpr l4_cap_idx_t THREAD_IRQ_CAP = 0x2UL << L4_CAP_SHIFT;
/* offset to the irq cap selector in the slot */
static constexpr l4_cap_idx_t TASK_VCPU_CAP = 0x3UL << L4_CAP_SHIFT;
/* offset to the irq cap selector in the slot */
static constexpr l4_cap_idx_t TASK_VCPU_IRQ_CAP = 0x4UL << L4_CAP_SHIFT;
/* shortcut to the main thread's gate cap */
static constexpr l4_cap_idx_t MAIN_THREAD_CAP = THREAD_AREA_BASE
+ THREAD_GATE_CAP;

View File

@ -0,0 +1,15 @@
#
# \brief Portions of base library shared by core and non-core processes
# \author Norman Feske
# \date 2013-02-14
#
include $(BASE_DIR)/lib/mk/base-common.inc
LIBS += syscall-foc startup-foc
SRC_CC += spin_lock.cc cap_map.cc
SRC_CC += rpc_dispatch_loop.cc
SRC_CC += thread.cc thread_bootstrap.cc thread_myself.cc utcb.cc
SRC_CC += capability.cc
SRC_CC += signal_source_client.cc

View File

@ -4,12 +4,4 @@
# \date 2013-02-14
#
include $(BASE_DIR)/lib/mk/base-common.inc
LIBS += syscall-foc startup-foc
SRC_CC += spin_lock.cc cap_map.cc
SRC_CC += rpc_dispatch_loop.cc
SRC_CC += thread.cc thread_bootstrap.cc thread_myself.cc utcb.cc
SRC_CC += capability.cc
SRC_CC += signal_source_client.cc
include $(REP_DIR)/lib/mk/base-foc-common.inc

View File

@ -27,7 +27,6 @@ SRC_CC += stack_area.cc \
platform.cc \
platform_rom_modules.cc \
platform_pd.cc \
platform_services.cc \
platform_thread.cc \
pd_session_component.cc \
ram_dataspace_support.cc \
@ -39,6 +38,7 @@ SRC_CC += stack_area.cc \
signal_receiver.cc \
thread_start.cc \
trace_session_component.cc \
vm_session_component.cc \
heartbeat.cc
INC_DIR += $(REP_DIR)/src/core/include \

View File

@ -1,6 +1,7 @@
REQUIRES += arm
SRC_CC += spec/arm/platform_arm.cc \
spec/arm/ipc_pager.cc
spec/arm/ipc_pager.cc \
platform_services.cc
# override default stack-area location
INC_DIR += $(REP_DIR)/src/include/spec/arm

View File

@ -0,0 +1,9 @@
#
# \brief Portions of base library shared by core and non-core processes
# \author Norman Feske
# \date 2013-02-14
#
vpath vm_session.cc $(REP_DIR)/src/lib/base/x86
include $(REP_DIR)/lib/mk/base-foc-common.inc

View File

@ -1,10 +1,10 @@
SRC_CC += io_port_session_component.cc \
io_port_session_support.cc \
spec/x86/ipc_pager.cc \
spec/x86/platform.cc
spec/x86/platform.cc \
spec/x86/platform_services.cc
vpath io_port_session_component.cc $(BASE_DIR)/src/core/spec/x86
vpath io_port_session_support.cc $(BASE_DIR)/src/core/spec/x86
vpath platform_services.cc $(BASE_DIR)/src/core/spec/x86
include $(REP_DIR)/lib/mk/core-foc.inc

View File

@ -28,9 +28,14 @@
#include <platform_pd.h>
#include <assertion.h>
namespace Fiasco {
struct l4_kernel_info_t;
}
namespace Genode {
class Xml_generator;
class Platform : public Platform_generic
{
private:
@ -73,12 +78,6 @@ namespace Genode {
addr_t _vm_start = 0; /* begin of virtual memory */
size_t _vm_size = 0; /* size of virtual memory */
/*
* We do not export any boot module loaded before FIRST_ROM.
*/
enum { FIRST_ROM = 3 };
/**
* Setup base resources
*
@ -97,6 +96,12 @@ namespace Genode {
*/
void _setup_io_port_alloc();
/**
* Setup content of platform_info ROM
*/
void _setup_platform_info(Xml_generator &,
Fiasco::l4_kernel_info_t &);
/**
* Setup IRQ allocator
*/
@ -116,6 +121,8 @@ namespace Genode {
public:
enum { VCPU_VIRT_EXT_START = 0x1000, VCPU_VIRT_EXT_END = 0x10000 };
/**
* Core pager thread that handles core-internal page-faults
*/

View File

@ -156,6 +156,12 @@ namespace Genode {
*/
Affinity::Location affinity() const;
/**
* Make thread to vCPU
*/
Fiasco::l4_cap_idx_t setup_vcpu(unsigned, Cap_mapping const &,
Cap_mapping &);
/************************
** Accessor functions **
************************/

View File

@ -0,0 +1,95 @@
/*
* \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.
*/
#ifndef _CORE__VM_SESSION_COMPONENT_H_
#define _CORE__VM_SESSION_COMPONENT_H_
/* Genode includes */
#include <base/allocator_guard.h>
#include <base/rpc_server.h>
#include <base/heap.h>
#include <vm_session/vm_session.h>
/* Core includes */
#include <dataspace_component.h>
#include <cap_mapping.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:
Constrained_ram_allocator &_ram_alloc;
Cap_quota_guard &_cap_alloc;
Ram_dataspace_capability _ds_cap { };
Vm_session::Vcpu_id const _id;
Cap_mapping _recall { true };
public:
Vcpu(Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc, Vm_session::Vcpu_id const id);
~Vcpu();
bool match(Vm_session::Vcpu_id const id) const { return id.id == _id.id; }
Dataspace_capability ds_cap() { return _ds_cap; }
Cap_mapping &recall_cap() { return _recall; }
};
#endif /* _CORE__VM_SESSION_COMPONENT_H_ */

View File

@ -17,6 +17,7 @@
#include <base/allocator_avl.h>
#include <base/sleep.h>
#include <util/misc_math.h>
#include <util/xml_generator.h>
/* base-internal includes */
#include <base/internal/crt0.h>
@ -384,6 +385,13 @@ void Platform::_setup_basics()
/* configure applicable address space but never use page0 */
_vm_size = _vm_start == 0 ? _vm_size - L4_PAGESIZE : _vm_size;
_vm_start = _vm_start == 0 ? L4_PAGESIZE : _vm_start;
/* reserve virtual range for extended vCPU features - better way XXX ? */
if (_vm_start < VCPU_VIRT_EXT_END) {
_vm_size -= VCPU_VIRT_EXT_END - _vm_start;
_vm_start = VCPU_VIRT_EXT_END;
}
_region_alloc.add_range(_vm_start, _vm_size);
/* preserve stack area in core's virtual address space */
@ -456,17 +464,38 @@ Platform::Platform() :
core_thread.pager(_sigma0);
_core_pd->bind_thread(core_thread);
/* export x86 platform specific infos */
{
/* export x86 platform specific infos */
void * phys_ptr = nullptr;
if (ram_alloc().alloc_aligned(get_page_size(), &phys_ptr,
get_page_size_log2()).ok()) {
addr_t const phys_addr = reinterpret_cast<addr_t>(phys_ptr);
/* empty for now */
_rom_fs.insert(new (core_mem_alloc()) Rom_module(phys_addr,
get_page_size(),
"platform_info"));
}
void * core_local_ptr = nullptr;
void * phys_ptr = nullptr;
unsigned const pages = 1;
size_t const align = get_page_size_log2();
size_t const size = pages << get_page_size_log2();
if (ram_alloc().alloc_aligned(size, &phys_ptr, align).error())
return;
if (region_alloc().alloc_aligned(size, &core_local_ptr, align).error())
return;
addr_t const phys_addr = reinterpret_cast<addr_t>(phys_ptr);
addr_t const core_local_addr = reinterpret_cast<addr_t>(core_local_ptr);
if (!map_local(phys_addr, core_local_addr, pages))
return;
memset(core_local_ptr, 0, size);
Genode::Xml_generator xml(reinterpret_cast<char *>(core_local_addr),
pages << get_page_size_log2(),
"platform_info", [&] ()
{
xml.node("hardware", [&] () {
_setup_platform_info(xml, sigma0_map_kip());
});
});
_rom_fs.insert(new (core_mem_alloc()) Rom_module(phys_addr, size,
"platform_info"));
}
/* core log as ROM module */
@ -474,22 +503,26 @@ Platform::Platform() :
void * core_local_ptr = nullptr;
void * phys_ptr = nullptr;
unsigned const pages = 1;
size_t const log_size = pages << get_page_size_log2();
size_t const align = get_page_size_log2();
size_t const size = pages << get_page_size_log2();
if (ram_alloc().alloc_aligned(size, &phys_ptr, align).error())
return;
if (region_alloc().alloc_aligned(size, &core_local_ptr, align).error())
return;
ram_alloc().alloc_aligned(log_size, &phys_ptr, get_page_size_log2());
addr_t const phys_addr = reinterpret_cast<addr_t>(phys_ptr);
/* let one page free after the log buffer */
region_alloc().alloc_aligned(log_size, &core_local_ptr, get_page_size_log2());
addr_t const core_local_addr = reinterpret_cast<addr_t>(core_local_ptr);
map_local(phys_addr, core_local_addr, pages);
memset(core_local_ptr, 0, log_size);
if (!map_local(phys_addr, core_local_addr, pages))
return;
_rom_fs.insert(new (core_mem_alloc()) Rom_module(phys_addr, log_size,
memset(core_local_ptr, 0, size);
_rom_fs.insert(new (core_mem_alloc()) Rom_module(phys_addr, size,
"core_log"));
init_core_log(Core_log_range { core_local_addr, log_size } );
init_core_log(Core_log_range { core_local_addr, size } );
}
}

View File

@ -356,3 +356,43 @@ Platform_thread::~Platform_thread()
if (_platform_pd)
_platform_pd->unbind_thread(*this);
}
Fiasco::l4_cap_idx_t Platform_thread::setup_vcpu(unsigned const vcpu_id,
Cap_mapping const &task_vcpu,
Cap_mapping &vcpu_irq)
{
if (!_platform_pd)
return Fiasco::L4_INVALID_CAP;
if (vcpu_id >= (Platform::VCPU_VIRT_EXT_END - Platform::VCPU_VIRT_EXT_START) / L4_PAGESIZE)
return Fiasco::L4_INVALID_CAP;
Genode::addr_t const vcpu_addr = Platform::VCPU_VIRT_EXT_START +
L4_PAGESIZE * vcpu_id;
l4_fpage_t vm_page = l4_fpage( vcpu_addr, L4_PAGESHIFT, L4_FPAGE_RW);
l4_msgtag_t msg = l4_task_add_ku_mem(_platform_pd->native_task().data()->kcap(), vm_page);
if (l4_error(msg)) {
Genode::error("ku_mem failed ", l4_error(msg));
return Fiasco::L4_INVALID_CAP;
}
msg = l4_thread_vcpu_control_ext(_thread.local.data()->kcap(), vcpu_addr);
if (l4_error(msg)) {
Genode::error("vcpu_control_exit failed ", l4_error(msg));
return Fiasco::L4_INVALID_CAP;
}
/* attach thread to irq */
vcpu_irq.remote = _gate.remote + TASK_VCPU_IRQ_CAP;
l4_msgtag_t tag = l4_rcv_ep_bind_thread(vcpu_irq.local.data()->kcap(),
_thread.local.data()->kcap(), 0);
if (l4_msgtag_has_error(tag))
warning("attaching thread's irq failed");
vcpu_irq.map(_platform_pd->native_task().data()->kcap());
/* set human readable name in kernel debugger */
Cap_mapping map(task_vcpu.local, _gate.remote + TASK_VCPU_CAP);
map.map(_platform_pd->native_task().data()->kcap());
return map.remote;
}

View File

@ -16,3 +16,6 @@
void Genode::Platform::_setup_io_port_alloc() { }
void Genode::Platform::setup_irq_mode(unsigned, unsigned, unsigned) { }
void Genode::Platform::_setup_platform_info(Xml_generator &,
Fiasco::l4_kernel_info_t &) { }

View File

@ -15,6 +15,7 @@
/* Genode includes */
#include <base/log.h>
#include <irq_session/irq_session.h>
#include <util/xml_generator.h>
#include "platform.h"
#include "util.h"
@ -23,6 +24,7 @@
namespace Fiasco {
#include <l4/sys/ipc.h>
#include <l4/sys/icu.h>
#include <l4/sys/kip.h>
}
void Genode::Platform::_setup_io_port_alloc()
@ -86,3 +88,27 @@ void Genode::Platform::setup_irq_mode(unsigned irq_number, unsigned trigger,
if (l4_error(l4_icu_set_mode(L4_BASE_ICU_CAP, irq_number, mode)))
error("setting mode for IRQ ", irq_number, " failed");
}
static bool cpu_name(char const * name) {
using Genode::uint32_t;
uint32_t cpuid = 0, edx = 0, ebx = 0, ecx = 0;
asm volatile ("cpuid" : "+a" (cpuid), "=d" (edx), "=b"(ebx), "=c"(ecx));
return ebx == *reinterpret_cast<uint32_t const *>(name) &&
edx == *reinterpret_cast<uint32_t const *>(name + 4) &&
ecx == *reinterpret_cast<uint32_t const *>(name + 8);
}
void Genode::Platform::_setup_platform_info(Xml_generator &xml,
Fiasco::l4_kernel_info_t &kip)
{
xml.node("features", [&] () {
/* XXX better detection required, best told us by kernel !? */
xml.attribute("svm", cpu_name("AuthenticAMD"));
xml.attribute("vmx", cpu_name("GenuineIntel"));
});
xml.node("tsc", [&] () {
xml.attribute("freq_khz" , kip.frequency_cpu);
});
}

View File

@ -0,0 +1,35 @@
/*
* \brief Platform specific services for FOC
* \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.
*/
/* core includes */
#include <core_env.h>
#include <platform_services.h>
#include <vm_root.h>
#include <io_port_root.h>
/*
* Add x86 specific services
*/
void Genode::platform_add_local_services(Rpc_entrypoint &ep,
Sliced_heap &heap,
Registry<Service> &services)
{
static Vm_root vm_root(ep, heap, core_env().ram_allocator(),
core_env().local_rm());
static Core_service<Vm_session_component> vm(services, vm_root);
static Io_port_root io_root(*core_env().pd_session(),
platform().io_port_alloc(), heap);
static Core_service<Io_port_session_component> io_port(services, io_root);
}

View File

@ -0,0 +1,191 @@
/*
* \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>
#include <dataspace/client.h>
/* core includes */
#include <vm_session_component.h>
#include <pd_session_component.h>
#include <cpu_thread_component.h>
namespace Fiasco {
#include <l4/sys/factory.h>
}
using namespace Genode;
Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
Resources resources,
Label const &,
Diag,
Ram_allocator &ram,
Region_map &local_rm)
:
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()),
_sliced_heap(_constrained_md_ram_alloc, local_rm)
{
_cap_quota_guard().withdraw(Cap_quota{1});
using namespace Fiasco;
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;
}
}
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);
}
}
Vcpu::Vcpu(Constrained_ram_allocator &ram_alloc,
Cap_quota_guard &cap_alloc,
Vm_session::Vcpu_id const id)
:
_ram_alloc(ram_alloc),
_cap_alloc(cap_alloc),
_id(id)
{
try {
/* create ds for vCPU state */
_ds_cap = _ram_alloc.alloc(4096, Cache_attribute::CACHED);
} catch (...) {
throw;
}
Fiasco::l4_msgtag_t msg = l4_factory_create_irq(Fiasco::L4_BASE_FACTORY_CAP,
_recall.local.data()->kcap());
if (l4_error(msg)) {
_ram_alloc.free(_ds_cap);
Genode::error("vcpu irq creation failed", l4_error(msg));
throw 1;
}
}
Vcpu::~Vcpu()
{
if (_ds_cap.valid())
_ram_alloc.free(_ds_cap);
}
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;
try {
vcpu = new (_sliced_heap) Vcpu(_constrained_md_ram_alloc,
_cap_quota_guard(),
Vcpu_id {_id_alloc});
Fiasco::l4_cap_idx_t task = thread->platform_thread().setup_vcpu(_id_alloc, _task_vcpu, vcpu->recall_cap());
if (task == Fiasco::L4_INVALID_CAP)
throw 0;
_ep.apply(vcpu->ds_cap(), [&] (Dataspace_component *ds) {
if (!ds)
throw 1;
/* tell client where to find task cap */
*reinterpret_cast<Fiasco::l4_cap_idx_t *>(ds->phys_addr()) = task;
});
} catch (int) {
if (vcpu)
destroy(_sliced_heap, vcpu);
return;
} catch (...) {
if (vcpu)
destroy(_sliced_heap, vcpu);
throw;
}
_vcpus.insert(vcpu);
_id_alloc++;
};
_ep.apply(cap, lambda);
}
Dataspace_capability Vm_session_component::_cpu_state(Vcpu_id const vcpu_id)
{
for (Vcpu *vcpu = _vcpus.first(); vcpu; vcpu = vcpu->next()) {
if (!vcpu->match(vcpu_id))
continue;
return vcpu->ds_cap();
}
return Dataspace_capability();
}
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();
Flexpage_iterator flex(dsc.phys_addr(), dsc.size(),
guest_phys, dsc.size(), guest_phys);
using namespace Fiasco;
uint8_t const flags = dsc.writable() ? L4_FPAGE_RWX : L4_FPAGE_RX;
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();
}
});
}

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@ if { [get_cmd_switch --autopilot] } {
exit 0
}
if {[have_spec nova]} {
if {[have_spec nova] || [have_spec foc]} {
} else {
puts "\n Run script is not supported on this platform. \n";
exit 0