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_ */