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
This commit is contained in:
Stefan Kalkowski 2022-11-15 13:34:37 +01:00 committed by Christian Helmuth
parent 0e9a49d1cf
commit b78b543011
19 changed files with 1175 additions and 241 deletions

View File

@ -26,6 +26,35 @@ import_from_depot [depot_user]/src/[base_src] \
build { test/terminal_expect_send } build { test/terminal_expect_send }
proc vmm_config { } {
# for early printk add bootarg: earlycon=pl011,0x9000000
if {[have_spec arm]} {
return {
<config kernel_rom="linux" initrd_rom="initrd" ram_size="128M"
cpu_count="1" cpu_type="arm,cortex-a15" gic_version="2"
bootargs="rdinit=/bin/sh ip=dhcp console=hvc0">
<virtio_device name="hvc0" type="console"/>
<virtio_device name="eth0" type="net"/>
<virtio_device name="blk0" type="block"/>
</config>}
}
if {[have_spec arm_64]} {
return {
<config kernel_rom="linux" initrd_rom="initrd" ram_size="128M"
cpu_count="4" cpu_type="arm,cortex-a53" gic_version="3"
bootargs="rdinit=/bin/sh ip=dhcp console=hvc0">
<virtio_device name="hvc0" type="console"/>
<virtio_device name="eth0" type="net"/>
<virtio_device name="blk0" type="block"/>
</config>}
}
return unavailable
}
install_config { install_config {
<config verbose="yes" prio_levels="2"> <config verbose="yes" prio_levels="2">
<parent-provides> <parent-provides>
@ -92,7 +121,7 @@ install_config {
<service name="Terminal"> <child name="terminal_crosslink"/> </service> <service name="Terminal"> <child name="terminal_crosslink"/> </service>
<service name="Nic"> <child name="nic_drv"/> </service> <service name="Nic"> <child name="nic_drv"/> </service>
<any-service><parent/><any-child/></any-service> <any-service><parent/><any-child/></any-service>
</route> </route>} [vmm_config] {
</start> </start>
<start name="vm"> <start name="vm">
@ -112,12 +141,7 @@ if { [have_spec arm] } {
if {![file exists bin/linux]} { if {![file exists bin/linux]} {
puts "Download linux kernel ..." puts "Download linux kernel ..."
exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-20.05/linux-arm32 exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-22.11/linux-virtio-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
} }
if {![file exists bin/initrd]} { if {![file exists bin/initrd]} {
@ -126,22 +150,15 @@ if { [have_spec arm] } {
} }
# #
# To obtain the linux kernel, do the following steps: # To build the linux kernel from scratch, 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'
# #
# 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: # copy arch/arm/boot/Image to your build directory in 'bin/linux'
# 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
# #
# #
# To construct the initrd do the following: # To construct the initrd do the following:
@ -169,12 +186,7 @@ if { [have_spec arm_64] } {
if {![file exists bin/linux]} { if {![file exists bin/linux]} {
puts "Download linux kernel ..." puts "Download linux kernel ..."
exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-20.02/linux-arm64 exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-22.11/linux-virtio-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
} }
if {![file exists bin/initrd]} { if {![file exists bin/initrd]} {
@ -183,22 +195,15 @@ if { [have_spec arm_64] } {
} }
# #
# To obtain the linux kernel, do the following steps: # To build the linux kernel from scratch, 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'
# #
# 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: # copy arch/arm64/boot/Image to your build directory in 'bin/linux'
# 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
# #
# #
# To construct the initrd do the following: # To construct the initrd do the following:
@ -227,7 +232,6 @@ exec [installed_command mkfs.vfat] bin/block.img
build_boot_image { build_boot_image {
test-terminal_expect_send test-terminal_expect_send
linux linux
dtb
initrd initrd
block.img block.img
} }
@ -238,4 +242,4 @@ build_boot_image {
append qemu_args " -nographic " append qemu_args " -nographic "
run_genode_until "\[init -> vm\] .*resolv.conf.*" 220 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

View File

@ -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:
! <config kernel_rom="linux"
! initrd_rom="initrd"
! ram_size="512M"
! cpu_count="1"
! cpu_type="arm,cortex-a53"
! gic_version="3"
! bootargs="console=hvc0"/>
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:
! <config ram_size="128M" cpu_count="1" ... >
!
! <virtio_device name="hvc0" type="console"/>
! ...
! </config>
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.

View File

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

View File

@ -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 <config.h>
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);
}

View File

@ -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 <board.h>
#include <base/allocator_avl.h>
#include <base/heap.h>
#include <util/bit_allocator.h>
#include <util/list_model.h>
#include <util/string.h>
#include <util/xml_node.h>
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<Virtio_device>::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<VIRTIO_IRQ_COUNT> _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<Virtio_device> _model;
struct Virtio_device_update_policy
: List_model<Config::Virtio_device>::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 <typename FN>
void for_each_virtio_device(FN const & fn) const {
_model.for_each(fn); }
void update(Xml_node);
};
#endif /* _SRC__SERVER__VMM__CONFIG_H_ */

View File

@ -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 <board.h>
#include <fdt.h>
#include <psci.h>
#include <util/array.h>
#include <util/endian.h>
#include <util/register_set.h>
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 <typename ACCESS_T>
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 <typename ACCESS_T>
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_big_endian_access>
{
Mmio(addr_t const base)
:
Mmio_big_endian_access(base),
Register_set(*static_cast<Mmio_big_endian_access *>(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>(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<Fdt_prop::Len>(len);
write<Fdt_prop::Nameoff>(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 <typename T>
void write(uint32_t off, T & buf) const
{
uint32_t v = host_to_big_endian(value);
buf.write(off, &v, length());
}
};
template <typename T, unsigned MAX>
struct Array : Genode::Array<T, MAX>
{
using Genode::Array<T,MAX>::Array;
size_t length() const
{
size_t ret = 0;
this->for_each([&] (unsigned, T const & v) { ret += v.length(); });
return ret;
}
template <typename BUF>
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<Name, 2>("arm,armv8-timer", "arm,armv7-timer"));
property(Name("interrupts"),
::Array<Value, 12>(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<Value,0>());
property(Name("interrupt-controller"), ::Array<Value,0>());
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<Value, 8>(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<Name, 2>("arm,pl011", "arm,primecell"));
property(Name("interrupts"),
::Array<Value, 3>(GIC_SPI, PL011_IRQ-32, IRQ_TYPE_LEVEL_HIGH));
property(Name("reg"), ::Array<Value, 4>(0, PL011_MMIO_START,
0, PL011_MMIO_SIZE));
property(Name("clock-names"),
::Array<Name, 2>("uartclk", "apb_pclk"));
property(Name("clocks"), ::Array<Value, 2>(CLK, CLK));
});
node(Name("memory"), [&] ()
{
property(Name("reg"), ::Array<Value, 4>(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<Value, 3>(GIC_SPI, dev.irq-32, IRQ_TYPE_EDGE_RISING));
property(Name("compatible"), Name("virtio,mmio"));
property(Name("dma-coherent"), ::Array<Value,0>());
property(Name("reg"),
::Array<Value, 4>(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_header::Magic>(FDT_MAGIC);
header.write<Fdt_header::Version>(FDT_VERSION);
header.write<Fdt_header::Last_comp_version>(FDT_COMP_VERSION);
header.write<Fdt_header::Boot_cpuid_phys>(0);
uint32_t off = Fdt_header::SIZE;
header.write<Fdt_header::Off_mem_rsvmap>(off);
Fdt_reserve_entry memory(_buffer.addr+off);
memory.write<Fdt_reserve_entry::Address>(0);
memory.write<Fdt_reserve_entry::Size>(0);
off += Fdt_reserve_entry::SIZE;
header.write<Fdt_header::Off_dt_struct>(off);
_generate_tree(off, config, initrd_start, initrd_size);
header.write<Fdt_header::Size_dt_struct>(off-Fdt_header::SIZE-Fdt_reserve_entry::SIZE);
header.write<Fdt_header::Off_dt_strings>(off);
header.write<Fdt_header::Size_dt_strings>(_dict.length());
_dict.write([&] (addr_t o, const char * src, size_t len) {
_buffer.write(off+o, src, len); });
off += _dict.length();
header.write<Fdt_header::Totalsize>(off);
}

View File

@ -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 <config.h>
#include <base/env.h>
#include <base/heap.h>
#include <util/dictionary.h>
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 <typename T>
void write(uint32_t off, T & buf) const {
buf.write(off, string(), length()); }
};
private:
struct String : Dictionary<String, Name>::Element
{
uint32_t const offset;
String(Dictionary<String, Name> & dict,
Name const & name,
uint32_t offset)
:
Dictionary<String, Name>::Element(dict, name),
offset(offset) {}
};
Heap & _heap;
uint32_t _offset { 0U };
Dictionary<String, Name> _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 <typename WRITE_FN>
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_ */

View File

@ -207,7 +207,7 @@ Gic::Gicd_banked::Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus)
_rdist.construct(GICR_MMIO_START + _rdist.construct(GICR_MMIO_START +
(cpu.cpu_id()*0x20000), 0x20000, (cpu.cpu_id()*0x20000), 0x20000,
cpu.cpu_id(), cpu.cpu_id(),
Vm::last_cpu() == cpu.cpu_id()); (_gic._cpu_cnt-1) == cpu.cpu_id());
bus.add(*_rdist); bus.add(*_rdist);
} }
} }
@ -251,26 +251,27 @@ void Gic::Gicd_sgir::write(Address_range &, Cpu & cpu,
unsigned target_list = Target_list::get(value); unsigned target_list = Target_list::get(value);
unsigned irq = Int_id::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) { switch (type) {
case Target_filter::MYSELF: case Target_filter::MYSELF:
if (i != cpu.cpu_id()) { continue; } if (c.cpu_id() != cpu.cpu_id())
return;
break; break;
case Target_filter::ALL: case Target_filter::ALL:
if (i == cpu.cpu_id()) { continue; } if (c.cpu_id() == cpu.cpu_id())
return;
break; break;
case Target_filter::LIST: case Target_filter::LIST:
if (!(target_list & (1<<i))) { continue; } if (!(target_list & (1<<c.cpu_id())))
return;
break; break;
default: default:
continue; return;
}; };
cpu.vm().cpu(i, [&] (Cpu & c) { c.gic().irq(irq).assert();
c.gic().irq(irq).assert(); if (cpu.cpu_id() != c.cpu_id()) { cpu.recall(); }
if (cpu.cpu_id() != c.cpu_id()) { cpu.recall(); } });
});
}
} }

View File

@ -11,7 +11,40 @@
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
*/ */
#include <config.h>
#include <vm.h> #include <vm.h>
#include <base/attached_rom_dataspace.h>
#include <base/component.h> #include <base/component.h>
#include <base/env.h>
#include <base/heap.h>
#include <util/reconstructible.h>
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<Main> handler { env.ep(), *this, &Main::update };
Vmm::Config config { heap };
Constructible<Vmm::Vm> 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); }

