diff --git a/repos/base-foc/config/x86_32.kernel b/repos/base-foc/config/x86_32.kernel index 07fc0a1e78..a82d362a66 100644 --- a/repos/base-foc/config/x86_32.kernel +++ b/repos/base-foc/config/x86_32.kernel @@ -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 diff --git a/repos/base-foc/config/x86_64.kernel b/repos/base-foc/config/x86_64.kernel index a5fe9f85cb..d2d30d1033 100644 --- a/repos/base-foc/config/x86_64.kernel +++ b/repos/base-foc/config/x86_64.kernel @@ -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 diff --git a/repos/base-foc/include/foc/native_capability.h b/repos/base-foc/include/foc/native_capability.h index a50787ce53..09e8a36160 100644 --- a/repos/base-foc/include/foc/native_capability.h +++ b/repos/base-foc/include/foc/native_capability.h @@ -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; diff --git a/repos/base-foc/lib/mk/base-foc-common.inc b/repos/base-foc/lib/mk/base-foc-common.inc new file mode 100644 index 0000000000..e573f5d243 --- /dev/null +++ b/repos/base-foc/lib/mk/base-foc-common.inc @@ -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 diff --git a/repos/base-foc/lib/mk/base-foc-common.mk b/repos/base-foc/lib/mk/base-foc-common.mk index e573f5d243..13da834d2b 100644 --- a/repos/base-foc/lib/mk/base-foc-common.mk +++ b/repos/base-foc/lib/mk/base-foc-common.mk @@ -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 diff --git a/repos/base-foc/lib/mk/core-foc.inc b/repos/base-foc/lib/mk/core-foc.inc index 8f8942b77e..7c496c853c 100644 --- a/repos/base-foc/lib/mk/core-foc.inc +++ b/repos/base-foc/lib/mk/core-foc.inc @@ -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 \ diff --git a/repos/base-foc/lib/mk/spec/arm/core-foc.mk b/repos/base-foc/lib/mk/spec/arm/core-foc.mk index 3408f26fbf..570a38a349 100644 --- a/repos/base-foc/lib/mk/spec/arm/core-foc.mk +++ b/repos/base-foc/lib/mk/spec/arm/core-foc.mk @@ -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 diff --git a/repos/base-foc/lib/mk/spec/x86/base-foc-common.mk b/repos/base-foc/lib/mk/spec/x86/base-foc-common.mk new file mode 100644 index 0000000000..c5c10663d4 --- /dev/null +++ b/repos/base-foc/lib/mk/spec/x86/base-foc-common.mk @@ -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 diff --git a/repos/base-foc/lib/mk/spec/x86/core-foc.inc b/repos/base-foc/lib/mk/spec/x86/core-foc.inc index d8523ddc9e..75851dc6c7 100644 --- a/repos/base-foc/lib/mk/spec/x86/core-foc.inc +++ b/repos/base-foc/lib/mk/spec/x86/core-foc.inc @@ -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 diff --git a/repos/base-foc/src/core/include/platform.h b/repos/base-foc/src/core/include/platform.h index 5faf355d1f..93ab31e304 100644 --- a/repos/base-foc/src/core/include/platform.h +++ b/repos/base-foc/src/core/include/platform.h @@ -28,9 +28,14 @@ #include #include +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 */ diff --git a/repos/base-foc/src/core/include/platform_thread.h b/repos/base-foc/src/core/include/platform_thread.h index b7f3d92d11..7e2e5b0c63 100644 --- a/repos/base-foc/src/core/include/platform_thread.h +++ b/repos/base-foc/src/core/include/platform_thread.h @@ -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 ** ************************/ diff --git a/repos/base-foc/src/core/include/vm_session_component.h b/repos/base-foc/src/core/include/vm_session_component.h new file mode 100644 index 0000000000..c6d1a927f8 --- /dev/null +++ b/repos/base-foc/src/core/include/vm_session_component.h @@ -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 +#include +#include +#include + +/* Core includes */ +#include +#include + +namespace Genode { class Vm_session_component; struct Vcpu; } + +class Genode::Vm_session_component +: + private Ram_quota_guard, + private Cap_quota_guard, + public Rpc_object +{ + private: + + Rpc_entrypoint &_ep; + Constrained_ram_allocator _constrained_md_ram_alloc; + Sliced_heap _sliced_heap; + List _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::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_ */ diff --git a/repos/base-foc/src/core/platform.cc b/repos/base-foc/src/core/platform.cc index 8a009b454f..89ca76d527 100644 --- a/repos/base-foc/src/core/platform.cc +++ b/repos/base-foc/src/core/platform.cc @@ -17,6 +17,7 @@ #include #include #include +#include /* base-internal includes */ #include @@ -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(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(phys_ptr); + addr_t const core_local_addr = reinterpret_cast(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(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(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(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 } ); } } diff --git a/repos/base-foc/src/core/platform_thread.cc b/repos/base-foc/src/core/platform_thread.cc index 654392f00d..ef9720abe2 100644 --- a/repos/base-foc/src/core/platform_thread.cc +++ b/repos/base-foc/src/core/platform_thread.cc @@ -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; +} diff --git a/repos/base-foc/src/core/spec/arm/platform_arm.cc b/repos/base-foc/src/core/spec/arm/platform_arm.cc index ff795b8e82..2843761d8d 100644 --- a/repos/base-foc/src/core/spec/arm/platform_arm.cc +++ b/repos/base-foc/src/core/spec/arm/platform_arm.cc @@ -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 &) { } diff --git a/repos/base-foc/src/core/spec/x86/platform.cc b/repos/base-foc/src/core/spec/x86/platform.cc index a1dc236c72..d72dd480ac 100644 --- a/repos/base-foc/src/core/spec/x86/platform.cc +++ b/repos/base-foc/src/core/spec/x86/platform.cc @@ -15,6 +15,7 @@ /* Genode includes */ #include #include +#include #include "platform.h" #include "util.h" @@ -23,6 +24,7 @@ namespace Fiasco { #include #include +#include } 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(name) && + edx == *reinterpret_cast(name + 4) && + ecx == *reinterpret_cast(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); + }); +} diff --git a/repos/base-foc/src/core/spec/x86/platform_services.cc b/repos/base-foc/src/core/spec/x86/platform_services.cc new file mode 100644 index 0000000000..b141416725 --- /dev/null +++ b/repos/base-foc/src/core/spec/x86/platform_services.cc @@ -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 +#include +#include +#include + +/* + * Add x86 specific services + */ +void Genode::platform_add_local_services(Rpc_entrypoint &ep, + Sliced_heap &heap, + Registry &services) +{ + static Vm_root vm_root(ep, heap, core_env().ram_allocator(), + core_env().local_rm()); + static Core_service vm(services, vm_root); + + static Io_port_root io_root(*core_env().pd_session(), + platform().io_port_alloc(), heap); + + static Core_service io_port(services, io_root); +} diff --git a/repos/base-foc/src/core/vm_session_component.cc b/repos/base-foc/src/core/vm_session_component.cc new file mode 100644 index 0000000000..9f6346218c --- /dev/null +++ b/repos/base-foc/src/core/vm_session_component.cc @@ -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 +#include + +/* core includes */ +#include +#include +#include + +namespace Fiasco { +#include +} + + +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(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(); + } + }); +} diff --git a/repos/base-foc/src/lib/base/x86/vm_session.cc b/repos/base-foc/src/lib/base/x86/vm_session.cc new file mode 100644 index 0000000000..c1e39ebbd5 --- /dev/null +++ b/repos/base-foc/src/lib/base/x86/vm_session.cc @@ -0,0 +1,1273 @@ +/* + * \brief Client-side VM session interface + * \author Alexander Boettcher + * \date 2018-08-27 + */ + +/* + * 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. + */ + +#include +#include +#include +#include +#include + +#include + +#include + +namespace Fiasco { +#include +#include +#include +#include +#include +#include +#include +} + +#include +#include + +enum Virt { VMX, SVM, UNKNOWN }; + +using namespace Genode; + +static uint32_t svm_features() +{ + uint32_t cpuid = 0x8000000a, edx = 0, ebx = 0, ecx = 0; + asm volatile ("cpuid" : "+a" (cpuid), "=d" (edx), "=b"(ebx), "=c"(ecx)); + return edx; +} + +static bool svm_np() { return svm_features() & (1U << 0); } +static bool svm_instr_decode_support() { return svm_features() & (1U << 7); } + +struct Vcpu; + +static Genode::Registry > vcpus; +static unsigned vcpu_id = 0; + +struct Vcpu : Genode::Thread +{ + private: + + enum Vmcs { + IRQ_WINDOW = 1U << 2, + + EXI_REASON = 0x4402, + DR7 = 0x681a, + + CR0 = 0x6800, + CR0_MASK = 0x6000, + CR4_MASK = 0x6002, + CR0_SHADOW = 0x6004, + CR4_SHADOW = 0x6006, + + CR3 = 0x6802, + CR4 = 0x6804, + SP = 0x681c, + IP = 0x681e, + INST_LEN = 0x440c, + FLAGS = 0x6820, + EFER = 0x2806, + CTRL_0 = 0x4002, + CTRL_1 = 0x401e, + + CS_SEL = 0x0802, + CS_LIMIT = 0x4802, + CS_AR = 0x4816, + CS_BASE = 0x6808, + + SS_SEL = 0x0804, + SS_LIMIT = 0x4804, + SS_AR = 0x4818, + SS_BASE = 0x680a, + + ES_SEL = 0x0800, + ES_LIMIT = 0x4800, + ES_AR = 0x4814, + ES_BASE = 0x6806, + + DS_SEL = 0x0806, + DS_LIMIT = 0x4806, + DS_AR = 0x481a, + DS_BASE = 0x680c, + + FS_SEL = 0x0808, + FS_LIMIT = 0x4808, + FS_AR = 0x481c, + FS_BASE = 0x680e, + + GS_SEL = 0x080a, + GS_LIMIT = 0x480a, + GS_AR = 0x481e, + GS_BASE = 0x6810, + + LDTR_SEL = 0x080c, + LDTR_LIMIT = 0x480c, + LDTR_AR = 0x4820, + LDTR_BASE = 0x6812, + + TR_SEL = 0x080e, + TR_LIMIT = 0x480e, + TR_AR = 0x4822, + TR_BASE = 0x6814, + + IDTR_LIMIT = 0x4812, + IDTR_BASE = 0x6818, + + GDTR_LIMIT = 0x4810, + GDTR_BASE = 0x6816, + + GUEST_PHYS = 0x2400, + EXIT_QUAL = 0x6400, + + SYSENTER_CS = 0x482a, + SYSENTER_SP = 0x6824, + SYSENTER_IP = 0x6826, + + STATE_INTR = 0x4824, + STATE_ACTV = 0x4826, + + INTR_INFO = 0x4016, + INTR_ERROR = 0x4018, + + IDT_INFO = 0x4408, + IDT_ERROR = 0x440a, + + EXIT_CTRL = 0x400c, + ENTRY_CTRL = 0x4012, + + TSC_OFF_LO = 0x2010, + TSC_OFF_HI = 0x2011, + + CR4_VMX = 1 << 13, + INTEL_EXIT_INVALID = 0x21, + + }; + + enum Vmcb + { + CTRL0_INTR = 1u << 0, + CTRL0_NMI = 1u << 1, + CTRL0_INIT = 1u << 3, + CTRL0_INVD = 1u << 22, + CTRL0_HLT = 1u << 24, + CTRL0_IO = 1u << 27, + CTRL0_MSR = 1u << 28, + CTRL0_SHUTDOWN = 1u << 31, + + AMD_EXIT_INVALID = 0xfd + }; + + enum { + CR0_PE = 0, /* 1U << 0 - not needed in case of UG */ + CR0_CP = 1U << 1, + CR0_NE = 1U << 5, + CR0_NM = 1U << 29, + CR0_CD = 1U << 30, + CR0_PG = 0 /* 1U << 31 - not needed in case of UG */ + }; + + addr_t const vmcb_ctrl0 = CTRL0_INTR | CTRL0_NMI | CTRL0_INIT | + CTRL0_INVD | CTRL0_HLT | CTRL0_IO | + CTRL0_MSR | CTRL0_SHUTDOWN; + + enum { EXIT_ON_HLT = 1U << 7 }; + addr_t const _vmcs_ctrl0 = EXIT_ON_HLT; + + addr_t const vmcs_cr0_mask = CR0_PE | CR0_CP | CR0_NM | CR0_NE | CR0_CD | CR0_PG; + addr_t const vmcs_cr0_set = 0; + + addr_t const vmcs_cr4_mask = CR4_VMX; + addr_t const vmcs_cr4_set = CR4_VMX; + + Signal_context_capability _signal; + Semaphore _wake_up { 0 }; + Semaphore &_handler_ready; + Vm_session_client::Vcpu_id _id; + addr_t _state { 0 }; + addr_t _task { 0 }; + enum Virt const _vm_type; + uint64_t _tsc_offset { 0 }; + + enum + { + VMEXIT_STARTUP = 0xfe, + VMEXIT_PAUSED = 0xff, + STACK_SIZE = 0x3000, + }; + + + enum State { + NONE = 0, + PAUSE = 1, + RUN = 2, + TERMINATE = 3, + } _remote { PAUSE }; + Lock _remote_lock { Lock::UNLOCKED }; + + void entry() override + { + _wake_up.down(); + + { + Lock::Guard guard(_remote_lock); + + /* leave scope for Thread::join() - vCPU setup failed */ + if (_remote == TERMINATE) + return; + + _remote = NONE; + } + + /* reserved ranged for state of vCPUs - see platform.cc */ + Genode::addr_t const vcpu_addr = 0x1000 + 0x1000 * _id.id; + Fiasco::l4_vcpu_state_t * const vcpu = reinterpret_cast(vcpu_addr); + + if (!l4_vcpu_check_version(vcpu)) + Genode::error("vCPU version mismatch kernel vs user-land - ", + vcpu->version, "!=", + (int)Fiasco::L4_VCPU_STATE_VERSION); + + using namespace Fiasco; + l4_vm_svm_vmcb_t *vmcb = reinterpret_cast(vcpu_addr + L4_VCPU_OFFSET_EXT_STATE); + void * vmcs = reinterpret_cast(vcpu_addr + L4_VCPU_OFFSET_EXT_STATE); + + /* set vm page table */ + vcpu->user_task = _task; + + Vm_state &state = *reinterpret_cast(_state); + state = Vm_state {}; + + /* initial startup VM exit to get valid VM state */ + if (_vm_type == Virt::VMX) { + _read_intel_state(state, vmcs, vcpu); + } + if (_vm_type == Virt::SVM) { + _read_amd_state(state, vmcb, vcpu); + } + + state.exit_reason = VMEXIT_STARTUP; + Genode::Signal_transmitter(_signal).submit(); + + _handler_ready.down(); + _wake_up.down(); + + /* + * Fiasoc.OC peculiarities + */ + if (_vm_type == Virt::SVM) { + enum { AMD_SVM_ENABLE = 1 << 12 }; + state.efer.value(state.efer.value() | AMD_SVM_ENABLE); + } + if (_vm_type == Virt::SVM) { + /* special handling on missing NPT support */ + vmcb->control_area.np_enable = svm_np(); + if (!vmcb->control_area.np_enable) + vmcb->control_area.intercept_exceptions |= 1 << 14; + + /* XXX - further stuff needed .. */ + if (!svm_instr_decode_support()) { + vmcb->control_area.intercept_instruction0 = vmcb_ctrl0; + vmcb->control_area.intercept_rd_crX = 0x0001; /* cr0 */ + vmcb->control_area.intercept_wr_crX = 0x0001; /* cr0 */ + } + } + if (_vm_type == Virt::VMX) { + Fiasco::l4_vm_vmx_write(vmcs, Vmcs::CR0_MASK, vmcs_cr0_mask); + Fiasco::l4_vm_vmx_write(vmcs, Vmcs::CR4_MASK, vmcs_cr4_mask); + Fiasco::l4_vm_vmx_write(vmcs, Vmcs::CR4_SHADOW, 0); + state.cr4.value(vmcs_cr4_set); + + enum { + EXIT_SAVE_EFER = 1U << 20, + ENTRY_LOAD_EFER = 1U << 15, + }; + Fiasco::l4_vm_vmx_write(vmcs, Vmcs::EXIT_CTRL, EXIT_SAVE_EFER); + Fiasco::l4_vm_vmx_write(vmcs, Vmcs::ENTRY_CTRL, ENTRY_LOAD_EFER); + } + + State local_state { NONE }; + + while (true) { + /* read in requested state from remote threads */ + { + Lock::Guard guard(_remote_lock); + local_state = _remote; + _remote = NONE; + } + + if (local_state == NONE) { + _wake_up.down(); + continue; + } + if (local_state == PAUSE) { + + if (_vm_type == Virt::SVM) + _write_amd_state(state, vmcb, vcpu); + if (_vm_type == Virt::VMX) + _write_intel_state(state, vmcs, vcpu); + + /* consume spurious notifications */ + Fiasco::l4_cap_idx_t tid = native_thread().kcap; + Fiasco::l4_cap_idx_t irq = tid + Fiasco::TASK_VCPU_IRQ_CAP; + l4_irq_receive(irq, L4_IPC_RECV_TIMEOUT_0); + + state.exit_reason = VMEXIT_PAUSED; + + if (_vm_type == Virt::SVM) + _read_amd_state(state, vmcb, vcpu); + if (_vm_type == Virt::VMX) + _read_intel_state(state, vmcs, vcpu); + + /* notify VM handler */ + Genode::Signal_transmitter(_signal).submit(); + + /* + * Wait until VM handler is really really done, + * otherwise we lose state. + */ + _handler_ready.down(); + continue; + } + + if (local_state != RUN) { + Genode::error("unknown vcpu state ", (int)local_state); + while (true) { _remote_lock.lock(); } + } + + /* transfer vCPU state to Fiasco.OC */ + if (_vm_type == Virt::SVM) + _write_amd_state(state, vmcb, vcpu); + if (_vm_type == Virt::VMX) + _write_intel_state(state, vmcs, vcpu); + + vcpu->saved_state = L4_VCPU_F_USER_MODE | L4_VCPU_F_FPU_ENABLED; + + /* tell Fiasco.OC to run the vCPU */ + l4_msgtag_t tag = l4_thread_vcpu_resume_start(); + tag = l4_thread_vcpu_resume_commit(L4_INVALID_CAP, tag); + + /* got VM exit or interrupted by asynchronous signal */ + uint64_t reason = 0; + + state = Vm_state {}; + + if (_vm_type == Virt::SVM) { + reason = vmcb->control_area.exitcode; + if (reason == 0x400) /* no NPT support */ + reason = 0xfc; + + /* remotely PAUSE was called */ + if (l4_error(tag) && reason == 0x60) { + reason = VMEXIT_PAUSED; + + Lock::Guard guard(_remote_lock); + if (_remote == PAUSE) { + _remote = NONE; + _wake_up.down(); + } + + /* consume notification */ + Fiasco::l4_cap_idx_t tid = native_thread().kcap; + Fiasco::l4_cap_idx_t irq = tid + Fiasco::TASK_VCPU_IRQ_CAP; + l4_irq_receive(irq, L4_IPC_NEVER); + } + + state.exit_reason = reason & 0xff; + _read_amd_state(state, vmcb, vcpu); + } + + if (_vm_type == Virt::VMX) { + reason = Fiasco::l4_vm_vmx_read_32(vmcs, Vmcs::EXI_REASON); + + /* remotely PAUSE was called */ + if (l4_error(tag) && reason == 0x1) { + reason = VMEXIT_PAUSED; + + Lock::Guard guard(_remote_lock); + if (_remote == PAUSE) { + _remote = NONE; + _wake_up.down(); + } + + /* consume notification */ + Fiasco::l4_cap_idx_t tid = native_thread().kcap; + Fiasco::l4_cap_idx_t irq = tid + Fiasco::TASK_VCPU_IRQ_CAP; + l4_irq_receive(irq, L4_IPC_RECV_TIMEOUT_0); + } + + state.exit_reason = reason & 0xff; + _read_intel_state(state, vmcs, vcpu); + } + + /* notify VM handler */ + Genode::Signal_transmitter(_signal).submit(); + + /* + * Wait until VM handler is really really done, + * otherwise we lose state. + */ + _handler_ready.down(); + } + } + + /* + * Convert to Intel format comprising 32 bits. + */ + addr_t _convert_ar(addr_t value) { + return ((value << 4) & 0x1f000) | (value & 0xff); } + + /* + * Convert to AMD (and Genode) format comprising 16 bits. + */ + uint16_t _convert_ar_16(addr_t value) { + return ((value & 0x1f000) >> 4) | (value & 0xff); } + + void _read_intel_state(Vm_state &state, void *vmcs, + Fiasco::l4_vcpu_state_t *vcpu) + { + state.ax.value(vcpu->r.ax); + state.cx.value(vcpu->r.cx); + state.dx.value(vcpu->r.dx); + state.bx.value(vcpu->r.bx); + + state.bp.value(vcpu->r.bp); + state.di.value(vcpu->r.di); + state.si.value(vcpu->r.si); + + state.flags.value(Fiasco::l4_vm_vmx_read(vmcs, Vmcs::FLAGS)); + + state.sp.value(Fiasco::l4_vm_vmx_read(vmcs, Vmcs::SP)); + + state.ip.value(Fiasco::l4_vm_vmx_read(vmcs, Vmcs::IP)); + state.ip_len.value(Fiasco::l4_vm_vmx_read(vmcs, Vmcs::INST_LEN)); + + state.dr7.value(Fiasco::l4_vm_vmx_read(vmcs, Vmcs::DR7)); + +#ifdef __x86_64__ + state.r8.value(vcpu->r.r8); + state.r9.value(vcpu->r.r9); + state.r10.value(vcpu->r.r10); + state.r11.value(vcpu->r.r11); + state.r12.value(vcpu->r.r12); + state.r13.value(vcpu->r.r13); + state.r14.value(vcpu->r.r14); + state.r15.value(vcpu->r.r15); +#endif + + { + addr_t const cr0 = Fiasco::l4_vm_vmx_read(vmcs, Vmcs::CR0); + addr_t const cr0_shadow = Fiasco::l4_vm_vmx_read(vmcs, Vmcs::CR0_SHADOW); + state.cr0.value((cr0 & ~vmcs_cr0_mask) | (cr0_shadow & vmcs_cr0_mask)); + if (state.cr0.value() != cr0_shadow) { + Genode::error("reset cr0_shadow to cr0 ", Genode::Hex(cr0), + " ", Genode::Hex(cr0_shadow), "->", + Genode::Hex(state.cr0.value())); + Fiasco::l4_vm_vmx_write(vmcs, Vmcs::CR0_SHADOW, state.cr0.value()); + } + } + + unsigned const cr2 = Fiasco::l4_vm_vmx_get_cr2_index(vmcs); + state.cr2.value(Fiasco::l4_vm_vmx_read(vmcs, cr2)); + state.cr3.value(Fiasco::l4_vm_vmx_read(vmcs, Vmcs::CR3)); + + { + addr_t const cr4 = Fiasco::l4_vm_vmx_read(vmcs, Vmcs::CR4); + addr_t const cr4_shadow = Fiasco::l4_vm_vmx_read(vmcs, Vmcs::CR4_SHADOW); + state.cr4.value((cr4 & ~vmcs_cr4_mask) | (cr4_shadow & vmcs_cr4_mask)); + if (state.cr4.value() != cr4_shadow) { + Genode::error("reset cr0_shadow to cr4 ", Genode::Hex(cr4), + " ", Genode::Hex(cr4_shadow), "->", + Genode::Hex(state.cr4.value())); + Fiasco::l4_vm_vmx_write(vmcs, Vmcs::CR4_SHADOW, + state.cr4.value()); + } + } + +/* + state.cs.value(Segment{utcb.cs.sel, utcb.cs.ar, utcb.cs.limit, + utcb.cs.base}); +*/ + using Fiasco::l4_vm_vmx_read; + using Fiasco::l4_vm_vmx_read_16; + using Fiasco::l4_vm_vmx_read_32; + using Fiasco::l4_vm_vmx_read_nat; + typedef Genode::Vm_state::Segment Segment; + typedef Genode::Vm_state::Range Range; + + { + Segment cs { l4_vm_vmx_read_16(vmcs, Vmcs::CS_SEL), + _convert_ar_16(l4_vm_vmx_read(vmcs, Vmcs::CS_AR)), + l4_vm_vmx_read_32(vmcs, Vmcs::CS_LIMIT), + l4_vm_vmx_read_nat(vmcs, Vmcs::CS_BASE) }; + + state.cs.value(cs); + } + + { + Segment ss { l4_vm_vmx_read_16(vmcs, Vmcs::SS_SEL), + _convert_ar_16(l4_vm_vmx_read(vmcs, Vmcs::SS_AR)), + l4_vm_vmx_read_32(vmcs, Vmcs::SS_LIMIT), + l4_vm_vmx_read_nat(vmcs, Vmcs::SS_BASE) }; + + state.ss.value(ss); + } + + { + Segment es { l4_vm_vmx_read_16(vmcs, Vmcs::ES_SEL), + _convert_ar_16(l4_vm_vmx_read(vmcs, Vmcs::ES_AR)), + l4_vm_vmx_read_32(vmcs, Vmcs::ES_LIMIT), + l4_vm_vmx_read_nat(vmcs, Vmcs::ES_BASE) }; + + state.es.value(es); + } + + { + Segment ds { l4_vm_vmx_read_16(vmcs, Vmcs::DS_SEL), + _convert_ar_16(l4_vm_vmx_read(vmcs, Vmcs::DS_AR)), + l4_vm_vmx_read_32(vmcs, Vmcs::DS_LIMIT), + l4_vm_vmx_read_nat(vmcs, Vmcs::DS_BASE) }; + + state.ds.value(ds); + } + + { + Segment fs { l4_vm_vmx_read_16(vmcs, Vmcs::FS_SEL), + _convert_ar_16(l4_vm_vmx_read(vmcs, Vmcs::FS_AR)), + l4_vm_vmx_read_32(vmcs, Vmcs::FS_LIMIT), + l4_vm_vmx_read_nat(vmcs, Vmcs::FS_BASE) }; + + state.fs.value(fs); + } + + { + Segment gs { l4_vm_vmx_read_16(vmcs, Vmcs::GS_SEL), + _convert_ar_16(l4_vm_vmx_read(vmcs, Vmcs::GS_AR)), + l4_vm_vmx_read_32(vmcs, Vmcs::GS_LIMIT), + l4_vm_vmx_read_nat(vmcs, Vmcs::GS_BASE) }; + + state.gs.value(gs); + } + + { + Segment tr { l4_vm_vmx_read_16(vmcs, Vmcs::TR_SEL), + _convert_ar_16(l4_vm_vmx_read(vmcs, Vmcs::TR_AR)), + l4_vm_vmx_read_32(vmcs, Vmcs::TR_LIMIT), + l4_vm_vmx_read_nat(vmcs, Vmcs::TR_BASE) }; + + state.tr.value(tr); + } + + { + Segment ldtr { l4_vm_vmx_read_16(vmcs, Vmcs::LDTR_SEL), + _convert_ar_16(l4_vm_vmx_read(vmcs, Vmcs::LDTR_AR)), + l4_vm_vmx_read_32(vmcs, Vmcs::LDTR_LIMIT), + l4_vm_vmx_read_nat(vmcs, Vmcs::LDTR_BASE) }; + + state.ldtr.value(ldtr); + } + + state.gdtr.value(Range{l4_vm_vmx_read_nat(vmcs, Vmcs::GDTR_BASE), + l4_vm_vmx_read_32(vmcs, Vmcs::GDTR_LIMIT)}); + + state.idtr.value(Range{l4_vm_vmx_read_nat(vmcs, Vmcs::IDTR_BASE), + l4_vm_vmx_read_32(vmcs, Vmcs::IDTR_LIMIT)}); + + state.sysenter_cs.value(l4_vm_vmx_read(vmcs, Vmcs::SYSENTER_CS)); + state.sysenter_sp.value(l4_vm_vmx_read(vmcs, Vmcs::SYSENTER_SP)); + state.sysenter_ip.value(l4_vm_vmx_read(vmcs, Vmcs::SYSENTER_IP)); + + state.qual_primary.value(l4_vm_vmx_read(vmcs, Vmcs::EXIT_QUAL)); + state.qual_secondary.value(l4_vm_vmx_read(vmcs, Vmcs::GUEST_PHYS)); + + state.ctrl_primary.value(l4_vm_vmx_read(vmcs, Vmcs::CTRL_0)); + state.ctrl_secondary.value(l4_vm_vmx_read(vmcs, Vmcs::CTRL_1)); + + if (state.exit_reason == INTEL_EXIT_INVALID || + state.exit_reason == VMEXIT_PAUSED) + { + state.inj_info.value(l4_vm_vmx_read(vmcs, Vmcs::INTR_INFO)); + state.inj_error.value(l4_vm_vmx_read(vmcs, Vmcs::INTR_ERROR)); + } else { + state.inj_info.value(l4_vm_vmx_read(vmcs, Vmcs::IDT_INFO)); + state.inj_error.value(l4_vm_vmx_read(vmcs, Vmcs::IDT_ERROR)); + } + + state.intr_state.value(l4_vm_vmx_read(vmcs, Vmcs::STATE_INTR)); + state.actv_state.value(l4_vm_vmx_read(vmcs, Vmcs::STATE_ACTV)); + + state.tsc.value(Trace::timestamp()); + state.tsc_offset.value(_tsc_offset); + + state.efer.value(l4_vm_vmx_read(vmcs, Vmcs::EFER)); + + /* XXX missing */ +#if 0 + if (state.pdpte_0_updated() || state.pdpte_1_updated() || + if (state.star_updated() || state.lstar_updated() || + state.fmask_updated() || state.kernel_gs_base_updated()) { + if (state.tpr_updated() || state.tpr_threshold_updated()) { +#endif + } + + void _read_amd_state(Vm_state &state, Fiasco::l4_vm_svm_vmcb_t *vmcb, + Fiasco::l4_vcpu_state_t * const vcpu) + { + state.ax.value(vmcb->state_save_area.rax); + state.cx.value(vcpu->r.cx); + state.dx.value(vcpu->r.dx); + state.bx.value(vcpu->r.bx); + + state.di.value(vcpu->r.di); + state.si.value(vcpu->r.si); + state.bp.value(vcpu->r.bp); + + state.flags.value(vmcb->state_save_area.rflags); + + state.sp.value(vmcb->state_save_area.rsp); + + state.ip.value(vmcb->state_save_area.rip); + state.ip_len.value(state.ip_len.value()); /* unsupported on AMD */ + + state.dr7.value(vmcb->state_save_area.dr7); + +#ifdef __x86_64__ + state.r8.value(vcpu->r.r8); + state.r9.value(vcpu->r.r9); + state.r10.value(vcpu->r.r10); + state.r11.value(vcpu->r.r11); + state.r12.value(vcpu->r.r12); + state.r13.value(vcpu->r.r13); + state.r14.value(vcpu->r.r14); + state.r15.value(vcpu->r.r15); +#endif + + state.cr0.value(vmcb->state_save_area.cr0); + state.cr2.value(vmcb->state_save_area.cr2); + state.cr3.value(vmcb->state_save_area.cr3); + state.cr4.value(vmcb->state_save_area.cr4); + + typedef Genode::Vm_state::Segment Segment; + + state.cs.value(Segment{vmcb->state_save_area.cs.selector, + vmcb->state_save_area.cs.attrib, + vmcb->state_save_area.cs.limit, + (addr_t)vmcb->state_save_area.cs.base}); + + state.ss.value(Segment{vmcb->state_save_area.ss.selector, + vmcb->state_save_area.ss.attrib, + vmcb->state_save_area.ss.limit, + (addr_t)vmcb->state_save_area.ss.base}); + + state.es.value(Segment{vmcb->state_save_area.es.selector, + vmcb->state_save_area.es.attrib, + vmcb->state_save_area.es.limit, + (addr_t)vmcb->state_save_area.es.base}); + + state.ds.value(Segment{vmcb->state_save_area.ds.selector, + vmcb->state_save_area.ds.attrib, + vmcb->state_save_area.ds.limit, + (addr_t)vmcb->state_save_area.ds.base}); + + state.fs.value(Segment{vmcb->state_save_area.fs.selector, + vmcb->state_save_area.fs.attrib, + vmcb->state_save_area.fs.limit, + (addr_t)vmcb->state_save_area.fs.base}); + + state.gs.value(Segment{vmcb->state_save_area.gs.selector, + vmcb->state_save_area.gs.attrib, + vmcb->state_save_area.gs.limit, + (addr_t)vmcb->state_save_area.gs.base}); + + state.tr.value(Segment{vmcb->state_save_area.tr.selector, + vmcb->state_save_area.tr.attrib, + vmcb->state_save_area.tr.limit, + (addr_t)vmcb->state_save_area.tr.base}); + + state.ldtr.value(Segment{vmcb->state_save_area.ldtr.selector, + vmcb->state_save_area.ldtr.attrib, + vmcb->state_save_area.ldtr.limit, + (addr_t)vmcb->state_save_area.ldtr.base}); + + typedef Genode::Vm_state::Range Range; + + state.gdtr.value(Range{(addr_t)vmcb->state_save_area.gdtr.base, + vmcb->state_save_area.gdtr.limit}); + + state.idtr.value(Range{(addr_t)vmcb->state_save_area.idtr.base, + vmcb->state_save_area.idtr.limit}); + + state.sysenter_cs.value(vmcb->state_save_area.sysenter_cs); + state.sysenter_sp.value(vmcb->state_save_area.sysenter_esp); + state.sysenter_ip.value(vmcb->state_save_area.sysenter_eip); + + state.qual_primary.value(vmcb->control_area.exitinfo1); + state.qual_secondary.value(vmcb->control_area.exitinfo2); + + uint32_t inj_info = 0; + uint32_t inj_error = 0; + if (state.exit_reason == AMD_EXIT_INVALID || + state.exit_reason == VMEXIT_PAUSED) + { + inj_info = vmcb->control_area.eventinj; + inj_error = vmcb->control_area.eventinj >> 32; + } else { + inj_info = vmcb->control_area.exitintinfo; + inj_error = vmcb->control_area.exitintinfo >> 32; + } + state.inj_info.value(inj_info); + state.inj_error.value(inj_error); + + state.intr_state.value(vmcb->control_area.interrupt_shadow); + state.actv_state.value(0); + + state.tsc.value(Trace::timestamp()); + state.tsc_offset.value(_tsc_offset); + + if (state.efer.valid()) { + Genode::error("efer not implemented"); + } + + if (state.pdpte_0.valid() || state.pdpte_1.valid() || + state.pdpte_2.valid() || state.pdpte_3.valid()) { + + Genode::error("pdpte not implemented"); + } + + if (state.star.valid() || state.lstar.valid() || + state.fmask.valid() || state.kernel_gs_base.valid()) { + + Genode::error("star, fstar, fmask, kernel_gs_base not implemented"); + } + + if (state.tpr.valid() || state.tpr_threshold.valid()) { + Genode::error("tpr not implemented"); + } + } + + void _write_intel_state(Vm_state &state, void *vmcs, + Fiasco::l4_vcpu_state_t *vcpu) + { + using Fiasco::l4_vm_vmx_write; + + if (state.ax.valid() || state.cx.valid() || state.dx.valid() || + state.bx.valid() || state.bp.valid() || state.di.valid() || + state.si.valid()) { + vcpu->r.ax = state.ax.value(); + vcpu->r.cx = state.cx.value(); + vcpu->r.dx = state.dx.value(); + vcpu->r.bx = state.bx.value(); + vcpu->r.bp = state.bp.value(); + vcpu->r.di = state.di.value(); + vcpu->r.si = state.si.value(); + } + + if (state.r8.valid() || state.r9.valid() || state.r10.valid() || + state.r11.valid() || state.r12.valid() || state.r13.valid() || + state.r14.valid() || state.r15.valid()) { +#ifdef __x86_64__ + vcpu->r.r8 = state.r8.value(); + vcpu->r.r9 = state.r9.value(); + vcpu->r.r10 = state.r10.value(); + vcpu->r.r11 = state.r11.value(); + vcpu->r.r12 = state.r12.value(); + vcpu->r.r13 = state.r13.value(); + vcpu->r.r14 = state.r14.value(); + vcpu->r.r15 = state.r15.value(); +#endif + } + + if (state.tsc_offset.valid()) { + _tsc_offset += state.tsc_offset.value(); + l4_vm_vmx_write(vmcs, Vmcs::TSC_OFF_LO, _tsc_offset & 0xffffffffu); + l4_vm_vmx_write(vmcs, Vmcs::TSC_OFF_HI, (_tsc_offset >> 32) & 0xffffffffu); + } + + if (state.star.valid() || state.lstar.valid() || + state.fmask.valid() || state.kernel_gs_base.valid()) + Genode::error(__LINE__, " not implemented"); + + if (state.tpr.valid() || state.tpr_threshold.valid()) + Genode::error(__LINE__, " not implemented"); + + if (state.dr7.valid()) + l4_vm_vmx_write(vmcs, Vmcs::DR7, state.dr7.value()); + + if (state.cr0.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::CR0, vmcs_cr0_set | (~vmcs_cr0_mask & state.cr0.value())); + l4_vm_vmx_write(vmcs, Vmcs::CR0_SHADOW, state.cr0.value()); + + #if 0 /* if CPU has xsave feature bit ... see Vm::load_guest_xcr0 */ + l4_vm_vmx_write(vmcs, Fiasco::L4_VM_VMX_VMCS_XCR0, state.cr0.value()); + #endif + } + + if (state.cr2.valid()) { + unsigned const cr2 = Fiasco::l4_vm_vmx_get_cr2_index(vmcs); + l4_vm_vmx_write(vmcs, cr2, state.cr2.value()); + } + + if (state.cr3.valid()) + l4_vm_vmx_write(vmcs, Vmcs::CR3, state.cr3.value()); + + if (state.cr4.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::CR4, + vmcs_cr4_set | (~vmcs_cr4_mask & state.cr4.value())); + l4_vm_vmx_write(vmcs, Vmcs::CR4_SHADOW, state.cr4.value()); + } + +#if 1 + if (state.inj_info.valid() || state.inj_error.valid()) { + addr_t ctrl_0 = Fiasco::l4_vm_vmx_read(vmcs, Vmcs::CTRL_0); + + if (state.inj_info.value() & 0x2000) + Genode::warning("unimplemented ", state.inj_info.value() & 0x1000, " ", state.inj_info.value() & 0x2000, " ", Genode::Hex(ctrl_0), " ", Genode::Hex(state.ctrl_secondary.value())); + + if (state.inj_info.value() & 0x1000) + ctrl_0 |= Vmcs::IRQ_WINDOW; + else + ctrl_0 &= ~Vmcs::IRQ_WINDOW; + + state.ctrl_primary.value(ctrl_0); + + l4_vm_vmx_write(vmcs, Vmcs::INTR_INFO, + state.inj_info.value() & ~0x3000); + l4_vm_vmx_write(vmcs, Vmcs::INTR_ERROR, + state.inj_error.value()); + } +#endif + + if (state.flags.valid()) + l4_vm_vmx_write(vmcs, Vmcs::FLAGS, state.flags.value()); + + if (state.sp.valid()) + l4_vm_vmx_write(vmcs, Vmcs::SP, state.sp.value()); + + if (state.ip.valid()) + l4_vm_vmx_write(vmcs, Vmcs::IP, state.ip.value()); + + if (state.efer.valid()) + l4_vm_vmx_write(vmcs, Vmcs::EFER, state.efer.value()); + + if (state.ctrl_primary.valid()) + l4_vm_vmx_write(vmcs, Vmcs::CTRL_0, + _vmcs_ctrl0 | state.ctrl_primary.value()); + + if (state.ctrl_secondary.valid()) + l4_vm_vmx_write(vmcs, Vmcs::CTRL_1, + state.ctrl_secondary.value()); + + if (state.intr_state.valid()) + l4_vm_vmx_write(vmcs, Vmcs::STATE_INTR, + state.intr_state.value()); + + if (state.actv_state.valid()) + l4_vm_vmx_write(vmcs, Vmcs::STATE_ACTV, + state.actv_state.valid()); + + if (state.cs.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::CS_SEL, state.cs.value().sel); + l4_vm_vmx_write(vmcs, Vmcs::CS_AR, _convert_ar(state.cs.value().ar)); + l4_vm_vmx_write(vmcs, Vmcs::CS_LIMIT, state.cs.value().limit); + l4_vm_vmx_write(vmcs, Vmcs::CS_BASE, state.cs.value().base); + } + + if (state.ss.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::SS_SEL, state.ss.value().sel); + l4_vm_vmx_write(vmcs, Vmcs::SS_AR, _convert_ar(state.ss.value().ar)); + l4_vm_vmx_write(vmcs, Vmcs::SS_LIMIT, state.ss.value().limit); + l4_vm_vmx_write(vmcs, Vmcs::SS_BASE, state.ss.value().base); + } + + if (state.es.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::ES_SEL, state.es.value().sel); + l4_vm_vmx_write(vmcs, Vmcs::ES_AR, _convert_ar(state.es.value().ar)); + l4_vm_vmx_write(vmcs, Vmcs::ES_LIMIT, state.es.value().limit); + l4_vm_vmx_write(vmcs, Vmcs::ES_BASE, state.es.value().base); + } + + if (state.ds.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::DS_SEL, state.ds.value().sel); + l4_vm_vmx_write(vmcs, Vmcs::DS_AR, _convert_ar(state.ds.value().ar)); + l4_vm_vmx_write(vmcs, Vmcs::DS_LIMIT, state.ds.value().limit); + l4_vm_vmx_write(vmcs, Vmcs::DS_BASE, state.ds.value().base); + } + + if (state.fs.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::FS_SEL, state.fs.value().sel); + l4_vm_vmx_write(vmcs, Vmcs::FS_AR, _convert_ar(state.fs.value().ar)); + l4_vm_vmx_write(vmcs, Vmcs::FS_LIMIT, state.fs.value().limit); + l4_vm_vmx_write(vmcs, Vmcs::FS_BASE, state.fs.value().base); + } + + if (state.gs.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::GS_SEL, state.gs.value().sel); + l4_vm_vmx_write(vmcs, Vmcs::GS_AR, _convert_ar(state.gs.value().ar)); + l4_vm_vmx_write(vmcs, Vmcs::GS_LIMIT, state.gs.value().limit); + l4_vm_vmx_write(vmcs, Vmcs::GS_BASE, state.gs.value().base); + } + + if (state.tr.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::TR_SEL, state.tr.value().sel); + l4_vm_vmx_write(vmcs, Vmcs::TR_AR, _convert_ar(state.tr.value().ar)); + l4_vm_vmx_write(vmcs, Vmcs::TR_LIMIT, state.tr.value().limit); + l4_vm_vmx_write(vmcs, Vmcs::TR_BASE, state.tr.value().base); + } + + if (state.ldtr.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::LDTR_SEL, state.ldtr.value().sel); + l4_vm_vmx_write(vmcs, Vmcs::LDTR_AR, _convert_ar(state.ldtr.value().ar)); + l4_vm_vmx_write(vmcs, Vmcs::LDTR_LIMIT, state.ldtr.value().limit); + l4_vm_vmx_write(vmcs, Vmcs::LDTR_BASE, state.ldtr.value().base); + } + + if (state.idtr.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::IDTR_BASE, state.idtr.value().base); + l4_vm_vmx_write(vmcs, Vmcs::IDTR_LIMIT, state.idtr.value().limit); + } + + if (state.gdtr.valid()) { + l4_vm_vmx_write(vmcs, Vmcs::GDTR_BASE, state.gdtr.value().base); + l4_vm_vmx_write(vmcs, Vmcs::GDTR_LIMIT, state.gdtr.value().limit); + } + + if (state.pdpte_0.valid()) + Genode::error(__LINE__, " not implemented"); + if (state.pdpte_1.valid()) + Genode::error(__LINE__, " not implemented"); + if (state.pdpte_2.valid()) + Genode::error(__LINE__, " not implemented"); + if (state.pdpte_3.valid()) + Genode::error(__LINE__, " not implemented"); + + if (state.sysenter_cs.valid()) + l4_vm_vmx_write(vmcs, Vmcs::SYSENTER_CS, + state.sysenter_cs.value()); + if (state.sysenter_sp.valid()) + l4_vm_vmx_write(vmcs, Vmcs::SYSENTER_SP, + state.sysenter_sp.value()); + if (state.sysenter_ip.valid()) + l4_vm_vmx_write(vmcs, Vmcs::SYSENTER_IP, + state.sysenter_ip.value()); + } + + void _write_amd_state(Vm_state &state, Fiasco::l4_vm_svm_vmcb_t *vmcb, + Fiasco::l4_vcpu_state_t *vcpu) + { + if (state.ax.valid() || state.cx.valid() || state.dx.valid() || + state.bx.valid() || state.bp.valid() || state.di.valid() || + state.si.valid()) { + vmcb->state_save_area.rax = state.ax.value(); + vcpu->r.ax = state.ax.value(); + vcpu->r.cx = state.cx.value(); + vcpu->r.dx = state.dx.value(); + vcpu->r.bx = state.bx.value(); + vcpu->r.bp = state.bp.value(); + vcpu->r.di = state.di.value(); + vcpu->r.si = state.si.value(); + } + + if (state.r8.valid() || state.r9.valid() || state.r10.valid() || + state.r11.valid() || state.r12.valid() || state.r13.valid() || + state.r14.valid() || state.r15.valid()) { +#ifdef __x86_64__ + vcpu->r.r8 = state.r8.value(); + vcpu->r.r9 = state.r9.value(); + vcpu->r.r10 = state.r10.value(); + vcpu->r.r11 = state.r11.value(); + vcpu->r.r12 = state.r12.value(); + vcpu->r.r13 = state.r13.value(); + vcpu->r.r14 = state.r14.value(); + vcpu->r.r15 = state.r15.value(); +#endif + } + + if (state.tsc_offset.valid()) { + _tsc_offset += state.tsc_offset.value(); + vmcb->control_area.tsc_offset = _tsc_offset; + } + + if (state.star.value() || state.lstar.value() || + state.fmask.value() || state.kernel_gs_base.value()) + Genode::error(__LINE__, " not implemented"); + + if (state.tpr.valid() || state.tpr_threshold.valid()) + Genode::error(__LINE__, " not implemented"); + + if (state.dr7.valid()) + vmcb->state_save_area.dr7 = state.dr7.value(); + + if (state.cr0.valid()) { + vmcb->state_save_area.cr0 = state.cr0.value(); +#if 0 + vmcb->state_save_area.xcr0 = state.cr0(); +#endif + } + + if (state.cr2.valid()) + vmcb->state_save_area.cr2 = state.cr2.value(); + + if (state.cr3.valid()) + vmcb->state_save_area.cr3 = state.cr3.value(); + + if (state.cr4.valid()) + vmcb->state_save_area.cr4 = state.cr4.value(); + + if (state.inj_info.valid()) + Genode::error(__LINE__, " not implemented "); + if (state.inj_error.valid()) + Genode::error(__LINE__, " not implemented "); + + if (state.flags.valid()) + vmcb->state_save_area.rflags = state.flags.value(); + + if (state.sp.valid()) + vmcb->state_save_area.rsp = state.sp.value(); + + if (state.ip.valid()) + vmcb->state_save_area.rip = state.ip.value(); + + if (state.efer.valid()) + vmcb->state_save_area.efer = state.efer.value(); + + if (state.ctrl_primary.valid()) + vmcb->control_area.intercept_instruction0 = vmcb_ctrl0 | + state.ctrl_primary.value(); + + if (state.ctrl_secondary.valid()) + vmcb->control_area.intercept_instruction1 = state.ctrl_secondary.value(); + + if (state.intr_state.valid()) + vmcb->control_area.interrupt_shadow = state.intr_state.value(); + + /* state.actv_state.valid() - not required for AMD */ + + if (state.cs.valid()) { + vmcb->state_save_area.cs.selector = state.cs.value().sel; + vmcb->state_save_area.cs.attrib = state.cs.value().ar; + vmcb->state_save_area.cs.limit = state.cs.value().limit; + vmcb->state_save_area.cs.base = state.cs.value().base; + } + + if (state.ss.valid()) { + vmcb->state_save_area.ss.selector = state.ss.value().sel; + vmcb->state_save_area.ss.attrib = state.ss.value().ar; + vmcb->state_save_area.ss.limit = state.ss.value().limit; + vmcb->state_save_area.ss.base = state.ss.value().base; + } + + if (state.es.valid()) { + vmcb->state_save_area.es.selector = state.es.value().sel; + vmcb->state_save_area.es.attrib = state.es.value().ar; + vmcb->state_save_area.es.limit = state.es.value().limit; + vmcb->state_save_area.es.base = state.es.value().base; + } + + if (state.ds.valid()) { + vmcb->state_save_area.ds.selector = state.ds.value().sel; + vmcb->state_save_area.ds.attrib = state.ds.value().ar; + vmcb->state_save_area.ds.limit = state.ds.value().limit; + vmcb->state_save_area.ds.base = state.ds.value().base; + } + + if (state.fs.valid()) { + vmcb->state_save_area.fs.selector = state.fs.value().sel; + vmcb->state_save_area.fs.attrib = state.fs.value().ar; + vmcb->state_save_area.fs.limit = state.fs.value().limit; + vmcb->state_save_area.fs.base = state.fs.value().base; + } + + if (state.gs.valid()) { + vmcb->state_save_area.gs.selector = state.gs.value().sel; + vmcb->state_save_area.gs.attrib = state.gs.value().ar; + vmcb->state_save_area.gs.limit = state.gs.value().limit; + vmcb->state_save_area.gs.base = state.gs.value().base; + } + + if (state.tr.valid()) { + vmcb->state_save_area.tr.selector = state.tr.value().sel; + vmcb->state_save_area.tr.attrib = state.tr.value().ar; + vmcb->state_save_area.tr.limit = state.tr.value().limit; + vmcb->state_save_area.tr.base = state.tr.value().base; + } + + if (state.ldtr.valid()) { + vmcb->state_save_area.ldtr.selector = state.ldtr.value().sel; + vmcb->state_save_area.ldtr.attrib = state.ldtr.value().ar; + vmcb->state_save_area.ldtr.limit = state.ldtr.value().limit; + vmcb->state_save_area.ldtr.base = state.ldtr.value().base; + } + + if (state.idtr.valid()) { + vmcb->state_save_area.idtr.base = state.idtr.value().base; + vmcb->state_save_area.idtr.limit = state.idtr.value().limit; + } + + if (state.gdtr.valid()) { + vmcb->state_save_area.gdtr.base = state.gdtr.value().base; + vmcb->state_save_area.gdtr.limit = state.gdtr.value().limit; + } + + if (state.pdpte_0.valid()) + Genode::error(__LINE__, " not implemented"); + if (state.pdpte_1.valid()) + Genode::error(__LINE__, " not implemented"); + if (state.pdpte_2.valid()) + Genode::error(__LINE__, " not implemented"); + if (state.pdpte_3.valid()) + Genode::error(__LINE__, " not implemented"); + + if (state.sysenter_cs.valid()) + vmcb->state_save_area.sysenter_cs = state.sysenter_cs.value(); + if (state.sysenter_sp.valid()) + vmcb->state_save_area.sysenter_esp = state.sysenter_sp.value(); + if (state.sysenter_ip.valid()) + vmcb->state_save_area.sysenter_eip = state.sysenter_ip.value(); + } + + public: + + Vcpu(Env &env, Signal_context_capability &cap, + Semaphore &handler_ready, + Vm_session_client::Vcpu_id &id, enum Virt type) + : + Thread(env, "vcpu_thread", STACK_SIZE), _signal(cap), + _handler_ready(handler_ready), _id(id), _vm_type(type) + { } + + bool match(Vm_session_client::Vcpu_id id) { return id.id == _id.id; } + + Genode::Vm_session_client::Vcpu_id id() const { return _id; } + + void assign_ds_state(Region_map &rm, Dataspace_capability cap) + { + _state = rm.attach(cap); + _task = *reinterpret_cast(_state); + *reinterpret_cast(_state) = 0UL; + } + + void resume() { + Lock::Guard guard(_remote_lock); + + if (_remote == RUN) + return; + + _remote = RUN; + _wake_up.up(); + } + + void pause() { + Lock::Guard guard(_remote_lock); + + if (_remote == PAUSE) + return; + + _remote = PAUSE; + + /* recall vCPU */ + Fiasco::l4_cap_idx_t tid = native_thread().kcap; + Fiasco::l4_cap_idx_t irq = tid + Fiasco::TASK_VCPU_IRQ_CAP; + Fiasco::l4_irq_trigger(irq); + + _wake_up.up(); + } + + void terminate() { + _remote = TERMINATE; + _wake_up.up(); + } +}; + + +static enum Virt virt_type(Genode::Env &env) +{ + try { + Genode::Attached_rom_dataspace const info(env, "platform_info"); + Genode::Xml_node const features = info.xml().sub_node("hardware").sub_node("features"); + + if (features.attribute_value("svm", false)) + return Virt::SVM; + + if (features.attribute_value("vmx", false)) + return Virt::VMX; + } catch (...) { } + + return Virt::UNKNOWN; +} + + +Vm_session_client::Vcpu_id +Vm_session_client::create_vcpu(Allocator &alloc, Env &env, + Vm_handler_base &handler) +{ + Vm_session_client::Vcpu_id id = { vcpu_id }; + + enum Virt vm_type = virt_type(env); + if (vm_type == Virt::UNKNOWN) { + Genode::error("unsupported hardware virtualisation"); + return id; + } + + /* create thread that switches modes between thread/cpu */ + Vcpu * vcpu = new (alloc) Registered(vcpus, env, handler._cap, + handler._done, id, vm_type); + + try { + /* now it gets actually valid - vcpu->cap() becomes valid */ + vcpu->start(); + + call(handler._cap, vcpu->id()); + + /* instruct core to let it become a vCPU */ + call(vcpu->cap()); + + vcpu->assign_ds_state(env.rm(), call(vcpu->id())); + } catch (...) { + vcpu->terminate(); + vcpu->join(); + + destroy(alloc, vcpu); + throw; + } + + vcpu_id ++; + return id; +} + +void Vm_session_client::run(Vcpu_id vcpu_id) +{ + vcpus.for_each([&] (Vcpu &vcpu) { + if (vcpu.match(vcpu_id)) + vcpu.resume(); + }); +} + +void Vm_session_client::pause(Vm_session_client::Vcpu_id vcpu_id) +{ + vcpus.for_each([&] (Vcpu &vcpu) { + if (!vcpu.match(vcpu_id)) + return; + + vcpu.pause(); + }); +} + +Dataspace_capability Vm_session_client::cpu_state(Vcpu_id vcpu_id) +{ + Dataspace_capability cap; + + vcpus.for_each([&] (Vcpu &vcpu) { + if (vcpu.match(vcpu_id)) + cap = call(vcpu_id); + }); + + return cap; +} diff --git a/repos/os/run/vmm_x86.run b/repos/os/run/vmm_x86.run index 7197c3cc48..1299fd9a7d 100644 --- a/repos/os/run/vmm_x86.run +++ b/repos/os/run/vmm_x86.run @@ -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