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())