View File

@ -22,6 +22,8 @@ namespace Vmm {
MIGRATE_INFO_TYPE = 0x84000006, MIGRATE_INFO_TYPE = 0x84000006,
PSCI_FEATURES = 0x8400000a, PSCI_FEATURES = 0x8400000a,
CPU_ON_32 = 0x84000003, CPU_ON_32 = 0x84000003,
CPU_SUSPEND = 0xc4000001,
CPU_OFF = 0xc4000002,
CPU_ON = 0xc4000003, CPU_ON = 0xc4000003,
}; };

View File

@ -1,5 +1,5 @@
/* /*
* \brief VMM address space utility * \brief VMM - board definitions for ARM 32-bit
* \author Stefan Kalkowski * \author Stefan Kalkowski
* \date 2019-11-13 * \date 2019-11-13
*/ */
@ -11,47 +11,14 @@
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
*/ */
#ifndef _SRC__SERVER__VMM__BOARD_H_ #ifndef _SRC__SERVER__VMM__SPEC__ARM_V7__BOARD_H_
#define _SRC__SERVER__VMM__BOARD_H_ #define _SRC__SERVER__VMM__SPEC__ARM_V7__BOARD_H_
#include <board_base.h>
namespace Vmm { namespace Vmm {
enum { KERNEL_OFFSET = 0x8000, };
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,
};
} }
#endif /* _SRC__SERVER__VMM__BOARD_H_ */ #endif /* _SRC__SERVER__VMM__SPEC__ARM_V7__BOARD_H_ */

