From b78b543011ba2a120998e6bf71a133a5c5946cac Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Tue, 15 Nov 2022 13:34:37 +0100 Subject: [PATCH] vmm: make ARM VMM configureable This commit enables users of the VMM to define CPU type and count, RAM size, kernel and initrd ROM names, GIC version, and Virtio devices to be used. Derived from the configuration values a flattened device-tree blob (DTB) is generated and transfered to the VM. Fix genodelabs/genode#4670 --- repos/os/run/vmm_arm.run | 90 ++-- repos/os/src/server/vmm/README | 84 ++++ repos/os/src/server/vmm/board_base.h | 43 ++ repos/os/src/server/vmm/config.cc | 66 +++ repos/os/src/server/vmm/config.h | 147 +++++++ repos/os/src/server/vmm/fdt.cc | 397 ++++++++++++++++++ repos/os/src/server/vmm/fdt.h | 149 +++++++ repos/os/src/server/vmm/gic.cc | 23 +- repos/os/src/server/vmm/main.cc | 35 +- repos/os/src/server/vmm/psci.h | 2 + repos/os/src/server/vmm/spec/arm_v7/board.h | 49 +-- repos/os/src/server/vmm/spec/arm_v7/target.mk | 21 +- repos/os/src/server/vmm/spec/arm_v8/board.h | 48 +-- repos/os/src/server/vmm/spec/arm_v8/cpu.cc | 12 +- repos/os/src/server/vmm/spec/arm_v8/target.mk | 21 +- repos/os/src/server/vmm/target.inc | 18 + repos/os/src/server/vmm/virtio_device.h | 11 +- repos/os/src/server/vmm/vm.cc | 130 ++++-- repos/os/src/server/vmm/vm.h | 70 +-- 19 files changed, 1175 insertions(+), 241 deletions(-) create mode 100644 repos/os/src/server/vmm/README create mode 100644 repos/os/src/server/vmm/board_base.h create mode 100644 repos/os/src/server/vmm/config.cc create mode 100644 repos/os/src/server/vmm/config.h create mode 100644 repos/os/src/server/vmm/fdt.cc create mode 100644 repos/os/src/server/vmm/fdt.h create mode 100644 repos/os/src/server/vmm/target.inc 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_ */