diff --git a/repos/base-hw/lib/mk/spec/x86_64/core-hw-pc.mk b/repos/base-hw/lib/mk/spec/x86_64/core-hw-pc.mk index 872990a6c4..cbfb0dcc48 100644 --- a/repos/base-hw/lib/mk/spec/x86_64/core-hw-pc.mk +++ b/repos/base-hw/lib/mk/spec/x86_64/core-hw-pc.mk @@ -22,9 +22,6 @@ SRC_CC += kernel/vm_thread_on.cc SRC_CC += spec/x86_64/virtualization/kernel/vm.cc SRC_CC += spec/x86_64/virtualization/kernel/svm.cc SRC_CC += spec/x86_64/virtualization/kernel/vmx.cc -SRC_CC += spec/x86_64/virtualization/vm_session_component.cc -SRC_CC += vm_session_common.cc -SRC_CC += vm_session_component.cc SRC_CC += kernel/lock.cc SRC_CC += spec/x86_64/pic.cc SRC_CC += spec/x86_64/pit.cc diff --git a/repos/base-hw/src/core/guest_memory.h b/repos/base-hw/src/core/guest_memory.h new file mode 100644 index 0000000000..5cfe658043 --- /dev/null +++ b/repos/base-hw/src/core/guest_memory.h @@ -0,0 +1,275 @@ +/* + * \brief Guest memory abstraction + * \author Stefan Kalkowski + * \author Benjamin Lamowski + * \date 2024-11-25 + */ + +/* + * Copyright (C) 2015-2024 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__GUEST_MEMORY_H_ +#define _CORE__GUEST_MEMORY_H_ + +/* base includes */ +#include +#include +#include +#include + +/* core includes */ +#include +#include + +namespace Core { class Guest_memory; } + +using namespace Core; + + +class Core::Guest_memory +{ + private: + + using Avl_region = Allocator_avl_tpl; + + using Attach_attr = Genode::Vm_session::Attach_attr; + + Sliced_heap _sliced_heap; + Avl_region _map { &_sliced_heap }; + + uint8_t _remaining_print_count { 10 }; + + void _with_region(addr_t const addr, auto const &fn) + { + Rm_region *region = _map.metadata((void *)addr); + if (region) + fn(*region); + else + if (_remaining_print_count) { + error(__PRETTY_FUNCTION__, " unknown region"); + _remaining_print_count--; + } + } + + public: + + enum class Attach_result { + OK, + INVALID_DS, + OUT_OF_RAM, + OUT_OF_CAPS, + REGION_CONFLICT, + }; + + + Attach_result attach(Region_map_detach &rm_detach, + Dataspace_component &dsc, + addr_t const guest_phys, + Attach_attr attr, + auto const &map_fn) + { + /* + * unsupported - deny otherwise arbitrary physical + * memory can be mapped to a VM + */ + if (dsc.managed()) + return Attach_result::INVALID_DS; + + if (guest_phys & 0xffful || attr.offset & 0xffful || + attr.size & 0xffful) + return Attach_result::INVALID_DS; + + if (!attr.size) { + attr.size = dsc.size(); + + if (attr.offset < attr.size) + attr.size -= attr.offset; + } + + if (attr.size > dsc.size()) + attr.size = dsc.size(); + + if (attr.offset >= dsc.size() || + attr.offset > dsc.size() - attr.size) + return Attach_result::INVALID_DS; + + using Alloc_error = Range_allocator::Alloc_error; + + Attach_result const retval = _map.alloc_addr(attr.size, guest_phys).convert( + + [&] (void *) { + + Rm_region::Attr const region_attr + { + .base = guest_phys, + .size = attr.size, + .write = dsc.writeable() && attr.writeable, + .exec = attr.executable, + .off = attr.offset, + .dma = false, + }; + + /* store attachment info in meta data */ + try { + _map.construct_metadata((void *)guest_phys, + dsc, rm_detach, region_attr); + + } catch (Allocator_avl_tpl::Assign_metadata_failed) { + if (_remaining_print_count) { + error("failed to store attachment info"); + _remaining_print_count--; + } + return Attach_result::INVALID_DS; + } + + Rm_region ®ion = *_map.metadata((void *)guest_phys); + + /* inform dataspace about attachment */ + dsc.attached_to(region); + + return Attach_result::OK; + }, + + [&] (Alloc_error error) { + + switch (error) { + + case Alloc_error::OUT_OF_RAM: + return Attach_result::OUT_OF_RAM; + case Alloc_error::OUT_OF_CAPS: + return Attach_result::OUT_OF_CAPS; + case Alloc_error::DENIED: + { + /* + * Handle attach after partial detach + */ + Rm_region *region_ptr = _map.metadata((void *)guest_phys); + if (!region_ptr) + return Attach_result::REGION_CONFLICT; + + Rm_region ®ion = *region_ptr; + + bool conflict = false; + region.with_dataspace([&] (Dataspace_component &dataspace) { + (void)dataspace; + if (!(dsc.cap() == dataspace.cap())) + conflict = true; + }); + if (conflict) + return Attach_result::REGION_CONFLICT; + + if (guest_phys < region.base() || + guest_phys > region.base() + region.size() - 1) + return Attach_result::REGION_CONFLICT; + } + + }; + + return Attach_result::OK; + } + ); + + if (retval == Attach_result::OK) { + addr_t phys_addr = dsc.phys_addr() + attr.offset; + size_t size = attr.size; + + map_fn(guest_phys, phys_addr, size); + } + + return retval; + } + + + void detach(addr_t guest_phys, + size_t size, + auto const &unmap_fn) + { + if (!size || (guest_phys & 0xffful) || (size & 0xffful)) { + if (_remaining_print_count) { + warning("vm_session: skipping invalid memory detach addr=", + (void *)guest_phys, " size=", (void *)size); + _remaining_print_count--; + } + return; + } + + addr_t const guest_phys_end = guest_phys + (size - 1); + addr_t addr = guest_phys; + do { + Rm_region *region = _map.metadata((void *)addr); + + /* walk region holes page-by-page */ + size_t iteration_size = 0x1000; + + if (region) { + iteration_size = region->size(); + detach_at(region->base(), unmap_fn); + } + + if (addr >= guest_phys_end - (iteration_size - 1)) + break; + + addr += iteration_size; + } while (true); + } + + + Guest_memory(Constrained_ram_allocator &constrained_md_ram_alloc, + Region_map ®ion_map) + : + _sliced_heap(constrained_md_ram_alloc, region_map) + { + /* configure managed VM area */ + _map.add_range(0UL, ~0UL); + } + + ~Guest_memory() + { + /* detach all regions */ + while (true) { + addr_t out_addr = 0; + + if (!_map.any_block_addr(&out_addr)) + break; + + detach_at(out_addr, [](addr_t, size_t) { }); + } + } + + + void detach_at(addr_t addr, + auto const &unmap_fn) + { + _with_region(addr, [&] (Rm_region ®ion) { + + if (!region.reserved()) + reserve_and_flush(addr, unmap_fn); + + /* free the reserved region */ + _map.free(reinterpret_cast(region.base())); + }); + } + + + void reserve_and_flush(addr_t addr, + auto const &unmap_fn) + { + _with_region(addr, [&] (Rm_region ®ion) { + + /* inform dataspace */ + region.with_dataspace([&] (Dataspace_component &dataspace) { + dataspace.detached_from(region); + }); + + region.mark_as_reserved(); + + unmap_fn(region.base(), region.size()); + }); + } +}; + +#endif /* _CORE__GUEST_MEMORY_H_ */ diff --git a/repos/base-hw/src/core/phys_allocated.h b/repos/base-hw/src/core/phys_allocated.h new file mode 100644 index 0000000000..dd640930b5 --- /dev/null +++ b/repos/base-hw/src/core/phys_allocated.h @@ -0,0 +1,79 @@ +/* + * \brief Allocate an object with a physical address + * \author Norman Feske + * \author Benjamin Lamowski + * \date 2024-12-02 + */ + +/* + * Copyright (C) 2024 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__PHYS_ALLOCATED_H_ +#define _CORE__PHYS_ALLOCATED_H_ + +/* base includes */ +#include +#include +#include + +/* core-local includes */ +#include + +namespace Core { + template + class Phys_allocated; +} + +using namespace Core; + + +template +class Core::Phys_allocated : Genode::Noncopyable +{ + private: + + Rpc_entrypoint &_ep; + Ram_allocator &_ram; + Region_map &_rm; + + Attached_ram_dataspace _ds { _ram, _rm, sizeof(T) }; + public: + + T &obj = *_ds.local_addr(); + + Phys_allocated(Rpc_entrypoint &ep, + Ram_allocator &ram, + Region_map &rm) + : + _ep(ep), _ram(ram), _rm(rm) + { + construct_at(&obj); + } + + Phys_allocated(Rpc_entrypoint &ep, + Ram_allocator &ram, + Region_map &rm, + auto const &construct_fn) + : + _ep(ep), _ram(ram), _rm(rm) + { + construct_fn(*this, &obj); + } + + ~Phys_allocated() { obj.~T(); } + + addr_t phys_addr() { + addr_t phys_addr { }; + _ep.apply(_ds.cap(), [&](Dataspace_component *dsc) { + phys_addr = dsc->phys_addr(); + }); + + return phys_addr; + } +}; + +#endif /* _CORE__PHYS_ALLOCATED_H_ */ diff --git a/repos/base-hw/src/core/spec/x86_64/vcpu.h b/repos/base-hw/src/core/spec/x86_64/vcpu.h new file mode 100644 index 0000000000..0d82da6e34 --- /dev/null +++ b/repos/base-hw/src/core/spec/x86_64/vcpu.h @@ -0,0 +1,128 @@ +/* + * \brief Vm_session vCPU + * \author Stefan Kalkowski + * \author Benjamin Lamowski + * \date 2024-11-26 + */ + +/* + * Copyright (C) 2015-2024 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__VCPU_H_ +#define _CORE__VCPU_H_ + +/* base includes */ +#include +#include + +/* base-hw includes */ +#include +#include + +/* core includes */ +#include +#include + +namespace Core { struct Vcpu; } + + +class Core::Vcpu : public Rpc_object +{ + private: + struct Data_pages { + uint8_t _[Vcpu_data::size()]; + }; + + Kernel::Vm::Identity &_id; + Rpc_entrypoint &_ep; + Vcpu_data _vcpu_data { }; + Kernel_object _kobj { }; + Constrained_ram_allocator &_ram; + Ram_dataspace_capability _ds_cap { }; + Region_map &_region_map; + Affinity::Location _location; + Phys_allocated _vcpu_data_pages; + + constexpr size_t vcpu_state_size() + { + return align_addr(sizeof(Board::Vcpu_state), + get_page_size_log2()); + } + + public: + + Vcpu(Kernel::Vm::Identity &id, + Rpc_entrypoint &ep, + Constrained_ram_allocator &constrained_ram_alloc, + Region_map ®ion_map, + Affinity::Location location) + : + _id(id), + _ep(ep), + _ram(constrained_ram_alloc), + _ds_cap( {_ram.alloc(vcpu_state_size(), Cache::UNCACHED)} ), + _region_map(region_map), + _location(location), + _vcpu_data_pages(ep, constrained_ram_alloc, region_map) + { + Region_map::Attr attr { }; + attr.writeable = true; + _vcpu_data.vcpu_state = _region_map.attach(_ds_cap, attr).convert( + [&] (Region_map::Range range) { return (Vcpu_state *)range.start; }, + [&] (Region_map::Attach_error) -> Vcpu_state * { + error("failed to attach VCPU data within core"); + return nullptr; + }); + + if (!_vcpu_data.vcpu_state) { + _ram.free(_ds_cap); + + throw Attached_dataspace::Region_conflict(); + } + + _vcpu_data.virt_area = &_vcpu_data_pages.obj; + _vcpu_data.phys_addr = _vcpu_data_pages.phys_addr(); + + ep.manage(this); + } + + ~Vcpu() + { + _region_map.detach((addr_t)_vcpu_data.vcpu_state); + _ram.free(_ds_cap); + _ep.dissolve(this); + } + + /******************************* + ** Native_vcpu RPC interface ** + *******************************/ + + Capability state() const { return _ds_cap; } + Native_capability native_vcpu() { return _kobj.cap(); } + + void exception_handler(Signal_context_capability handler) + { + using Genode::warning; + if (!handler.valid()) { + warning("invalid signal"); + return; + } + + if (_kobj.constructed()) { + warning("Cannot register vcpu handler twice"); + return; + } + + unsigned const cpu = _location.xpos(); + + if (!_kobj.create(cpu, (void *)&_vcpu_data, + Capability_space::capid(handler), _id)) + warning("Cannot instantiate vm kernel object, invalid signal context?"); + } +}; + +#endif /* _CORE__VCPU_H_ */ diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/board.h b/repos/base-hw/src/core/spec/x86_64/virtualization/board.h index 889c6cd2d1..82bae483b7 100644 --- a/repos/base-hw/src/core/spec/x86_64/virtualization/board.h +++ b/repos/base-hw/src/core/spec/x86_64/virtualization/board.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -34,10 +33,6 @@ namespace Board { using Vcpu_data = Genode::Vcpu_data; using Vcpu_state = Genode::Vcpu_state; - enum { - VCPU_MAX = 16 - }; - enum Platform_exitcodes : uint64_t { EXIT_NPF = 0xfc, EXIT_INIT = 0xfd, diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/platform_services.cc b/repos/base-hw/src/core/spec/x86_64/virtualization/platform_services.cc index 11d9308b82..2cd111f391 100644 --- a/repos/base-hw/src/core/spec/x86_64/virtualization/platform_services.cc +++ b/repos/base-hw/src/core/spec/x86_64/virtualization/platform_services.cc @@ -37,7 +37,7 @@ void Core::platform_add_local_services(Rpc_entrypoint &ep, static Vm_root vm_root(ep, sliced_heap, core_ram, core_rm, trace_sources); - static Core_service vm_service(local_services, vm_root); + static Core_service> vm_service(local_services, vm_root); static Core_service io_port_ls(local_services, io_port_root); } diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/svm_session_component.h b/repos/base-hw/src/core/spec/x86_64/virtualization/svm_session_component.h new file mode 100644 index 0000000000..5e631f76bd --- /dev/null +++ b/repos/base-hw/src/core/spec/x86_64/virtualization/svm_session_component.h @@ -0,0 +1,234 @@ +/* + * \brief SVM VM session component for 'base-hw' + * \author Stefan Kalkowski + * \author Benjamin Lamowski + * \date 2024-09-20 + */ + +/* + * Copyright (C) 2015-2024 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__SVM_VM_SESSION_COMPONENT_H_ +#define _CORE__SVM_VM_SESSION_COMPONENT_H_ + +/* base includes */ +#include +#include +#include +#include +#include + +/* base-hw includes */ +#include + +/* core includes */ +#include +#include +#include +#include + +#include +#include +#include +#include + + +namespace Core { class Svm_session_component; } + + +class Core::Svm_session_component +: + public Session_object +{ + private: + + using Vm_page_table = Hw::Hpt; + + using Vm_page_table_array = + Vm_page_table::Allocator::Array; + + + /* + * Noncopyable + */ + Svm_session_component(Svm_session_component const &); + Svm_session_component &operator = (Svm_session_component const &); + + struct Detach : Region_map_detach + { + Svm_session_component &_session; + + Detach(Svm_session_component &session) : _session(session) + { } + + void detach_at(addr_t at) override + { + _session._detach_at(at); + } + + void reserve_and_flush(addr_t at) override + { + _session._reserve_and_flush(at); + } + + void unmap_region(addr_t base, size_t size) override + { + Genode::error(__func__, " unimplemented ", base, " ", size); + } + } _detach { *this }; + + Registry> _vcpus { }; + + Rpc_entrypoint &_ep; + Constrained_ram_allocator _constrained_ram_alloc; + Region_map &_region_map; + Heap _heap; + Phys_allocated _table; + Phys_allocated _table_array; + Guest_memory _memory; + Vmid_allocator &_vmid_alloc; + Kernel::Vm::Identity _id; + uint8_t _remaining_print_count { 10 }; + + void _detach_at(addr_t addr) + { + _memory.detach_at(addr, + [&](addr_t vm_addr, size_t size) { + _table.obj.remove_translation(vm_addr, size, _table_array.obj.alloc()); }); + } + + void _reserve_and_flush(addr_t addr) + { + _memory.reserve_and_flush(addr, [&](addr_t vm_addr, size_t size) { + _table.obj.remove_translation(vm_addr, size, _table_array.obj.alloc()); }); + } + + public: + + Svm_session_component(Vmid_allocator & vmid_alloc, + Rpc_entrypoint &ds_ep, + Resources resources, + Label const &label, + Diag diag, + Ram_allocator &ram_alloc, + Region_map ®ion_map, + Trace::Source_registry &) + : + Session_object(ds_ep, resources, label, diag), + _ep(ds_ep), + _constrained_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()), + _region_map(region_map), + _heap(_constrained_ram_alloc, region_map), + _table(_ep, _constrained_ram_alloc, _region_map), + _table_array(_ep, _constrained_ram_alloc, _region_map, + [] (Phys_allocated &table_array, auto *obj_ptr) { + construct_at(obj_ptr, [&] (void *virt) { + return table_array.phys_addr() + ((addr_t) obj_ptr - (addr_t)virt); + }); + }), + _memory(_constrained_ram_alloc, region_map), + _vmid_alloc(vmid_alloc), + _id({(unsigned)_vmid_alloc.alloc(), (void *)_table.phys_addr()}) + { } + + ~Svm_session_component() + { + _vcpus.for_each([&] (Registered &vcpu) { + destroy(_heap, &vcpu); }); + + _vmid_alloc.free(_id.id); + } + + + /************************** + ** Vm session interface ** + **************************/ + + void attach(Dataspace_capability cap, addr_t guest_phys, Attach_attr attr) override + { + bool out_of_tables = false; + bool invalid_mapping = false; + + auto const &map_fn = [&](addr_t vm_addr, addr_t phys_addr, size_t size) { + Page_flags const pflags { RW, EXEC, USER, NO_GLOBAL, RAM, CACHED }; + + try { + _table.obj.insert_translation(vm_addr, phys_addr, size, pflags, _table_array.obj.alloc()); + } catch(Hw::Out_of_tables &) { + if (_remaining_print_count) { + Genode::error("Translation table needs too much RAM"); + _remaining_print_count--; + } + out_of_tables = true; + } catch(...) { + if (_remaining_print_count) { + Genode::error("Invalid mapping ", Genode::Hex(phys_addr), " -> ", + Genode::Hex(vm_addr), " (", size, ")"); + } + invalid_mapping = true; + } + }; + + if (!cap.valid()) + throw Invalid_dataspace(); + + /* check dataspace validity */ + _ep.apply(cap, [&] (Dataspace_component *ptr) { + if (!ptr) + throw Invalid_dataspace(); + + Dataspace_component &dsc = *ptr; + + Guest_memory::Attach_result result = + _memory.attach(_detach, dsc, guest_phys, attr, map_fn); + + if (out_of_tables) + throw Out_of_ram(); + + if (invalid_mapping) + throw Invalid_dataspace(); + + switch (result) { + case Guest_memory::Attach_result::OK : break; + case Guest_memory::Attach_result::INVALID_DS : throw Invalid_dataspace(); break; + case Guest_memory::Attach_result::OUT_OF_RAM : throw Out_of_ram(); break; + case Guest_memory::Attach_result::OUT_OF_CAPS : throw Out_of_caps(); break; + case Guest_memory::Attach_result::REGION_CONFLICT: throw Region_conflict(); break; + } + }); + } + + void attach_pic(addr_t) override + { } + + void detach(addr_t guest_phys, size_t size) override + { + _memory.detach(guest_phys, size, [&](addr_t vm_addr, size_t size) { + _table.obj.remove_translation(vm_addr, size, _table_array.obj.alloc()); }); + } + + Capability create_vcpu(Thread_capability tcap) override + { + Affinity::Location vcpu_location; + _ep.apply(tcap, [&] (Cpu_thread_component *ptr) { + if (!ptr) return; + vcpu_location = ptr->platform_thread().affinity(); + }); + + Vcpu &vcpu = *new (_heap) + Registered(_vcpus, + _id, + _ep, + _constrained_ram_alloc, + _region_map, + vcpu_location); + + return vcpu.cap(); + } +}; + +#endif /* _CORE__SVM_VM_SESSION_COMPONENT_H_ */ diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/vm_page_table.h b/repos/base-hw/src/core/spec/x86_64/virtualization/vm_page_table.h deleted file mode 100644 index 96c60ef8f7..0000000000 --- a/repos/base-hw/src/core/spec/x86_64/virtualization/vm_page_table.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * \brief VM page table abstraction between VMX and SVM for x86 - * \author Benjamin Lamowski - * \date 2024-04-23 - */ - -/* - * Copyright (C) 2024 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__SPEC__PC__VIRTUALIZATION__VM_PAGE_TABLE_H_ -#define _CORE__SPEC__PC__VIRTUALIZATION__VM_PAGE_TABLE_H_ - -#include -#include -#include -#include - -namespace Board { - using namespace Genode; - - struct Vm_page_table - { - /* Both Ept and Hpt need to actually use this allocator */ - using Allocator = Genode::Page_table_allocator<1UL << SIZE_LOG2_4KB>; - - template - struct is_same { - static const bool value = false; - }; - - template - struct is_same { - static const bool value = true; - }; - - static_assert(is_same::value, - "Ept uses different allocator"); - static_assert(is_same::value, - "Hpt uses different allocator"); - - static constexpr size_t ALIGNM_LOG2 = Hw::SIZE_LOG2_4KB; - - enum Virt_type { - VIRT_TYPE_NONE, - VIRT_TYPE_VMX, - VIRT_TYPE_SVM - }; - - union { - Hw::Ept ept; - Hw::Hpt hpt; - }; - - void insert_translation(addr_t vo, - addr_t pa, - size_t size, - Page_flags const & flags, - Allocator & alloc) - { - if (virt_type() == VIRT_TYPE_VMX) - ept.insert_translation(vo, pa, size, flags, alloc); - else if (virt_type() == VIRT_TYPE_SVM) - hpt.insert_translation(vo, pa, size, flags, alloc); - } - - void remove_translation(addr_t vo, size_t size, Allocator & alloc) - { - if (virt_type() == VIRT_TYPE_VMX) - ept.remove_translation(vo, size, alloc); - else if (virt_type() == VIRT_TYPE_SVM) - hpt.remove_translation(vo, size, alloc); - } - - static Virt_type virt_type() { - static Virt_type virt_type { VIRT_TYPE_NONE }; - - if (virt_type == VIRT_TYPE_NONE) { - if (Hw::Virtualization_support::has_vmx()) - virt_type = VIRT_TYPE_VMX; - else if (Hw::Virtualization_support::has_svm()) - virt_type = VIRT_TYPE_SVM; - else - error("Failed to detect Virtualization technology"); - } - - return virt_type; - } - - Vm_page_table() - { - if (virt_type() == VIRT_TYPE_VMX) - Genode::construct_at(this); - else if (virt_type() == VIRT_TYPE_SVM) - Genode::construct_at(this); - } - }; - - using Vm_page_table_array = - Vm_page_table::Allocator::Array; -}; - -#endif /* _CORE__SPEC__PC__VIRTUALIZATION__VM_PAGE_TABLE_H_ */ diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/vm_session_component.cc b/repos/base-hw/src/core/spec/x86_64/virtualization/vm_session_component.cc deleted file mode 100644 index 639c7fee1f..0000000000 --- a/repos/base-hw/src/core/spec/x86_64/virtualization/vm_session_component.cc +++ /dev/null @@ -1,181 +0,0 @@ -/* - * \brief VM session component for 'base-hw' - * \author Stefan Kalkowski - * \author Benjamin Lamowski - * \date 2015-02-17 - */ - -/* - * Copyright (C) 2015-2024 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. - */ - -/* Genode includes */ -#include - -/* base internal includes */ -#include - -/* core includes */ -#include -#include -#include -#include - -using namespace Core; - - -static Core_mem_allocator & cma() { - return static_cast(platform().core_mem_alloc()); } - - -void Vm_session_component::_attach(addr_t phys_addr, addr_t vm_addr, size_t size) -{ - using namespace Hw; - - Page_flags pflags { RW, EXEC, USER, NO_GLOBAL, RAM, CACHED }; - - try { - _table.insert_translation(vm_addr, phys_addr, size, pflags, - _table_array.alloc()); - return; - } catch(Hw::Out_of_tables &) { - Genode::error("Translation table needs to much RAM"); - } catch(...) { - Genode::error("Invalid mapping ", Genode::Hex(phys_addr), " -> ", - Genode::Hex(vm_addr), " (", size, ")"); - } -} - - -void Vm_session_component::_attach_vm_memory(Dataspace_component &dsc, - addr_t const vm_addr, - Attach_attr const attribute) -{ - _attach(dsc.phys_addr() + attribute.offset, vm_addr, attribute.size); -} - - -void Vm_session_component::attach_pic(addr_t ) -{ } - - -void Vm_session_component::_detach_vm_memory(addr_t vm_addr, size_t size) -{ - _table.remove_translation(vm_addr, size, _table_array.alloc()); -} - - -void * Vm_session_component::_alloc_table() -{ - /* get some aligned space for the translation table */ - return cma().alloc_aligned(sizeof(Board::Vm_page_table), - Board::Vm_page_table::ALIGNM_LOG2).convert( - [&] (void *table_ptr) { - return table_ptr; }, - - [&] (Range_allocator::Alloc_error) -> void * { - /* XXX handle individual error conditions */ - error("failed to allocate kernel object"); - throw Insufficient_ram_quota(); } - ); -} - - -Genode::addr_t Vm_session_component::_alloc_vcpu_data(Genode::addr_t ds_addr) -{ - /* - * XXX these allocations currently leak memory on VM Session - * destruction. This cannot be easily fixed because the - * Core Mem Allocator does not implement free(). - * - * Normally we would use constrained_md_ram_alloc to make the allocation, - * but to get the physical address of the pages in virt_area, we need - * to use the Core Mem Allocator. - */ - - Vcpu_data * vcpu_data = (Vcpu_data *) cma() - .try_alloc(sizeof(Board::Vcpu_data)) - .convert( - [&](void *ptr) { return ptr; }, - [&](Range_allocator::Alloc_error) -> void * { - /* XXX handle individual error conditions */ - error("failed to allocate kernel object"); - throw Insufficient_ram_quota(); - }); - - vcpu_data->virt_area = cma() - .alloc_aligned(Vcpu_data::size(), 12) - .convert( - [&](void *ptr) { return ptr; }, - [&](Range_allocator::Alloc_error) -> void * { - /* XXX handle individual error conditions */ - error("failed to allocate kernel object"); - throw Insufficient_ram_quota(); - }); - - vcpu_data->vcpu_state = (Vcpu_state *) ds_addr; - vcpu_data->phys_addr = (addr_t)cma().phys_addr(vcpu_data->virt_area); - - return (Genode::addr_t) vcpu_data; -} - - -Vm_session_component::Vm_session_component(Vmid_allocator & vmid_alloc, - Rpc_entrypoint &ds_ep, - Resources resources, - Label const &, - Diag, - Ram_allocator &ram_alloc, - Region_map ®ion_map, - unsigned, - Trace::Source_registry &) -: - Ram_quota_guard(resources.ram_quota), - Cap_quota_guard(resources.cap_quota), - _ep(ds_ep), - _constrained_md_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()), - _sliced_heap(_constrained_md_ram_alloc, region_map), - _region_map(region_map), - _table(*construct_at(_alloc_table())), - _table_array(*(new (cma()) Board::Vm_page_table_array([] (void * virt) { - return (addr_t)cma().phys_addr(virt);}))), - _vmid_alloc(vmid_alloc), - _id({(unsigned)_vmid_alloc.alloc(), cma().phys_addr(&_table)}) -{ - /* configure managed VM area */ - _map.add_range(0UL, ~0UL); -} - - -Vm_session_component::~Vm_session_component() -{ - /* detach all regions */ - while (true) { - addr_t out_addr = 0; - - if (!_map.any_block_addr(&out_addr)) - break; - - detach_at(out_addr); - } - - /* free region in allocator */ - for (unsigned i = 0; i < _vcpu_id_alloc; i++) { - if (!_vcpus[i].constructed()) - continue; - - Vcpu & vcpu = *_vcpus[i]; - if (vcpu.ds_cap.valid()) { - _region_map.detach(vcpu.ds_addr); - _constrained_md_ram_alloc.free(vcpu.ds_cap); - } - } - - /* free guest-to-host page tables */ - destroy(platform().core_mem_alloc(), &_table); - destroy(platform().core_mem_alloc(), &_table_array); - _vmid_alloc.free(_id.id); -} diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/vmx_session_component.h b/repos/base-hw/src/core/spec/x86_64/virtualization/vmx_session_component.h new file mode 100644 index 0000000000..66538e11bd --- /dev/null +++ b/repos/base-hw/src/core/spec/x86_64/virtualization/vmx_session_component.h @@ -0,0 +1,234 @@ +/* + * \brief VMX VM session component for 'base-hw' + * \author Stefan Kalkowski + * \author Benjamin Lamowski + * \date 2024-09-20 + */ + +/* + * Copyright (C) 2015-2024 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__VMX_VM_SESSION_COMPONENT_H_ +#define _CORE__VMX_VM_SESSION_COMPONENT_H_ + +/* base includes */ +#include +#include +#include +#include +#include + +/* base-hw includes */ +#include + +/* core includes */ +#include +#include +#include +#include + +#include +#include +#include +#include + + +namespace Core { class Vmx_session_component; } + + +class Core::Vmx_session_component +: + public Session_object +{ + private: + + using Vm_page_table = Hw::Ept; + + using Vm_page_table_array = + Vm_page_table::Allocator::Array; + + + /* + * Noncopyable + */ + Vmx_session_component(Vmx_session_component const &); + Vmx_session_component &operator = (Vmx_session_component const &); + + struct Detach : Region_map_detach + { + Vmx_session_component &_session; + + Detach(Vmx_session_component &session) : _session(session) + { } + + void detach_at(addr_t at) override + { + _session._detach_at(at); + } + + void reserve_and_flush(addr_t at) override + { + _session._reserve_and_flush(at); + } + + void unmap_region(addr_t base, size_t size) override + { + Genode::error(__func__, " unimplemented ", base, " ", size); + } + } _detach { *this }; + + Registry> _vcpus { }; + + Rpc_entrypoint &_ep; + Constrained_ram_allocator _constrained_ram_alloc; + Region_map &_region_map; + Heap _heap; + Phys_allocated _table; + Phys_allocated _table_array; + Guest_memory _memory; + Vmid_allocator &_vmid_alloc; + Kernel::Vm::Identity _id; + uint8_t _remaining_print_count { 10 }; + + void _detach_at(addr_t addr) + { + _memory.detach_at(addr, + [&](addr_t vm_addr, size_t size) { + _table.obj.remove_translation(vm_addr, size, _table_array.obj.alloc()); }); + } + + void _reserve_and_flush(addr_t addr) + { + _memory.reserve_and_flush(addr, [&](addr_t vm_addr, size_t size) { + _table.obj.remove_translation(vm_addr, size, _table_array.obj.alloc()); }); + } + + public: + + Vmx_session_component(Vmid_allocator & vmid_alloc, + Rpc_entrypoint &ds_ep, + Resources resources, + Label const &label, + Diag diag, + Ram_allocator &ram_alloc, + Region_map ®ion_map, + Trace::Source_registry &) + : + Session_object(ds_ep, resources, label, diag), + _ep(ds_ep), + _constrained_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()), + _region_map(region_map), + _heap(_constrained_ram_alloc, region_map), + _table(_ep, _constrained_ram_alloc, _region_map), + _table_array(_ep, _constrained_ram_alloc, _region_map, + [] (Phys_allocated &table_array, auto *obj_ptr) { + construct_at(obj_ptr, [&] (void *virt) { + return table_array.phys_addr() + ((addr_t) obj_ptr - (addr_t)virt); + }); + }), + _memory(_constrained_ram_alloc, region_map), + _vmid_alloc(vmid_alloc), + _id({(unsigned)_vmid_alloc.alloc(), (void *)_table.phys_addr()}) + { } + + ~Vmx_session_component() + { + _vcpus.for_each([&] (Registered &vcpu) { + destroy(_heap, &vcpu); }); + + _vmid_alloc.free(_id.id); + } + + + /************************** + ** Vm session interface ** + **************************/ + + void attach(Dataspace_capability cap, addr_t guest_phys, Attach_attr attr) override + { + bool out_of_tables = false; + bool invalid_mapping = false; + + auto const &map_fn = [&](addr_t vm_addr, addr_t phys_addr, size_t size) { + Page_flags const pflags { RW, EXEC, USER, NO_GLOBAL, RAM, CACHED }; + + try { + _table.obj.insert_translation(vm_addr, phys_addr, size, pflags, _table_array.obj.alloc()); + } catch(Hw::Out_of_tables &) { + if (_remaining_print_count) { + Genode::error("Translation table needs too much RAM"); + _remaining_print_count--; + } + out_of_tables = true; + } catch(...) { + if (_remaining_print_count) { + Genode::error("Invalid mapping ", Genode::Hex(phys_addr), " -> ", + Genode::Hex(vm_addr), " (", size, ")"); + } + invalid_mapping = true; + } + }; + + if (!cap.valid()) + throw Invalid_dataspace(); + + /* check dataspace validity */ + _ep.apply(cap, [&] (Dataspace_component *ptr) { + if (!ptr) + throw Invalid_dataspace(); + + Dataspace_component &dsc = *ptr; + + Guest_memory::Attach_result result = + _memory.attach(_detach, dsc, guest_phys, attr, map_fn); + + if (out_of_tables) + throw Out_of_ram(); + + if (invalid_mapping) + throw Invalid_dataspace(); + + switch (result) { + case Guest_memory::Attach_result::OK : break; + case Guest_memory::Attach_result::INVALID_DS : throw Invalid_dataspace(); break; + case Guest_memory::Attach_result::OUT_OF_RAM : throw Out_of_ram(); break; + case Guest_memory::Attach_result::OUT_OF_CAPS : throw Out_of_caps(); break; + case Guest_memory::Attach_result::REGION_CONFLICT: throw Region_conflict(); break; + } + }); + } + + void attach_pic(addr_t) override + { } + + void detach(addr_t guest_phys, size_t size) override + { + _memory.detach(guest_phys, size, [&](addr_t vm_addr, size_t size) { + _table.obj.remove_translation(vm_addr, size, _table_array.obj.alloc()); }); + } + + Capability create_vcpu(Thread_capability tcap) override + { + Affinity::Location vcpu_location; + _ep.apply(tcap, [&] (Cpu_thread_component *ptr) { + if (!ptr) return; + vcpu_location = ptr->platform_thread().affinity(); + }); + + Vcpu &vcpu = *new (_heap) + Registered(_vcpus, + _id, + _ep, + _constrained_ram_alloc, + _region_map, + vcpu_location); + + return vcpu.cap(); + } +}; + +#endif /* _CORE__VMX_VM_SESSION_COMPONENT_H_ */ diff --git a/repos/base-hw/src/core/spec/x86_64/vm_root.h b/repos/base-hw/src/core/spec/x86_64/vm_root.h new file mode 100644 index 0000000000..ab87d40881 --- /dev/null +++ b/repos/base-hw/src/core/spec/x86_64/vm_root.h @@ -0,0 +1,99 @@ +/* + * \brief x86_64 specific Vm root interface + * \author Stefan Kalkowski + * \author Benjamin Lamowski + * \date 2012-10-08 + */ + +/* + * Copyright (C) 2012-2024 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__INCLUDE__VM_ROOT_H_ +#define _CORE__INCLUDE__VM_ROOT_H_ + +/* Genode includes */ +#include + +/* Hw includes */ + #include + +/* core includes */ +#include +#include + +#include + +namespace Core { class Vm_root; } + + +class Core::Vm_root : public Root_component> +{ + private: + + Ram_allocator &_ram_allocator; + Region_map &_local_rm; + Trace::Source_registry &_trace_sources; + Vmid_allocator _vmid_alloc { }; + + protected: + + Session_object *_create_session(const char *args) override + { + Session::Resources resources = session_resources_from_args(args); + + if (Hw::Virtualization_support::has_svm()) + return new (md_alloc()) + Svm_session_component(_vmid_alloc, + *ep(), + resources, + session_label_from_args(args), + session_diag_from_args(args), + _ram_allocator, _local_rm, + _trace_sources); + + if (Hw::Virtualization_support::has_vmx()) + return new (md_alloc()) + Vmx_session_component(_vmid_alloc, + *ep(), + session_resources_from_args(args), + session_label_from_args(args), + session_diag_from_args(args), + _ram_allocator, _local_rm, + _trace_sources); + + Genode::error( "No virtualization support detected."); + throw Core::Service_denied(); + } + + void _upgrade_session(Session_object *vm, const char *args) override + { + vm->upgrade(ram_quota_from_args(args)); + vm->upgrade(cap_quota_from_args(args)); + } + + public: + + /** + * Constructor + * + * \param session_ep entrypoint managing vm_session components + * \param md_alloc meta-data allocator to be used by root component + */ + Vm_root(Rpc_entrypoint &session_ep, + Allocator &md_alloc, + Ram_allocator &ram_alloc, + Region_map &local_rm, + Trace::Source_registry &trace_sources) + : + Root_component>(&session_ep, &md_alloc), + _ram_allocator(ram_alloc), + _local_rm(local_rm), + _trace_sources(trace_sources) + { } +}; + +#endif /* _CORE__INCLUDE__VM_ROOT_H_ */ diff --git a/repos/base-hw/src/core/vm_root.h b/repos/base-hw/src/core/vm_root.h index 3e6b72dba9..ecf7ee509f 100644 --- a/repos/base-hw/src/core/vm_root.h +++ b/repos/base-hw/src/core/vm_root.h @@ -17,7 +17,6 @@ /* Genode includes */ #include -#include /* core includes */ #include diff --git a/repos/base/include/vm_session/connection.h b/repos/base/include/vm_session/connection.h index cf9d5210e3..34bb4e47ef 100644 --- a/repos/base/include/vm_session/connection.h +++ b/repos/base/include/vm_session/connection.h @@ -99,7 +99,7 @@ struct Genode::Vm_connection : Connection, Rpc_client long priority = Cpu_session::DEFAULT_PRIORITY, unsigned long affinity = 0) : - Connection(env, label, Ram_quota { 16*1024 }, Affinity(), + Connection(env, label, Ram_quota { 5*1024*1024 }, Affinity(), Args("priority=", Hex(priority), ", " "affinity=", Hex(affinity))), Rpc_client(cap())