View File

@ -1,18 +1,5 @@
TARGET = vmm include $(PRG_DIR)/../../target.inc
REQUIRES = hw arm_v7
LIBS = base REQUIRES += arm_v7
SRC_CC += spec/arm_v7/generic_timer.cc SRC_CC += spec/arm_v7/generic_timer.cc
SRC_CC += address_space.cc SRC_CC += spec/arm_v7/cpu.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 :=

View File

@ -1,5 +1,5 @@
/* /*
* \brief VMM address space utility * \brief VMM - board definitions for ARM 64-bit
* \author Stefan Kalkowski * \author Stefan Kalkowski
* \date 2019-11-13 * \date 2019-11-13
*/ */
@ -11,47 +11,13 @@
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
*/ */
#ifndef _SRC__SERVER__VMM__BOARD_H_ #ifndef _SRC__SERVER__VMM__SPEC__ARM_V8__BOARD_H_
#define _SRC__SERVER__VMM__BOARD_H_ #define _SRC__SERVER__VMM__SPEC__ARM_V8__BOARD_H_
#include <board_base.h>
namespace Vmm { namespace Vmm {
enum { KERNEL_OFFSET = 0x80000, };
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,
};
} }
#endif /* _SRC__SERVER__VMM__BOARD_H_ */ #endif /* _SRC__SERVER__VMM__SPEC__ARM_V8__BOARD_H_ */

