diff --git a/repos/os/run/vmm_arm.run b/repos/os/run/vmm_arm.run
index 06a8ada3ae..d80052352a 100644
--- a/repos/os/run/vmm_arm.run
+++ b/repos/os/run/vmm_arm.run
@@ -26,6 +26,35 @@ import_from_depot [depot_user]/src/[base_src] \
build { test/terminal_expect_send }
+proc vmm_config { } {
+
+ # for early printk add bootarg: earlycon=pl011,0x9000000
+
+ if {[have_spec arm]} {
+ return {
+
+
+
+
+ }
+ }
+
+ if {[have_spec arm_64]} {
+ return {
+
+
+
+
+ }
+ }
+
+ return unavailable
+}
+
install_config {
@@ -92,7 +121,7 @@ install_config {
-
+ } [vmm_config] {
@@ -112,12 +141,7 @@ if { [have_spec arm] } {
if {![file exists bin/linux]} {
puts "Download linux kernel ..."
- exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-20.05/linux-arm32
- }
-
- if {![file exists bin/dtb]} {
- puts "Download device tree blob ..."
- exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-21.02/dtb-arm32-virt
+ exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-22.11/linux-virtio-arm32
}
if {![file exists bin/initrd]} {
@@ -126,22 +150,15 @@ if { [have_spec arm] } {
}
#
-# To obtain the linux kernel, do the following steps:
-#
-# wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.3.10.tar.xz
-#
-# tar -xJf linux-5.3.10.tar.xz
-# cd linux-5.3.10
-#
-# make O=../build-linux-aarch32 ARCH=arm CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-arm- defconfig
-# make O=../build-linux-aarch32 ARCH=arm CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-arm- -j32
-#
-# copy ../build-linux-aarch32/arch/arm/boot/zImage to your build directory in 'bin/linux'
+# To build the linux kernel from scratch, do the following steps:
#
+# wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.14.21.tar.xz
+# tar -xJf linux-5.14.21.tar.xz
+# cd linux-5.14.21
+# wget -O .config https://raw.githubusercontent.com/skalk/linux/66fb717e92b47512d8f686860a2c5e7329d01691/arch/arm/configs/virtio_only_defconfig
+# make ARCH=arm CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-arm-
#
-# To get the dtb (device-tree-binary), you have to compile the file:
-# repos/os/src/server/vmm/spec/arm_v7/virt.dts with the dtc compiler:
-# dtc repos/os/src/server/vmm/spec/arm_v7/virt.dts > bin/dtb
+# copy arch/arm/boot/Image to your build directory in 'bin/linux'
#
#
# To construct the initrd do the following:
@@ -169,12 +186,7 @@ if { [have_spec arm_64] } {
if {![file exists bin/linux]} {
puts "Download linux kernel ..."
- exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-20.02/linux-arm64
- }
-
- if {![file exists bin/dtb]} {
- puts "Download device tree blob ..."
- exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-21.02/dtb-arm64-virt-smp
+ exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-22.11/linux-virtio-arm64
}
if {![file exists bin/initrd]} {
@@ -183,22 +195,15 @@ if { [have_spec arm_64] } {
}
#
-# To obtain the linux kernel, do the following steps:
-#
-# wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.53.tar.xz
-#
-# tar -xJf linux-4.19.53.tar.xz
-# cd linux-4.19.53
-#
-# make O=../build-linux-aarch64 ARCH=arm64 CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-aarch64- defconfig
-# make O=../build-linux-aarch64 ARCH=arm64 CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-aarch64- -j32
-#
-# copy ../build-linux-aarch64/arch/arm64/boot/Image to your build directory in 'bin/linux'
+# To build the linux kernel from scratch, do the following steps:
#
+# wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.14.21.tar.xz
+# tar -xJf linux-5.14.21.tar.xz
+# cd linux-5.14.21
+# wget -O .config https://raw.githubusercontent.com/skalk/linux/66fb717e92b47512d8f686860a2c5e7329d01691/arch/arm64/configs/virtio_only_defconfig
+# make ARCH=arm64 CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-aarch64-
#
-# To get the dtb (device-tree-binary), you have to compile the file:
-# repos/os/src/server/vmm/spec/arm_v8/virt.dts with the dtc compiler:
-# dtc repos/os/src/server/vmm/spec/arm_v8/virt.dts > bin/dtb
+# copy arch/arm64/boot/Image to your build directory in 'bin/linux'
#
#
# To construct the initrd do the following:
@@ -227,7 +232,6 @@ exec [installed_command mkfs.vfat] bin/block.img
build_boot_image {
test-terminal_expect_send
linux
- dtb
initrd
block.img
}
@@ -238,4 +242,4 @@ build_boot_image {
append qemu_args " -nographic "
run_genode_until "\[init -> vm\] .*resolv.conf.*" 220
-exec rm bin/linux bin/dtb bin/initrd bin/block.img
+exec rm bin/linux bin/initrd bin/block.img
diff --git a/repos/os/src/server/vmm/README b/repos/os/src/server/vmm/README
new file mode 100644
index 0000000000..32a65a7a77
--- /dev/null
+++ b/repos/os/src/server/vmm/README
@@ -0,0 +1,84 @@
+The vmm component implements a virtual-machine monitor that is able to
+drive Linux VMs on ARMv7 and ARMv8 using hardware-assisted virtualization.
+It uses the VM session for ARM, currently provided by the base-hw kernel only.
+It is limited to load Linux kernel binaries and an initram-filesystem only.
+
+The VMM produces a flattened device-tree blob (DTB) to the Linux guest OS
+that is derived from the configuration of the VMM.
+
+The following configuration attributes are evaluated:
+
+!
+
+Configuration attributes explained in more detail:
+
+:kernel_rom:
+ This attribute is optional. It denotes the ROM, which is requested as kernel
+ binary to be loaded. The default name requested will be "linux". Please note
+ that the Linux kernel image shouldn't be gzipped, because the VMM does not
+ deflate the image before loading.
+
+:initrd_rom:
+ This attribute is optional. It denotes the ROM, which is requested as initramfs
+ resp. initrd to be loaded. The default name requested will be "initrd".
+
+:ram_size:
+ This attribute is mandatory. It defines the size of the VM's memory.
+
+:cpu_count:
+ This attribute is mandatory. It defines the available number of virtual CPUs
+ for the VM. The virtual CPUs are getting assigned round-robin to the physical
+ CPUs available to the VMM.
+
+:cpu_type:
+ This attribute's default value is "arm,cortex-a15". If your underlying hardware
+ has a different one, you should use here the Linux-specific CPU compatible string
+ for the actual CPU of your board.
+
+:gic_version:
+ This attribute's default value is "2". If your underlying hardware uses a
+ different ARM Generic Interrupt Controller version, you should specify the
+ actual version here. The only versions allowed are 2 and 3. Other interrupt
+ controller models are not supported.
+
+:bootargs:
+ This attribute is mandatory. It defines the Linux kernel's cmdline. The default
+ value is "console=ttyAMA0"
+
+
+Virtio devices
+--------------
+
+In addition to the general configuration attributes, one can define several Virtio
+devices per VM. This is done by configuration sub-nodes, like:
+
+!
+!
+!
+! ...
+!
+
+For each virtio_device node the following attributes need to be set:
+
+:name:
+ A unique name denoting the device.
+
+:type:
+ The Virtio type of device. One can decide in between "console", "net",
+ and "block". The "console" type gets mapped to a Genode Terminal session,
+ "net" is mapped to a Nic session, and "block" to a Block session.
+
+
+Additional devices
+------------------
+
+Apart from defined Virtio devices, as well as the defined CPUs and GIC version,
+the VMM always assigns a PL011 UART device to the VM, which gets connected to
+a Terminal session as backend. This Terminal session uses "earlycon" as
+last label.
diff --git a/repos/os/src/server/vmm/board_base.h b/repos/os/src/server/vmm/board_base.h
new file mode 100644
index 0000000000..e4164d9687
--- /dev/null
+++ b/repos/os/src/server/vmm/board_base.h
@@ -0,0 +1,43 @@
+/*
+ * \brief VMM - board definitions
+ * \author Stefan Kalkowski
+ * \date 2019-11-13
+ */
+
+/*
+ * Copyright (C) 2019 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 _SRC__SERVER__VMM__BOARD_H_
+#define _SRC__SERVER__VMM__BOARD_H_
+
+namespace Vmm {
+
+ enum {
+ GICD_MMIO_START = 0x8000000,
+ GICD_MMIO_SIZE = 0x10000,
+ GICC_MMIO_START = 0x8010000,
+ GICC_MMIO_SIZE = 0x10000,
+ GICR_MMIO_START = 0x80a0000,
+ GICR_MMIO_SIZE = 0xf60000,
+
+ PL011_MMIO_START = 0x9000000,
+ PL011_MMIO_SIZE = 0x1000,
+ PL011_IRQ = 33,
+
+ VIRTIO_MMIO_START = 0xa000000,
+ VIRTIO_MMIO_SIZE = 0x1000000,
+ VIRTIO_IRQ_START = 40,
+ VIRTIO_IRQ_COUNT = 128,
+
+ RAM_START = 0x40000000,
+ MINIMUM_RAM_SIZE = 32 * 1024 * 1024,
+
+ VTIMER_IRQ = 27,
+ };
+}
+
+#endif /* _SRC__SERVER__VMM__BOARD_H_ */
diff --git a/repos/os/src/server/vmm/config.cc b/repos/os/src/server/vmm/config.cc
new file mode 100644
index 0000000000..4c9fd4dca4
--- /dev/null
+++ b/repos/os/src/server/vmm/config.cc
@@ -0,0 +1,66 @@
+/*
+ * \brief VMM for ARM virtualization - config frontend
+ * \author Stefan Kalkowski
+ * \date 2022-11-10
+ */
+
+/*
+ * Copyright (C) 2022 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
+
+using namespace Genode;
+using namespace Vmm;
+
+
+Config::Virtio_device::Virtio_device(Name & name, Type type, Config & config)
+:
+ _config(config),
+ name(name),
+ type(type),
+ mmio_start(config._mmio_alloc.alloc(MMIO_SIZE)),
+ mmio_size(MMIO_SIZE),
+ irq(config._irq_alloc.alloc()) {}
+
+
+Config::Virtio_device::~Virtio_device()
+{
+ _config._irq_alloc.free(irq);
+ _config._mmio_alloc.free(mmio_start);
+}
+
+
+void Vmm::Config::update(Xml_node node)
+{
+ _kernel_name = node.attribute_value("kernel_rom", Name("linux"));
+ _initrd_name = node.attribute_value("initrd_rom", Name("initrd"));
+ _ram_size = node.attribute_value("ram_size", Number_of_bytes());
+ _cpu_count = node.attribute_value("cpu_count", 0U);
+ _cpu_type = node.attribute_value("cpu_type", Name("arm,cortex-a15"));
+ _gic_version = node.attribute_value("gic_version", 2U);
+ _bootargs = node.attribute_value("bootargs", Arguments("console=ttyAMA0"));
+
+ if (_gic_version < 2 || _gic_version > 3) {
+ error("Invalid GIC version, supported are: 2 and 3");
+ throw Invalid_configuration();
+ }
+
+ if (_ram_size < MINIMUM_RAM_SIZE) {
+ error("Minimum RAM size is ", Hex((size_t)MINIMUM_RAM_SIZE));
+ warning("Reset RAM size to minimum");
+ _ram_size = MINIMUM_RAM_SIZE;
+ }
+
+ if (_cpu_count < 1) {
+ error("Minimum CPU count is 1");
+ warning("Reset CPU count to minimum");
+ _cpu_count = 1;
+ }
+
+ Virtio_device_update_policy policy(*this);
+ _model.update_from_xml(policy, node);
+}
diff --git a/repos/os/src/server/vmm/config.h b/repos/os/src/server/vmm/config.h
new file mode 100644
index 0000000000..cb188d6299
--- /dev/null
+++ b/repos/os/src/server/vmm/config.h
@@ -0,0 +1,147 @@
+/*
+ * \brief VMM for ARM virtualization - config frontend
+ * \author Stefan Kalkowski
+ * \date 2022-11-10
+ */
+
+/*
+ * Copyright (C) 2022 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 _SRC__SERVER__VMM__CONFIG_H_
+#define _SRC__SERVER__VMM__CONFIG_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Vmm {
+ class Config;
+ using namespace Genode;
+}
+
+class Vmm::Config
+{
+ public:
+
+ struct Invalid_configuration{};
+
+ using Name = String<128>;
+ using Arguments = String<512>;
+
+ struct Virtio_device : List_model::Element
+ {
+ enum Type { INVALID, CONSOLE, NET, BLOCK };
+
+ enum { MMIO_SIZE = 0x200 };
+
+ Config & _config;
+
+ Virtio_device(Name & name, Type type, Config & config);
+ ~Virtio_device();
+
+ Name const name;
+ Type const type;
+ void * const mmio_start;
+ size_t const mmio_size;
+ unsigned const irq;
+ };
+
+ private:
+
+ struct Irq_allocator
+ {
+ Bit_allocator _alloc {};
+
+ unsigned alloc() {
+ return VIRTIO_IRQ_START + _alloc.alloc(); }
+ void free(unsigned irq) {
+ _alloc.free(VIRTIO_IRQ_START+irq); }
+ };
+
+ Heap & _heap;
+ Allocator_avl _mmio_alloc { &_heap };
+ Irq_allocator _irq_alloc { };
+ Name _kernel_name { };
+ Name _initrd_name { };
+ size_t _ram_size { 0 };
+ unsigned _cpu_count { 0 };
+ Name _cpu_type { };
+ unsigned _gic_version { 0 };
+ Arguments _bootargs { };
+
+ List_model _model;
+
+ struct Virtio_device_update_policy
+ : List_model::Update_policy
+ {
+ Config & config;
+
+ Virtio_device_update_policy(Config & config)
+ : config(config) {}
+
+ static Virtio_device::Type type(Xml_node node)
+ {
+ Virtio_device::Type t = Virtio_device::INVALID;
+ Config::Name type = node.attribute_value("type", Config::Name());
+ if (type == "console") t = Virtio_device::CONSOLE;
+ if (type == "net") t = Virtio_device::NET;
+ if (type == "block") t = Virtio_device::BLOCK;
+ return t;
+ }
+
+ void destroy_element(Element & dev) { destroy(config._heap, &dev); }
+
+ Element & create_element(Genode::Xml_node node)
+ {
+ Config::Name name = node.attribute_value("name", Config::Name());
+ Virtio_device::Type t = type(node);
+ if (t == Virtio_device::INVALID || !name.valid()) {
+ error("Invalid type or missing name in Virtio device node");
+ throw Invalid_configuration();
+ }
+ return *(new (config._heap) Element(name, t, config));
+ }
+
+ void update_element(Element &, Genode::Xml_node) {}
+
+ static bool element_matches_xml_node(Element const & dev, Xml_node node)
+ {
+ Config::Name name = node.attribute_value("name", Config::Name());
+ Virtio_device::Type t = type(node);
+ return name == dev.name && t == dev.type;
+ }
+
+ static bool node_is_element(Xml_node node) {
+ return node.has_type("virtio_device"); }
+ };
+
+ public:
+
+ Config(Heap & heap) : _heap(heap) {
+ _mmio_alloc.add_range(VIRTIO_MMIO_START, VIRTIO_MMIO_SIZE); }
+
+ const char * kernel_name() const { return _kernel_name.string(); }
+ const char * initrd_name() const { return _initrd_name.string(); }
+ const char * cpu_type() const { return _cpu_type.string(); }
+ const char * bootargs() const { return _bootargs.string(); }
+
+ size_t ram_size() const { return _ram_size; }
+ unsigned cpu_count() const { return _cpu_count; }
+ unsigned gic_version() const { return _gic_version; }
+
+ template
+ void for_each_virtio_device(FN const & fn) const {
+ _model.for_each(fn); }
+
+ void update(Xml_node);
+};
+
+#endif /* _SRC__SERVER__VMM__CONFIG_H_ */
diff --git a/repos/os/src/server/vmm/fdt.cc b/repos/os/src/server/vmm/fdt.cc
new file mode 100644
index 0000000000..294c3ba3a0
--- /dev/null
+++ b/repos/os/src/server/vmm/fdt.cc
@@ -0,0 +1,397 @@
+/*
+ * \brief VMM utilities to generate a flattened device tree blob
+ * \author Stefan Kalkowski
+ * \date 2022-11-04
+ */
+
+/*
+ * Copyright (C) 2022 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
+
+using namespace Genode;
+
+class Mmio_big_endian_access
+{
+ friend Register_set_plain_access;
+
+ private:
+
+ addr_t const _base;
+
+ /**
+ * Write '_ACCESS_T' typed 'value' to MMIO base + 'offset'
+ */
+ template
+ inline void _write(off_t const offset, ACCESS_T const value)
+ {
+ addr_t const dst = _base + offset;
+ *(ACCESS_T volatile *)dst = host_to_big_endian(value);
+ }
+
+ /**
+ * Read '_ACCESS_T' typed from MMIO base + 'offset'
+ */
+ template
+ inline ACCESS_T _read(off_t const &offset) const
+ {
+ addr_t const dst = _base + offset;
+ ACCESS_T const value = *(ACCESS_T volatile *)dst;
+ return host_to_big_endian(value);
+ }
+
+ public:
+
+ Mmio_big_endian_access(addr_t const base) : _base(base) { }
+
+ addr_t base() const { return _base; }
+};
+
+
+struct Mmio : Mmio_big_endian_access,
+ Register_set
+{
+ Mmio(addr_t const base)
+ :
+ Mmio_big_endian_access(base),
+ Register_set(*static_cast(this)) { }
+};
+
+
+struct Fdt_header : Mmio
+{
+ struct Magic : Register<0x0, 32> {};
+ struct Totalsize : Register<0x4, 32> {};
+ struct Off_dt_struct : Register<0x8, 32> {};
+ struct Off_dt_strings : Register<0xc, 32> {};
+ struct Off_mem_rsvmap : Register<0x10, 32> {};
+ struct Version : Register<0x14, 32> {};
+ struct Last_comp_version : Register<0x18, 32> {};
+ struct Boot_cpuid_phys : Register<0x1c, 32> {};
+ struct Size_dt_strings : Register<0x20, 32> {};
+ struct Size_dt_struct : Register<0x24, 32> {};
+
+ using Mmio::Mmio;
+
+ enum { SIZE = 10*4 };
+};
+
+
+struct Fdt_reserve_entry : Mmio
+{
+ struct Address : Register<0, 64> {};
+ struct Size : Register<8, 64> {};
+
+ using Mmio::Mmio;
+
+ enum { SIZE = 2*8 };
+};
+
+
+enum Fdt_tokens {
+ FDT_BEGIN_NODE = 0x00000001U,
+ FDT_END_NODE = 0x00000002U,
+ FDT_PROP = 0x00000003U,
+ FDT_NOP = 0x00000004U,
+ FDT_END = 0x00000009U,
+};
+
+
+struct Fdt_token : Mmio
+{
+ struct Type : Register<0, 32> {};
+
+ Fdt_token(addr_t const base, Fdt_tokens type)
+ :
+ Mmio(base)
+ {
+ write(type);
+ }
+
+ enum { SIZE = 4 };
+};
+
+
+struct Fdt_prop : Fdt_token
+{
+ struct Len : Register<4, 32> {};
+ struct Nameoff : Register<8, 32> {};
+
+ Fdt_prop(addr_t base, uint32_t len, uint32_t name_offset)
+ :
+ Fdt_token(base, FDT_PROP)
+ {
+ write(len);
+ write(name_offset);
+ }
+
+ enum { SIZE = Fdt_token::SIZE + 2*4 };
+};
+
+
+enum Interrupt_specifier {
+ GIC_SPI = 0,
+ GIC_PPI = 1,
+};
+
+
+enum Interrupt_type {
+ IRQ_TYPE_NONE = 0,
+ IRQ_TYPE_EDGE_RISING = 1,
+ IRQ_TYPE_EDGE_FALLING = 2,
+ IRQ_TYPE_EDGE_BOTH = 3,
+ IRQ_TYPE_LEVEL_HIGH = 4,
+ IRQ_TYPE_LEVEL_LOW = 8,
+};
+
+
+enum {
+ FDT_MAGIC = 0xd00dfeed,
+ FDT_VERSION = 17,
+ FDT_COMP_VERSION = 16,
+};
+
+
+enum Phandles {
+ GIC = 1,
+ CLK = 2
+};
+
+struct Value
+{
+ uint32_t value;
+
+ Value(uint32_t const & v) : value(v) {};
+ Value() : value(0) {};
+
+ Value &operator= (uint32_t const & v)
+ {
+ value = v;
+ return *this;
+ }
+
+ size_t length() const { return sizeof(value); }
+
+ template
+ void write(uint32_t off, T & buf) const
+ {
+ uint32_t v = host_to_big_endian(value);
+ buf.write(off, &v, length());
+ }
+};
+
+
+template
+struct Array : Genode::Array
+{
+ using Genode::Array::Array;
+
+ size_t length() const
+ {
+ size_t ret = 0;
+ this->for_each([&] (unsigned, T const & v) { ret += v.length(); });
+ return ret;
+ }
+
+ template
+ void write(uint32_t off, BUF & buf) const
+ {
+ this->for_each([&] (unsigned, T const & v) {
+ v.write(off, buf);
+ off += (uint32_t)v.length();
+ });
+ }
+};
+
+
+void Vmm::Fdt_generator::_generate_tree(uint32_t & off, Config const & config,
+ void * initrd_start, size_t initrd_size)
+{
+ using Name = Fdt_dictionary::Name;
+
+ auto node = [&] (auto const & name, auto const & fn)
+ {
+ Fdt_token start(_buffer.addr+off, FDT_BEGIN_NODE);
+ off += Fdt_token::SIZE;
+ _buffer.write(off, name.string(), name.length());
+ off += (uint32_t)name.length();
+ off = align_addr(off, 2);
+ fn();
+ Fdt_token end(_buffer.addr+off, FDT_END_NODE);
+ off += Fdt_token::SIZE;
+ };
+
+ auto property = [&] (auto const & name, auto const & val)
+ {
+ _dict.add(name);
+ Fdt_prop prop(_buffer.addr+off, (uint32_t)val.length(),
+ _dict.offset(name));
+ off += Fdt_prop::SIZE;
+ val.write(off, _buffer);
+ off = align_addr(off+(uint32_t)val.length(), 2);
+ };
+
+ node(Name(""), [&] ()
+ {
+ property(Name("compatible"), Name("linux,dummy-virt"));
+ property(Name("#address-cells"), Value(2));
+ property(Name("#size-cells"), Value(2));
+ property(Name("interrupt-parent"), Value(GIC));
+
+ node(Name("cpus"), [&] ()
+ {
+ property(Name("#address-cells"), Value(1));
+ property(Name("#size-cells"), Value(0));
+
+ for (unsigned i = 0; i < config.cpu_count(); i++) {
+ node(Name("cpu@", i), [&] ()
+ {
+ property(Name("compatible"), Name(config.cpu_type()));
+ property(Name("reg"), Value(i));
+ property(Name("device_type"), Name("cpu"));
+ property(Name("enable-method"), Name("psci"));
+ });
+ }
+ });
+
+ node(Name("psci"), [&] ()
+ {
+ property(Name("compatible"), Name("arm,psci-1.0"));
+ property(Name("method"), Name("hvc"));
+ property(Name("cpu_suspend"), Value{Psci::CPU_SUSPEND});
+ property(Name("cpu_off"), Value{Psci::CPU_OFF});
+ property(Name("cpu_on"), Value{Psci::CPU_ON});
+ });
+
+ node(Name("timer"), [&] ()
+ {
+ property(Name("compatible"),
+ ::Array("arm,armv8-timer", "arm,armv7-timer"));
+ property(Name("interrupts"),
+ ::Array(GIC_PPI, 0xd, IRQ_TYPE_LEVEL_HIGH,
+ GIC_PPI, 0xe, IRQ_TYPE_LEVEL_HIGH,
+ GIC_PPI, 0xb, IRQ_TYPE_LEVEL_HIGH,
+ GIC_PPI, 0xa, IRQ_TYPE_LEVEL_HIGH));
+ });
+
+ node(Name("gic"), [&] ()
+ {
+ bool gicv2 = config.gic_version() < 3U;
+ property(Name("phandle"), Value(GIC));
+ property(Name("compatible"),
+ (gicv2) ? Name("arm,gic-400") : Name("arm,gic-v3"));
+ property(Name("ranges"), ::Array());
+ property(Name("interrupt-controller"), ::Array());
+ property(Name("#address-cells"), Value(2));
+ property(Name("#redistributor-regions"), Value(1));
+ property(Name("#interrupt-cells"), Value(3));
+ property(Name("#size-cells"), Value(2));
+ property(Name("reg"),
+ ::Array(0, GICD_MMIO_START, 0, GICD_MMIO_SIZE,
+ 0, (gicv2) ? GICC_MMIO_START : GICR_MMIO_START,
+ 0, (gicv2) ? GICC_MMIO_SIZE : GICR_MMIO_SIZE));
+ });
+
+ node(Name("clocks"), [&] ()
+ {
+ property(Name("#address-cells"), Value(1));
+ property(Name("#size-cells"), Value(0));
+
+ node(Name("clk@0"), [&] ()
+ {
+ property(Name("compatible"), Name("fixed-clock"));
+ property(Name("clock-output-names"), Name("clk24mhz"));
+ property(Name("clock-frequency"), Value(24000000));
+ property(Name("#clock-cells"), Value(0));
+ property(Name("reg"), Value(0));
+ property(Name("phandle"), Value(CLK));
+ });
+ });
+
+ node(Name("pl011"), [&] ()
+ {
+ property(Name("compatible"),
+ ::Array("arm,pl011", "arm,primecell"));
+ property(Name("interrupts"),
+ ::Array(GIC_SPI, PL011_IRQ-32, IRQ_TYPE_LEVEL_HIGH));
+ property(Name("reg"), ::Array(0, PL011_MMIO_START,
+ 0, PL011_MMIO_SIZE));
+ property(Name("clock-names"),
+ ::Array("uartclk", "apb_pclk"));
+ property(Name("clocks"), ::Array(CLK, CLK));
+ });
+
+ node(Name("memory"), [&] ()
+ {
+ property(Name("reg"), ::Array(0, RAM_START, 0, config.ram_size()));
+ property(Name("device_type"), Name("memory"));
+ });
+
+ node(Name("chosen"), [&] ()
+ {
+ /* we're sure that the initrd start address is wide below 4GB */
+ uint32_t start = (uint32_t)((addr_t)initrd_start & 0xffffffff);
+ property(Name("linux,initrd-start"), Value(start));
+ property(Name("linux,initrd-end"), Value(start+initrd_size));
+ property(Name("bootargs"), Name(config.bootargs()));
+ property(Name("stdout-path"), Name("/pl011"));
+ });
+
+ config.for_each_virtio_device([&] (Config::Virtio_device const & dev) {
+ node(Name("virtio@", dev.mmio_start), [&] ()
+ {
+ property(Name("interrupts"),
+ ::Array(GIC_SPI, dev.irq-32, IRQ_TYPE_EDGE_RISING));
+ property(Name("compatible"), Name("virtio,mmio"));
+ property(Name("dma-coherent"), ::Array());
+ property(Name("reg"),
+ ::Array(0, (uint32_t)((addr_t)dev.mmio_start & 0xffffffff),
+ 0, (uint32_t)dev.mmio_size));
+ });
+ });
+ });
+
+ Fdt_token end(_buffer.addr+off, FDT_END);
+ off += Fdt_token::SIZE;
+}
+
+
+void Vmm::Fdt_generator::generate(Config const & config,
+ void * initrd_start, size_t initrd_size)
+{
+ Fdt_header header(_buffer.addr);
+ header.write(FDT_MAGIC);
+ header.write(FDT_VERSION);
+ header.write(FDT_COMP_VERSION);
+ header.write(0);
+
+ uint32_t off = Fdt_header::SIZE;
+ header.write(off);
+ Fdt_reserve_entry memory(_buffer.addr+off);
+ memory.write(0);
+ memory.write(0);
+
+ off += Fdt_reserve_entry::SIZE;
+ header.write(off);
+
+ _generate_tree(off, config, initrd_start, initrd_size);
+
+ header.write(off-Fdt_header::SIZE-Fdt_reserve_entry::SIZE);
+
+ header.write(off);
+ header.write(_dict.length());
+ _dict.write([&] (addr_t o, const char * src, size_t len) {
+ _buffer.write(off+o, src, len); });
+
+ off += _dict.length();
+ header.write(off);
+}
diff --git a/repos/os/src/server/vmm/fdt.h b/repos/os/src/server/vmm/fdt.h
new file mode 100644
index 0000000000..979c772975
--- /dev/null
+++ b/repos/os/src/server/vmm/fdt.h
@@ -0,0 +1,149 @@
+/*
+ * \brief VMM utilities to generate a flattened device tree blob
+ * \author Stefan Kalkowski
+ * \date 2022-11-04
+ */
+
+/*
+ * Copyright (C) 2022 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 _SRC__SERVER__VMM__FDT_H_
+#define _SRC__SERVER__VMM__FDT_H_
+
+#include
+#include
+#include
+#include
+
+namespace Vmm {
+ class Fdt_generator;
+
+ using namespace Genode;
+}
+
+
+class Vmm::Fdt_generator
+{
+ private:
+
+ class Fdt_dictionary
+ {
+ public:
+
+ struct Name : Genode::String<64>
+ {
+ using String<64>::String;
+
+ template
+ void write(uint32_t off, T & buf) const {
+ buf.write(off, string(), length()); }
+ };
+
+ private:
+
+ struct String : Dictionary::Element
+ {
+ uint32_t const offset;
+
+ String(Dictionary & dict,
+ Name const & name,
+ uint32_t offset)
+ :
+ Dictionary::Element(dict, name),
+ offset(offset) {}
+ };
+
+ Heap & _heap;
+ uint32_t _offset { 0U };
+ Dictionary _dict {};
+
+ public:
+
+ struct Not_found {};
+
+ Fdt_dictionary(Heap & heap) : _heap(heap) { }
+
+ ~Fdt_dictionary()
+ {
+ for (;;)
+ if (!_dict.with_any_element([&] (String & str) {
+ destroy(_heap, &str); }))
+ break;
+ }
+
+ void add(Name const & name)
+ {
+ _dict.with_element(name, [&] (String const &) {}, [&] ()
+ {
+ new (_heap) String(_dict, name, _offset);
+ _offset += (uint32_t)name.length();
+ });
+ }
+
+ uint32_t offset(Name const & name) const
+ {
+ return _dict.with_element(name, [&] (String const & str)
+ {
+ return str.offset;
+ }, [&] ()
+ {
+ throw Not_found();
+ return 0;
+ });
+ }
+
+ template
+ void write(WRITE_FN const & write_fn) const
+ {
+ _dict.for_each([&] (String const & str)
+ {
+ write_fn(str.offset, str.name.string(), str.name.length());
+ });
+ }
+
+ uint32_t length() const { return _offset; }
+ };
+
+
+ struct Buffer
+ {
+ struct Buffer_exceeded {};
+
+ addr_t const addr;
+ size_t const size;
+ size_t used { 0 };
+
+ Buffer(addr_t addr, size_t size) : addr(addr), size(size) {}
+
+ void write(addr_t offset, void const * data, size_t length)
+ {
+ if ((offset + length) > size)
+ throw Buffer_exceeded();
+
+ memcpy((void*)(addr + offset), data, length);
+ if ((offset + length) > used) used = offset + length;
+ }
+ };
+
+ Env & _env;
+ Heap & _heap;
+ Buffer _buffer;
+ Fdt_dictionary _dict { _heap };
+
+ void _generate_tree(uint32_t & off, Config const & config,
+ void * initrd_start, size_t initrd_size);
+
+ public:
+
+ Fdt_generator(Env & env, Heap & heap, addr_t dtb_addr, size_t max_size)
+ : _env(env), _heap(heap), _buffer(dtb_addr, max_size) { }
+
+ void generate(Config const & config, void * initrd_start,
+ size_t initrd_size);
+};
+
+#endif /* _SRC__SERVER__VMM__FDT_H_ */
diff --git a/repos/os/src/server/vmm/gic.cc b/repos/os/src/server/vmm/gic.cc
index 25e67e5128..e3a9a465b0 100644
--- a/repos/os/src/server/vmm/gic.cc
+++ b/repos/os/src/server/vmm/gic.cc
@@ -207,7 +207,7 @@ Gic::Gicd_banked::Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus)
_rdist.construct(GICR_MMIO_START +
(cpu.cpu_id()*0x20000), 0x20000,
cpu.cpu_id(),
- Vm::last_cpu() == cpu.cpu_id());
+ (_gic._cpu_cnt-1) == cpu.cpu_id());
bus.add(*_rdist);
}
}
@@ -251,26 +251,27 @@ void Gic::Gicd_sgir::write(Address_range &, Cpu & cpu,
unsigned target_list = Target_list::get(value);
unsigned irq = Int_id::get(value);
- for (unsigned i = 0; i <= Vm::last_cpu(); i++) {
+ cpu.vm().for_each_cpu([&] (Cpu & c) {
switch (type) {
case Target_filter::MYSELF:
- if (i != cpu.cpu_id()) { continue; }
+ if (c.cpu_id() != cpu.cpu_id())
+ return;
break;
case Target_filter::ALL:
- if (i == cpu.cpu_id()) { continue; }
+ if (c.cpu_id() == cpu.cpu_id())
+ return;
break;
case Target_filter::LIST:
- if (!(target_list & (1<
#include
+#include
#include
+#include
+#include
+#include
-void Component::construct(Genode::Env & env) { static Vmm::Vm vm(env); }
+using namespace Genode;
+
+
+struct Main
+{
+ Env & env;
+ Heap heap { env.ram(), env.rm() };
+ Attached_rom_dataspace config_rom { env, "config" };
+ Signal_handler handler { env.ep(), *this, &Main::update };
+ Vmm::Config config { heap };
+ Constructible vm {};
+
+ void update()
+ {
+ config_rom.update();
+ config.update(config_rom.xml());
+ vm.construct(env, heap, config);
+ }
+
+ Main(Env & env) : env(env)
+ {
+ config_rom.sigh(handler);
+ update();
+ }
+
+};
+
+
+void Component::construct(Env & env) { static Main m(env); }
diff --git a/repos/os/src/server/vmm/psci.h b/repos/os/src/server/vmm/psci.h
index 04e93ac779..147d6e8471 100644
--- a/repos/os/src/server/vmm/psci.h
+++ b/repos/os/src/server/vmm/psci.h
@@ -22,6 +22,8 @@ namespace Vmm {
MIGRATE_INFO_TYPE = 0x84000006,
PSCI_FEATURES = 0x8400000a,
CPU_ON_32 = 0x84000003,
+ CPU_SUSPEND = 0xc4000001,
+ CPU_OFF = 0xc4000002,
CPU_ON = 0xc4000003,
};
diff --git a/repos/os/src/server/vmm/spec/arm_v7/board.h b/repos/os/src/server/vmm/spec/arm_v7/board.h
index 32d533d72d..bfb98e59a0 100644
--- a/repos/os/src/server/vmm/spec/arm_v7/board.h
+++ b/repos/os/src/server/vmm/spec/arm_v7/board.h
@@ -1,5 +1,5 @@
/*
- * \brief VMM address space utility
+ * \brief VMM - board definitions for ARM 32-bit
* \author Stefan Kalkowski
* \date 2019-11-13
*/
@@ -11,47 +11,14 @@
* under the terms of the GNU Affero General Public License version 3.
*/
-#ifndef _SRC__SERVER__VMM__BOARD_H_
-#define _SRC__SERVER__VMM__BOARD_H_
+#ifndef _SRC__SERVER__VMM__SPEC__ARM_V7__BOARD_H_
+#define _SRC__SERVER__VMM__SPEC__ARM_V7__BOARD_H_
+
+#include
namespace Vmm {
-
- enum {
- SIZE_1_MB = 1024 * 1024,
- KERNEL_OFFSET = 54 * SIZE_1_MB,
- DTB_OFFSET = 64 * SIZE_1_MB,
- INITRD_OFFSET = 96 * SIZE_1_MB,
-
- GIC_VERSION = 2,
- GICD_MMIO_START = 0x8000000,
- GICD_MMIO_SIZE = 0x10000,
- GICC_MMIO_START = 0x8010000,
- GICR_MMIO_START = 0x80a0000,
- GICR_MMIO_SIZE = 0xf60000,
-
- PL011_MMIO_START = 0x9000000,
- PL011_MMIO_SIZE = 0x1000,
- PL011_IRQ = 33,
-
- VIRTIO_CONSOLE_MMIO_START = 0xa000000,
- VIRTIO_CONSOLE_MMIO_SIZE = 0x200,
- VIRTIO_CONSOLE_IRQ = 48,
-
- VIRTIO_NET_MMIO_START = 0xa000200,
- VIRTIO_NET_MMIO_SIZE = 0x200,
- VIRTIO_NET_IRQ = 49,
-
- VIRTIO_BLK_MMIO_START = 0xa000400,
- VIRTIO_BLK_MMIO_SIZE = 0x200,
- VIRTIO_BLK_IRQ = 50,
-
- RAM_START = 0x40000000,
- RAM_SIZE = 128 * 1024 *1024,
-
- VTIMER_IRQ = 27,
-
- MAX_CPUS = 1,
- };
+ enum { KERNEL_OFFSET = 0x8000, };
}
-#endif /* _SRC__SERVER__VMM__BOARD_H_ */
+#endif /* _SRC__SERVER__VMM__SPEC__ARM_V7__BOARD_H_ */
+
diff --git a/repos/os/src/server/vmm/spec/arm_v7/target.mk b/repos/os/src/server/vmm/spec/arm_v7/target.mk
index 175ad7c3bb..bcfd68502a 100644
--- a/repos/os/src/server/vmm/spec/arm_v7/target.mk
+++ b/repos/os/src/server/vmm/spec/arm_v7/target.mk
@@ -1,18 +1,5 @@
-TARGET = vmm
-REQUIRES = hw arm_v7
-LIBS = base
+include $(PRG_DIR)/../../target.inc
+
+REQUIRES += arm_v7
SRC_CC += spec/arm_v7/generic_timer.cc
-SRC_CC += address_space.cc
-SRC_CC += cpu.cc
-SRC_CC += cpu_base.cc
-SRC_CC += generic_timer.cc
-SRC_CC += gic.cc
-SRC_CC += main.cc
-SRC_CC += mmio.cc
-SRC_CC += pl011.cc
-SRC_CC += vm.cc
-INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)
-
-vpath %.cc $(PRG_DIR)/../..
-
-CC_CXX_WARN_STRICT :=
+SRC_CC += spec/arm_v7/cpu.cc
diff --git a/repos/os/src/server/vmm/spec/arm_v8/board.h b/repos/os/src/server/vmm/spec/arm_v8/board.h
index 9f17addb85..466e7fc3c8 100644
--- a/repos/os/src/server/vmm/spec/arm_v8/board.h
+++ b/repos/os/src/server/vmm/spec/arm_v8/board.h
@@ -1,5 +1,5 @@
/*
- * \brief VMM address space utility
+ * \brief VMM - board definitions for ARM 64-bit
* \author Stefan Kalkowski
* \date 2019-11-13
*/
@@ -11,47 +11,13 @@
* under the terms of the GNU Affero General Public License version 3.
*/
-#ifndef _SRC__SERVER__VMM__BOARD_H_
-#define _SRC__SERVER__VMM__BOARD_H_
+#ifndef _SRC__SERVER__VMM__SPEC__ARM_V8__BOARD_H_
+#define _SRC__SERVER__VMM__SPEC__ARM_V8__BOARD_H_
+
+#include
namespace Vmm {
-
- enum {
- SIZE_1_MB = 1024 * 1024,
- KERNEL_OFFSET = 0x80000,
- INITRD_OFFSET = 32 * SIZE_1_MB,
- DTB_OFFSET = 64 * SIZE_1_MB,
-
- GIC_VERSION = 3,
- GICD_MMIO_START = 0x8000000,
- GICD_MMIO_SIZE = 0x10000,
- GICC_MMIO_START = 0x8010000,
- GICR_MMIO_START = 0x80a0000,
- GICR_MMIO_SIZE = 0xf60000,
-
- PL011_MMIO_START = 0x9000000,
- PL011_MMIO_SIZE = 0x1000,
- PL011_IRQ = 33,
-
- VIRTIO_CONSOLE_MMIO_START = 0xa000000,
- VIRTIO_CONSOLE_MMIO_SIZE = 0x200,
- VIRTIO_CONSOLE_IRQ = 48,
-
- VIRTIO_NET_MMIO_START = 0xa000200,
- VIRTIO_NET_MMIO_SIZE = 0x200,
- VIRTIO_NET_IRQ = 49,
-
- VIRTIO_BLK_MMIO_START = 0xa000400,
- VIRTIO_BLK_MMIO_SIZE = 0x200,
- VIRTIO_BLK_IRQ = 50,
-
- RAM_START = 0x40000000,
- RAM_SIZE = 128 * 1024 *1024,
-
- VTIMER_IRQ = 27,
-
- MAX_CPUS = 4,
- };
+ enum { KERNEL_OFFSET = 0x80000, };
}
-#endif /* _SRC__SERVER__VMM__BOARD_H_ */
+#endif /* _SRC__SERVER__VMM__SPEC__ARM_V8__BOARD_H_ */
diff --git a/repos/os/src/server/vmm/spec/arm_v8/cpu.cc b/repos/os/src/server/vmm/spec/arm_v8/cpu.cc
index 20c992eda6..96d202de40 100644
--- a/repos/os/src/server/vmm/spec/arm_v8/cpu.cc
+++ b/repos/os/src/server/vmm/spec/arm_v8/cpu.cc
@@ -188,14 +188,12 @@ void Cpu::Icc_sgi1r_el1::write(Genode::addr_t v)
unsigned target_list = v & 0xffff;
unsigned irq = (v >> 24) & 0xf;
- for (unsigned i = 0; i <= Vm::last_cpu(); i++) {
- if (target_list & (1< class Virtio_device;
using namespace Genode;
@@ -219,8 +220,16 @@ class Vmm::Virtio_split_queue
};
+class Vmm::Virtio_device_base : public List::Element
+{
+ public:
+
+ virtual ~Virtio_device_base() {}
+};
+
+
template
-class Vmm::Virtio_device : public Vmm::Mmio_device
+class Vmm::Virtio_device : public Vmm::Mmio_device, public Virtio_device_base
{
protected:
diff --git a/repos/os/src/server/vmm/vm.cc b/repos/os/src/server/vmm/vm.cc
index d01ddd9d15..7d40779c33 100644
--- a/repos/os/src/server/vmm/vm.cc
+++ b/repos/os/src/server/vmm/vm.cc
@@ -11,53 +11,87 @@
* under the terms of the GNU Affero General Public License version 3.
*/
+#include
#include
using Vmm::Vm;
-void Vm::_load_kernel()
+
+enum { LOG2_2MB = 21 };
+
+Genode::Entrypoint & Vm::Cpu_entry::ep(unsigned i, Vm & vm)
{
- Genode::memcpy((void*)(_ram.local() + KERNEL_OFFSET),
- _kernel_rom.local_addr(),
- _kernel_rom.size());
+ if (i == 0)
+ return vm._env.ep();
+
+ _ep.construct(vm._env, STACK_SIZE, "vcpu ep",
+ vm._env.cpu().affinity_space().location_of_index(i));
+ return *_ep;
}
-void Vm::_load_dtb()
+
+Vm::Cpu_entry::Cpu_entry(unsigned i, Vm & vm)
+:
+ cpu(vm, vm._vm, vm._bus, vm._gic, vm._env, vm._heap, ep(i, vm), i) { }
+
+
+Genode::addr_t Vm::_initrd_offset() const
{
- Genode::memcpy((void*)(_ram.local() + DTB_OFFSET),
- _dtb_rom.local_addr(),
- _dtb_rom.size());
+ return align_addr(KERNEL_OFFSET+_kernel_rom.size(), LOG2_2MB);
+}
+
+
+Genode::addr_t Vm::_dtb_offset() const
+{
+ return align_addr(_initrd_offset()+_initrd_rom.size(), LOG2_2MB);
+}
+
+
+void Vm::_load_kernel()
+{
+ memcpy((void*)(_ram.local() + KERNEL_OFFSET),
+ _kernel_rom.local_addr(), _kernel_rom.size());
}
void Vm::_load_initrd()
{
- Genode::memcpy((void*)(_ram.local() + INITRD_OFFSET),
- _initrd_rom.local_addr(),
- _initrd_rom.size());
+ memcpy((void*)(_ram.local() + _initrd_offset()),
+ _initrd_rom.local_addr(), _initrd_rom.size());
+}
+
+
+void Vm::_load_dtb()
+{
+ Fdt_generator fdt(_env, _heap, _ram.local() + _dtb_offset(), 1 << LOG2_2MB);
+ fdt.generate(_config, (void*)(_ram.base()+_initrd_offset()), _initrd_rom.size());
}
Vmm::Cpu & Vm::boot_cpu()
{
- if (!_cpus[0].constructed())
- _cpus[0].construct(*this, _vm, _bus, _gic, _env, _heap, _env.ep(), 0);
- return *_cpus[0];
+ if (!_cpu_list.first()) {
+ Cpu_entry * last = nullptr;
+ for (unsigned i = 0; i < _config.cpu_count(); i++) {
+ Cpu_entry * e = new (_heap) Cpu_entry(i, *this);
+ _cpu_list.insert(e, last);
+ last = e;
+ }
+ }
+
+ return _cpu_list.first()->cpu;
}
-Vm::Vm(Genode::Env & env)
-: _env(env),
- _gic("Gicv3", GICD_MMIO_START, GICD_MMIO_SIZE,
- MAX_CPUS, GIC_VERSION, _vm, _bus, env),
- _uart("Pl011", PL011_MMIO_START, PL011_MMIO_SIZE,
- PL011_IRQ, boot_cpu(), _bus, env),
- _virtio_console("HVC", VIRTIO_CONSOLE_MMIO_START, VIRTIO_CONSOLE_MMIO_SIZE,
- VIRTIO_CONSOLE_IRQ, boot_cpu(), _bus, _ram, env),
- _virtio_net("Net", VIRTIO_NET_MMIO_START, VIRTIO_NET_MMIO_SIZE,
- VIRTIO_NET_IRQ, boot_cpu(), _bus, _ram, env),
- _virtio_block("Block", VIRTIO_BLK_MMIO_START, VIRTIO_BLK_MMIO_SIZE,
- VIRTIO_BLK_IRQ, boot_cpu(), _bus, _ram, env, _heap)
+Vm::Vm(Genode::Env & env, Heap & heap, Config & config)
+:
+ _env(env),
+ _heap(heap),
+ _config(config),
+ _gic("Gic", GICD_MMIO_START, GICD_MMIO_SIZE,
+ config.cpu_count(), config.gic_version(), _vm, _bus, env),
+ _uart("Pl011", PL011_MMIO_START, PL011_MMIO_SIZE,
+ PL011_IRQ, boot_cpu(), _bus, env)
{
_vm.attach(_vm_ram.cap(), RAM_START,
Genode::Vm_session::Attach_attr { .offset = 0,
@@ -65,21 +99,45 @@ Vm::Vm(Genode::Env & env)
.executable = true,
.writeable = true });
- _load_kernel();
- _load_dtb();
- _load_initrd();
+ _config.for_each_virtio_device([&] (Config::Virtio_device const & dev) {
+ switch (dev.type) {
+ case Config::Virtio_device::CONSOLE:
+ _device_list.insert(new (_heap)
+ Virtio_console(dev.name.string(), (uint64_t)dev.mmio_start,
+ dev.mmio_size, dev.irq, boot_cpu(),
+ _bus, _ram, env));
+ return;
+ case Config::Virtio_device::NET:
+ _device_list.insert(new (_heap)
+ Virtio_net(dev.name.string(), (uint64_t)dev.mmio_start,
+ dev.mmio_size, dev.irq, boot_cpu(), _bus, _ram,
+ env));
+ return;
+ case Config::Virtio_device::BLOCK:
+ _device_list.insert(new (_heap)
+ Virtio_block_device(dev.name.string(), (uint64_t)dev.mmio_start,
+ dev.mmio_size, dev.irq, boot_cpu(),
+ _bus, _ram, env, heap));
+ default:
+ return;
+ };
+ });
- for (unsigned i = 1; i < MAX_CPUS; i++) {
- Genode::Affinity::Space space = _env.cpu().affinity_space();
- Genode::Affinity::Location location(space.location_of_index(i));
- _eps[i].construct(_env, STACK_SIZE, "vcpu ep", location);
- _cpus[i].construct(*this, _vm, _bus, _gic, _env, _heap, *_eps[i], i);
- }
+ _load_kernel();
+ _load_initrd();
+ _load_dtb();
Genode::log("Start virtual machine ...");
Cpu & cpu = boot_cpu();
cpu.initialize_boot(_ram.base() + KERNEL_OFFSET,
- _ram.base() + DTB_OFFSET);
+ _ram.base() + _dtb_offset());
cpu.run();
};
+
+
+Vm::~Vm()
+{
+ while (_cpu_list.first()) destroy(_heap, _cpu_list.first());
+ while (_device_list.first()) destroy(_heap, _device_list.first());
+}
diff --git a/repos/os/src/server/vmm/vm.h b/repos/os/src/server/vmm/vm.h
index 6e11b97a56..715debda75 100644
--- a/repos/os/src/server/vmm/vm.h
+++ b/repos/os/src/server/vmm/vm.h
@@ -15,6 +15,7 @@
#define _SRC__SERVER__VMM__VM_H_
#include
+#include
#include
#include
#include
@@ -28,42 +29,54 @@
#include
#include
-namespace Vmm { class Vm; }
+namespace Vmm {
+ class Vm;
+ using namespace Genode;
+}
class Vmm::Vm
{
private:
- using Ep = Genode::Entrypoint;
+ struct Cpu_entry : List::Element
+ {
+ enum { STACK_SIZE = sizeof(unsigned long) * 2048, };
- enum { STACK_SIZE = sizeof(unsigned long) * 2048, };
+ Constructible _ep {};
- Genode::Env & _env;
- Genode::Vm_connection _vm { _env };
- Genode::Attached_rom_dataspace _kernel_rom { _env, "linux" };
- Genode::Attached_rom_dataspace _dtb_rom { _env, "dtb" };
- Genode::Attached_rom_dataspace _initrd_rom { _env, "initrd" };
- Genode::Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(),
- RAM_SIZE, Genode::CACHED };
- Ram _ram { RAM_START, RAM_SIZE,
- (Genode::addr_t)_vm_ram.local_addr()};
- Genode::Heap _heap { _env.ram(), _env.rm() };
- Mmio_bus _bus;
- Gic _gic;
- Genode::Constructible _eps[MAX_CPUS];
- Genode::Constructible _cpus[MAX_CPUS];
- Pl011 _uart;
- Virtio_console _virtio_console;
- Virtio_net _virtio_net;
- Virtio_block_device _virtio_block;
+ Entrypoint & ep(unsigned i, Vm & vm);
+ Cpu cpu;
+
+ Cpu_entry(unsigned idx, Vm & vm);
+ };
+
+ Env & _env;
+ Heap & _heap;
+ Config & _config;
+ Vm_connection _vm { _env };
+ Attached_rom_dataspace _kernel_rom { _env, _config.kernel_name() };
+ Attached_rom_dataspace _initrd_rom { _env, _config.initrd_name() };
+ Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(),
+ _config.ram_size(), CACHED };
+ Ram _ram { RAM_START, _config.ram_size(),
+ (addr_t)_vm_ram.local_addr()};
+ Mmio_bus _bus;
+ Gic _gic;
+ List _cpu_list;
+ List _device_list;
+ Pl011 _uart;
+
+ addr_t _initrd_offset() const;
+ addr_t _dtb_offset() const;
void _load_kernel();
- void _load_dtb();
void _load_initrd();
+ void _load_dtb();
public:
- Vm(Genode::Env & env);
+ Vm(Env & env, Heap & heap, Config & config);
+ ~Vm();
Mmio_bus & bus() { return _bus; }
Cpu & boot_cpu();
@@ -71,11 +84,16 @@ class Vmm::Vm
template
void cpu(unsigned cpu, F func)
{
- if (cpu >= MAX_CPUS) Genode::error("Cpu number out of bounds ");
- else func(*_cpus[cpu]);
+ for (Cpu_entry * ce = _cpu_list.first(); ce; ce = ce->next())
+ if (ce->cpu.cpu_id() == cpu) func(ce->cpu);
}
- static unsigned last_cpu() { return MAX_CPUS - 1; }
+ template
+ void for_each_cpu(F func)
+ {
+ for (Cpu_entry * ce = _cpu_list.first(); ce; ce = ce->next())
+ func(ce->cpu);
+ }
};
#endif /* _SRC__SERVER__VMM__VM_H_ */