View File

@ -188,14 +188,12 @@ void Cpu::Icc_sgi1r_el1::write(Genode::addr_t v)
unsigned target_list = v & 0xffff; unsigned target_list = v & 0xffff;
unsigned irq = (v >> 24) & 0xf; unsigned irq = (v >> 24) & 0xf;
for (unsigned i = 0; i <= Vm::last_cpu(); i++) { vm.for_each_cpu([&] (Cpu & cpu) {
if (target_list & (1<<i)) { if (target_list & (1<<cpu.cpu_id())) {
vm.cpu(i, [&] (Cpu & cpu) { cpu.gic().irq(irq).assert();
cpu.gic().irq(irq).assert(); cpu.recall();
cpu.recall();
});
} }
} });
}; };

View File

@ -1,18 +1,5 @@
TARGET = vmm include $(PRG_DIR)/../../target.inc
REQUIRES = hw arm_v8
LIBS = base REQUIRES += arm_v8
SRC_CC += spec/arm_v8/generic_timer.cc SRC_CC += spec/arm_v8/generic_timer.cc
SRC_CC += address_space.cc SRC_CC += spec/arm_v8/cpu.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 :=

View File

@ -0,0 +1,18 @@
TARGET = vmm
REQUIRES = hw
LIBS = base
SRC_CC += address_space.cc
SRC_CC += cpu_base.cc
SRC_CC += config.cc
SRC_CC += fdt.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 :=

View File

@ -25,6 +25,7 @@
namespace Vmm { namespace Vmm {
class Virtio_split_queue; class Virtio_split_queue;
class Virtio_device_base;
template <typename QUEUE, unsigned NUM> class Virtio_device; template <typename QUEUE, unsigned NUM> class Virtio_device;
using namespace Genode; using namespace Genode;
@ -219,8 +220,16 @@ class Vmm::Virtio_split_queue
}; };
class Vmm::Virtio_device_base : public List<Virtio_device_base>::Element
{
public:
virtual ~Virtio_device_base() {}
};
template <typename QUEUE, unsigned NUM> template <typename QUEUE, unsigned NUM>
class Vmm::Virtio_device : public Vmm::Mmio_device class Vmm::Virtio_device : public Vmm::Mmio_device, public Virtio_device_base
{ {
protected: protected:

View File

@ -11,53 +11,87 @@
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
*/ */
#include <fdt.h>
#include <vm.h> #include <vm.h>
using Vmm::Vm; 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), if (i == 0)
_kernel_rom.local_addr<void>(), return vm._env.ep();
_kernel_rom.size());
_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), return align_addr(KERNEL_OFFSET+_kernel_rom.size(), LOG2_2MB);
_dtb_rom.local_addr<void>(), }
_dtb_rom.size());
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<void>(), _kernel_rom.size());
} }
void Vm::_load_initrd() void Vm::_load_initrd()
{ {
Genode::memcpy((void*)(_ram.local() + INITRD_OFFSET), memcpy((void*)(_ram.local() + _initrd_offset()),
_initrd_rom.local_addr<void>(), _initrd_rom.local_addr<void>(), _initrd_rom.size());
_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() Vmm::Cpu & Vm::boot_cpu()
{ {
if (!_cpus[0].constructed()) if (!_cpu_list.first()) {
_cpus[0].construct(*this, _vm, _bus, _gic, _env, _heap, _env.ep(), 0); Cpu_entry * last = nullptr;
return *_cpus[0]; 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) Vm::Vm(Genode::Env & env, Heap & heap, Config & config)
: _env(env), :
_gic("Gicv3", GICD_MMIO_START, GICD_MMIO_SIZE, _env(env),
MAX_CPUS, GIC_VERSION, _vm, _bus, env), _heap(heap),
_uart("Pl011", PL011_MMIO_START, PL011_MMIO_SIZE, _config(config),
PL011_IRQ, boot_cpu(), _bus, env), _gic("Gic", GICD_MMIO_START, GICD_MMIO_SIZE,
_virtio_console("HVC", VIRTIO_CONSOLE_MMIO_START, VIRTIO_CONSOLE_MMIO_SIZE, config.cpu_count(), config.gic_version(), _vm, _bus, env),
VIRTIO_CONSOLE_IRQ, boot_cpu(), _bus, _ram, env), _uart("Pl011", PL011_MMIO_START, PL011_MMIO_SIZE,
_virtio_net("Net", VIRTIO_NET_MMIO_START, VIRTIO_NET_MMIO_SIZE, PL011_IRQ, boot_cpu(), _bus, env)
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.attach(_vm_ram.cap(), RAM_START, _vm.attach(_vm_ram.cap(), RAM_START,
Genode::Vm_session::Attach_attr { .offset = 0, Genode::Vm_session::Attach_attr { .offset = 0,
@ -65,21 +99,45 @@ Vm::Vm(Genode::Env & env)
.executable = true, .executable = true,
.writeable = true }); .writeable = true });
_load_kernel(); _config.for_each_virtio_device([&] (Config::Virtio_device const & dev) {
_load_dtb(); switch (dev.type) {
_load_initrd(); 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++) { _load_kernel();
Genode::Affinity::Space space = _env.cpu().affinity_space(); _load_initrd();
Genode::Affinity::Location location(space.location_of_index(i)); _load_dtb();
_eps[i].construct(_env, STACK_SIZE, "vcpu ep", location);
_cpus[i].construct(*this, _vm, _bus, _gic, _env, _heap, *_eps[i], i);
}
Genode::log("Start virtual machine ..."); Genode::log("Start virtual machine ...");
Cpu & cpu = boot_cpu(); Cpu & cpu = boot_cpu();
cpu.initialize_boot(_ram.base() + KERNEL_OFFSET, cpu.initialize_boot(_ram.base() + KERNEL_OFFSET,
_ram.base() + DTB_OFFSET); _ram.base() + _dtb_offset());
cpu.run(); cpu.run();
}; };
Vm::~Vm()
{
while (_cpu_list.first()) destroy(_heap, _cpu_list.first());
while (_device_list.first()) destroy(_heap, _device_list.first());
}

View File

@ -15,6 +15,7 @@
#define _SRC__SERVER__VMM__VM_H_ #define _SRC__SERVER__VMM__VM_H_
#include <board.h> #include <board.h>
#include <config.h>
#include <ram.h> #include <ram.h>
#include <exception.h> #include <exception.h>
#include <cpu.h> #include <cpu.h>
@ -28,42 +29,54 @@
#include <base/attached_rom_dataspace.h> #include <base/attached_rom_dataspace.h>
#include <vm_session/connection.h> #include <vm_session/connection.h>
namespace Vmm { class Vm; } namespace Vmm {
class Vm;
using namespace Genode;
}
class Vmm::Vm class Vmm::Vm
{ {
private: private:
using Ep = Genode::Entrypoint; struct Cpu_entry : List<Cpu_entry>::Element
{
enum { STACK_SIZE = sizeof(unsigned long) * 2048, };
enum { STACK_SIZE = sizeof(unsigned long) * 2048, }; Constructible<Entrypoint> _ep {};
Genode::Env & _env; Entrypoint & ep(unsigned i, Vm & vm);
Genode::Vm_connection _vm { _env }; Cpu cpu;
Genode::Attached_rom_dataspace _kernel_rom { _env, "linux" };
Genode::Attached_rom_dataspace _dtb_rom { _env, "dtb" }; Cpu_entry(unsigned idx, Vm & vm);
Genode::Attached_rom_dataspace _initrd_rom { _env, "initrd" }; };
Genode::Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(),
RAM_SIZE, Genode::CACHED }; Env & _env;
Ram _ram { RAM_START, RAM_SIZE, Heap & _heap;
(Genode::addr_t)_vm_ram.local_addr<void>()}; Config & _config;
Genode::Heap _heap { _env.ram(), _env.rm() }; Vm_connection _vm { _env };
Mmio_bus _bus; Attached_rom_dataspace _kernel_rom { _env, _config.kernel_name() };
Gic _gic; Attached_rom_dataspace _initrd_rom { _env, _config.initrd_name() };
Genode::Constructible<Ep> _eps[MAX_CPUS]; Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(),
Genode::Constructible<Cpu> _cpus[MAX_CPUS]; _config.ram_size(), CACHED };
Pl011 _uart; Ram _ram { RAM_START, _config.ram_size(),
Virtio_console _virtio_console; (addr_t)_vm_ram.local_addr<void>()};
Virtio_net _virtio_net; Mmio_bus _bus;
Virtio_block_device _virtio_block; Gic _gic;
List<Cpu_entry> _cpu_list;
List<Virtio_device_base> _device_list;
Pl011 _uart;
addr_t _initrd_offset() const;
addr_t _dtb_offset() const;
void _load_kernel(); void _load_kernel();
void _load_dtb();
void _load_initrd(); void _load_initrd();
void _load_dtb();
public: public:
Vm(Genode::Env & env); Vm(Env & env, Heap & heap, Config & config);
~Vm();
Mmio_bus & bus() { return _bus; } Mmio_bus & bus() { return _bus; }
Cpu & boot_cpu(); Cpu & boot_cpu();
@ -71,11 +84,16 @@ class Vmm::Vm
template <typename F> template <typename F>
void cpu(unsigned cpu, F func) void cpu(unsigned cpu, F func)
{ {
if (cpu >= MAX_CPUS) Genode::error("Cpu number out of bounds "); for (Cpu_entry * ce = _cpu_list.first(); ce; ce = ce->next())
else func(*_cpus[cpu]); if (ce->cpu.cpu_id() == cpu) func(ce->cpu);
} }
static unsigned last_cpu() { return MAX_CPUS - 1; } template <typename F>
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_ */ #endif /* _SRC__SERVER__VMM__VM_H_ */