os: introduce vmm component for ARMv8

Fix #3553
This commit is contained in:
Stefan Kalkowski 2019-09-03 16:29:27 +02:00 committed by Norman Feske
parent af29dcf557
commit 972e1893c9
27 changed files with 2736 additions and 10 deletions

View File

@ -6,7 +6,7 @@
assert_spec hw
if { ![have_spec imx7d_sabre] && ![have_spec arndale] } {
if { ![have_spec imx7d_sabre] && ![have_spec arndale] && ![have_spec imx8q_evk] } {
puts "Run script is not supported on this platform"
exit 0
}
@ -21,7 +21,7 @@ build $build_components
create_boot_directory
install_config {
<config verbose="yes">
<config verbose="yes" prio_levels="2">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
@ -44,7 +44,7 @@ install_config {
<resource name="RAM" quantum="1M"/>
<provides> <service name="Terminal"/> </provides>
</start>
<start name="vmm">
<start name="vmm" caps="200" priority="-1">
<resource name="RAM" quantum="256M"/>
</start>
<start name="vm">
@ -56,6 +56,18 @@ install_config {
}
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-15.02/arm_vt/linux
}
if {![file exists bin/dtb]} {
puts "Download device tree blob ..."
exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-15.02/arm_vt/dtb
}
#
# This test uses a Linux kernel built from unmodified vanilla kernel sources
# but using a slightly simplified kernel configuration, as well as device tree
@ -76,15 +88,62 @@ install_config {
# ! make ARCH=arm CROSS_COMPILE=<cross_compiler_prefix> -j8 Image
# ! make ARCH=arm CROSS_COMPILE=<cross_compiler_prefix> vexpress-v2p-ca15-tc1.dtb
#
if {![file exists bin/linux]} {
puts "Download linux kernel ..."
exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-15.02/arm_vt/linux
}
if {![file exists bin/dtb]} {
puts "Download device tree blob ..."
exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-15.02/arm_vt/dtb
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-19.11/linux-arm64-image-5.2
}
if {![file exists bin/dtb]} {
puts "Download device tree blob ..."
exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-19.11/dtb-arm64-virt
}
if {![file exists bin/initrd]} {
puts "Download initramfs ..."
exec >& /dev/null wget -c -O bin/initrd http://genode.org/files/release-19.11/initrd-arm64
}
#
# 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 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
#
#
# To construct the initrd do the following:
# * get and install gcc from linaro
# (https://releases.linaro.org/components/toolchain/binaries/latest-7/)
# * build busybox
# wget https://busybox.net/downloads/busybox-1.29.3.tar.bz2
# tar xjf busybox-1.29.3.tar.bz2
# mkdir build-busybox-aarch64
# cd busybox-1.29.3
# make O=../build-busybox-aarch64 defconfig
# make O=../build-busybox-aarch64 menuconfig
#
# [*] Setting -> Build static binary (no shared libs)
#
# cd ../build-busybox-aarch64
# make CROSS_COMPILE=/usr/local/gcc-linaro/bin/aarch64-linux-gnu- install -j6
# * create ramdisk
# cd _install
# find . | cpio -H newc -o | gzip > ../initrd
}
set boot_modules {
@ -96,6 +155,7 @@ set boot_modules {
linux
dtb
}
append_if [have_spec arm_64] boot_modules initrd
build_boot_image $boot_modules
#

View File

@ -0,0 +1,32 @@
/*
* \brief VMM address space utility
* \author Stefan Kalkowski
* \date 2019-09-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.
*/
#include <address_space.h>
using Vmm::Address_range;
Address_range::Address_range(Genode::uint64_t start,
Genode::uint64_t size)
: start(start), size(size) { }
Address_range & Address_range::find(Address_range & bus_addr)
{
if (match(bus_addr))
return *this;
Address_range * ar =
Avl_node<Address_range>::child(bus_addr.start > start);
if (!ar) throw Not_found(bus_addr);
return ar->find(bus_addr);
}

View File

@ -0,0 +1,82 @@
/*
* \brief VMM address space utility
* \author Stefan Kalkowski
* \date 2019-09-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__ADDRESS_SPACE_H_
#define _SRC__SERVER__VMM__ADDRESS_SPACE_H_
#include <exception.h>
#include <util/avl_tree.h>
namespace Vmm {
struct Address_range;
class Address_space;
}
struct Vmm::Address_range : Genode::Avl_node<Address_range>
{
Genode::uint64_t const start;
Genode::uint64_t const size;
Address_range(Genode::uint64_t start,
Genode::uint64_t size);
Genode::uint64_t end() const { return start + size; }
bool match(Address_range & other) const {
return other.start >= start && other.end() <= end(); }
Address_range & find(Address_range & bus_addr);
struct Not_found : Exception
{
Not_found(Address_range & access)
: Exception("Could not find ", access) {}
};
void print(Genode::Output & out) const
{
Genode::print(out, "address=", Genode::Hex(start),
" width=", Genode::Hex(size));
}
/************************
** Avl_node interface **
************************/
bool higher(Address_range * range) {
return range->start > start; }
};
class Vmm::Address_space
{
private:
Genode::Avl_tree<Address_range> _tree;
public:
template <typename T>
T & get(Address_range & bus_addr)
{
if (!_tree.first()) throw Address_range::Not_found(bus_addr);
return *static_cast<T*>(&_tree.first()->find(bus_addr));
}
void add(Address_range & ar) { _tree.insert(&ar); }
};
#endif /* _SRC__SERVER__VMM__ADDRESS_SPACE_H_ */

View File

@ -0,0 +1,403 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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.
*/
#include <cpu.h>
#include <vm.h>
#include <psci.h>
using Vmm::Cpu;
using Vmm::Gic;
Genode::Lock & Vmm::lock() { static Genode::Lock l {}; return l; }
Cpu::System_register::Iss::access_t
Cpu::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
unsigned crm, unsigned op2)
{
access_t v = 0;
Crn::set(v, crn);
Crm::set(v, crm);
Opcode0::set(v, op0);
Opcode1::set(v, op1);
Opcode2::set(v, op2);
return v;
};
Cpu::System_register::Iss::access_t
Cpu::System_register::Iss::mask_encoding(access_t v)
{
return Crm::masked(v) |
Crn::masked(v) |
Opcode1::masked(v) |
Opcode2::masked(v) |
Opcode0::masked(v);
}
Cpu::System_register::System_register(unsigned op0,
unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2,
const char * name,
bool writeable,
Genode::addr_t v,
Genode::Avl_tree<System_register> & tree)
: _encoding(Iss::value(op0, crn, op1, crm, op2)),
_name(name),
_writeable(writeable),
_value(v)
{
tree.insert(this);
}
Genode::addr_t Cpu::Ccsidr::read() const
{
struct Clidr : Genode::Register<32>
{
enum Cache_entry {
NO_CACHE,
INSTRUCTION_CACHE_ONLY,
DATA_CACHE_ONLY,
SEPARATE_CACHE,
UNIFIED_CACHE
};
static unsigned level(unsigned l, access_t reg) {
return (reg >> l*3) & 0b111; }
};
struct Csselr : Genode::Register<32>
{
struct Instr : Bitfield<0, 1> {};
struct Level : Bitfield<1, 4> {};
};
enum { INVALID = 0xffffffff };
unsigned level = Csselr::Level::get(csselr.read());
bool instr = Csselr::Instr::get(csselr.read());
if (level > 6) {
Genode::warning("Invalid Csselr value!");
return INVALID;
}
unsigned ce = Clidr::level(level, state.clidr_el1);
if (ce == Clidr::NO_CACHE ||
(ce == Clidr::DATA_CACHE_ONLY && instr)) {
Genode::warning("Invalid Csselr value!");
return INVALID;
}
if (ce == Clidr::INSTRUCTION_CACHE_ONLY ||
(ce == Clidr::SEPARATE_CACHE && instr)) {
Genode::log("Return Ccsidr instr value ", state.ccsidr_inst_el1[level]);
return state.ccsidr_inst_el1[level];
}
Genode::log("Return Ccsidr value ", state.ccsidr_data_el1[level]);
return state.ccsidr_data_el1[level];
}
Genode::addr_t Cpu::Ctr_el0::read() const
{
Genode::addr_t ret;
asm volatile("mrs %0, ctr_el0" : "=r" (ret));
return ret;
}
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<<i)) {
vm.cpu(i, [&] (Cpu & cpu) {
cpu.gic().irq(irq).assert();
cpu.recall();
});
}
}
};
bool Cpu::_handle_sys_reg()
{
using Iss = System_register::Iss;
Iss::access_t v = _state.esr_el2;
System_register * reg = _reg_tree.first();
if (reg) reg = reg->find_by_encoding(Iss::mask_encoding(v));
if (!reg) {
Genode::error("ignore unknown system register access @ ip=", (void*)_state.ip, ":");
Genode::error(Iss::Direction::get(v) ? "read" : "write",
": "
"op0=", Iss::Opcode0::get(v), " "
"op1=", Iss::Opcode1::get(v), " "
"r", Iss::Register::get(v), " "
"crn=", Iss::Crn::get(v), " "
"crm=", Iss::Crm::get(v), " ",
"op2=", Iss::Opcode2::get(v));
if (Iss::Direction::get(v)) _state.r[Iss::Register::get(v)] = 0;
_state.ip += sizeof(Genode::uint32_t);
return false;
}
if (Iss::Direction::get(v)) { /* read access */
_state.r[Iss::Register::get(v)] = reg->read();
} else { /* write access */
if (!reg->writeable()) {
Genode::error("writing to system register ",
reg->name(), " not allowed!");
return false;
}
reg->write(_state.r[Iss::Register::get(v)]);
}
_state.ip += sizeof(Genode::uint32_t);
return true;
}
void Cpu::_handle_wfi()
{
_state.ip += sizeof(Genode::uint32_t);
if (_state.esr_el2 & 1) return; /* WFE */
_active = false;
_timer.schedule_timeout();
}
void Cpu::_handle_brk()
{
Genode::uint64_t offset = 0x0;
if (!(_state.pstate & 0b100)) {
offset = 0x400;
} else if (_state.pstate & 0b1) {
offset = 0x200;
}
_state.esr_el1 = _state.esr_el2;
_state.spsr_el1 = _state.pstate;
_state.elr_el1 = _state.ip;
_state.ip = _state.vbar_el1 + offset;
_state.pstate = 0b1111000101;
}
void Cpu::_handle_sync()
{
/* check device number*/
switch (Esr::Ec::get(_state.esr_el2)) {
case Esr::Ec::HVC:
_handle_hyper_call();
break;
case Esr::Ec::MRS_MSR:
_handle_sys_reg();
break;
case Esr::Ec::DA:
_handle_data_abort();
break;
case Esr::Ec::WFI:
_handle_wfi();
return;
case Esr::Ec::BRK:
_handle_brk();
return;
default:
throw Exception("Unknown trap: %x",
Esr::Ec::get(_state.esr_el2));
};
}
void Cpu::_handle_irq()
{
enum { /* FIXME */ VT_TIMER_IRQ = 27 };
switch (_state.irqs.last_irq) {
case VT_TIMER_IRQ:
_timer.handle_irq();
break;
default:
_gic.handle_irq();
};
}
void Cpu::_handle_hyper_call()
{
switch(_state.r[0]) {
case Psci::PSCI_VERSION:
_state.r[0] = Psci::VERSION;
return;
case Psci::MIGRATE_INFO_TYPE:
_state.r[0] = Psci::NOT_SUPPORTED;
return;
case Psci::PSCI_FEATURES:
_state.r[0] = Psci::NOT_SUPPORTED;
return;
case Psci::CPU_ON:
_vm.cpu((unsigned)_state.r[1], [&] (Cpu & cpu) {
cpu.state().ip = _state.r[2];
cpu.state().r[0] = _state.r[3];
cpu.run();
});
_state.r[0] = Psci::SUCCESS;
return;
default:
Genode::warning("unknown hypercall! ", cpu_id());
dump();
};
}
void Cpu::_handle_data_abort()
{
_vm.bus().handle_memory_access(*this);
_state.ip += sizeof(Genode::uint32_t);
}
void Cpu::_update_state()
{
if (!_gic.pending_irq()) return;
_active = true;
_timer.cancel_timeout();
}
unsigned Cpu::cpu_id() const { return _vcpu_id.id; }
void Cpu::run() { _vm_session.run(_vcpu_id); }
void Cpu::pause() { _vm_session.pause(_vcpu_id); }
bool Cpu::active() const { return _active; }
Cpu::State & Cpu::state() const { return _state; }
Gic::Gicd_banked & Cpu::gic() { return _gic; }
void Cpu::handle_exception()
{
/* check exception reason */
switch (_state.exception_type) {
case NO_EXCEPTION: break;
case AARCH64_IRQ: _handle_irq(); break;
case AARCH64_SYNC: _handle_sync(); break;
default:
throw Exception("Curious exception ",
_state.exception_type, " occured");
}
_state.exception_type = NO_EXCEPTION;
}
void Cpu::dump()
{
using namespace Genode;
auto lambda = [] (addr_t exc) {
switch (exc) {
case AARCH64_SYNC: return "aarch64 sync";
case AARCH64_IRQ: return "aarch64 irq";
case AARCH64_FIQ: return "aarch64 fiq";
case AARCH64_SERROR: return "aarch64 serr";
case AARCH32_SYNC: return "aarch32 sync";
case AARCH32_IRQ: return "aarch32 irq";
case AARCH32_FIQ: return "aarch32 fiq";
case AARCH32_SERROR: return "aarch32 serr";
default: return "unknown";
};
};
log("VM state (", _active ? "active" : "inactive", ") :");
for (unsigned i = 0; i < 31; i++) {
log(" r", i, " = ",
Hex(_state.r[i], Hex::PREFIX, Hex::PAD));
}
log(" sp = ", Hex(_state.sp, Hex::PREFIX, Hex::PAD));
log(" ip = ", Hex(_state.ip, Hex::PREFIX, Hex::PAD));
log(" sp_el1 = ", Hex(_state.sp_el1, Hex::PREFIX, Hex::PAD));
log(" elr_el1 = ", Hex(_state.elr_el1, Hex::PREFIX, Hex::PAD));
log(" pstate = ", Hex(_state.pstate, Hex::PREFIX, Hex::PAD));
log(" exception = ", _state.exception_type, " (",
lambda(_state.exception_type), ")");
log(" esr_el2 = ", Hex(_state.esr_el2, Hex::PREFIX, Hex::PAD));
_timer.dump();
}
void Cpu::recall()
{
Genode::Signal_transmitter(_vm_handler).submit();
};
Cpu::Cpu(Vm & vm,
Genode::Vm_connection & vm_session,
Mmio_bus & bus,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep)
: _vm(vm),
_vm_session(vm_session),
_heap(heap),
_vm_handler(*this, ep, *this, &Cpu::_handle_nothing),
_vcpu_id(_vm_session.with_upgrade([&]() {
return _vm_session.create_vcpu(heap, env, _vm_handler);
})),
_state(*((State*)env.rm().attach(_vm_session.cpu_state(_vcpu_id)))),
// op0, crn, op1, crm, op2, writeable, reset value
_sr_id_aa64afr0_el1 (3, 0, 0, 5, 4, "ID_AA64AFR0_EL1", false, 0x0, _reg_tree),
_sr_id_aa64afr1_el1 (3, 0, 0, 5, 5, "ID_AA64AFR1_EL1", false, 0x0, _reg_tree),
_sr_id_aa64dfr0_el1 (3, 0, 0, 5, 0, "ID_AA64DFR0_EL1", false, 0x6, _reg_tree),
_sr_id_aa64dfr1_el1 (3, 0, 0, 5, 1, "ID_AA64DFR1_EL1", false, 0x0, _reg_tree),
_sr_id_aa64isar0_el1(3, 0, 0, 6, 0, "ID_AA64ISAR0_EL1", false, _state.id_aa64isar0_el1, _reg_tree),
_sr_id_aa64isar1_el1(3, 0, 0, 6, 1, "ID_AA64ISAR1_EL1", false, _state.id_aa64isar1_el1, _reg_tree),
_sr_id_aa64mmfr0_el1(3, 0, 0, 7, 0, "ID_AA64MMFR0_EL1", false, _state.id_aa64mmfr0_el1, _reg_tree),
_sr_id_aa64mmfr1_el1(3, 0, 0, 7, 1, "ID_AA64MMFR1_EL1", false, _state.id_aa64mmfr1_el1, _reg_tree),
_sr_id_aa64mmfr2_el1(3, 0, 0, 7, 2, "ID_AA64MMFR2_EL1", false, _state.id_aa64mmfr2_el1, _reg_tree),
_sr_id_aa64pfr0_el1 (_state.id_aa64pfr0_el1, _reg_tree),
_sr_id_aa64pfr1_el1 (3, 0, 0, 4, 1, "ID_AA64PFR1_EL1", false, 0x0, _reg_tree),
_sr_id_aa64zfr0_el1 (3, 0, 0, 4, 4, "ID_AA64ZFR0_EL1", false, 0x0, _reg_tree),
_sr_aidr_el1 (3, 0, 1, 0, 7, "AIDR_EL1", false, 0x0, _reg_tree),
_sr_revidr_el1 (3, 0, 0, 0, 6, "REVIDR_EL1", false, 0x0, _reg_tree),
_sr_clidr_el1 (3, 0, 1, 0, 1, "CLIDR_EL1", false, _state.clidr_el1, _reg_tree),
_sr_csselr_el1 (3, 0, 2, 0, 0, "CSSELR_EL1", true, 0x0, _reg_tree),
_sr_ctr_el0 (_reg_tree),
_sr_ccsidr_el1 (_sr_csselr_el1, _state, _reg_tree),
//_sr_pmccfiltr_el0 (3, 14, 3, 15, 7, "PMCCFILTR_EL0", true, 0x0, _reg_tree),
_sr_pmuserenr_el0 (3, 9, 3, 14, 0, "PMUSEREN_EL0", true, 0x0, _reg_tree),
_sr_dbgbcr0 (2, 0, 0, 0, 5, "DBGBCR_EL1", true, 0x0, _reg_tree),
_sr_dbgbvr0 (2, 0, 0, 0, 4, "DBGBVR_EL1", true, 0x0, _reg_tree),
_sr_dbgwcr0 (2, 0, 0, 0, 7, "DBGWCR_EL1", true, 0x0, _reg_tree),
_sr_dbgwvr0 (2, 0, 0, 0, 6, "DBGWVR_EL1", true, 0x0, _reg_tree),
_sr_mdscr (2, 0, 0, 2, 2, "MDSCR_EL1", true, 0x0, _reg_tree),
_sr_osdlr (2, 1, 0, 3, 4, "OSDLR_EL1", true, 0x0, _reg_tree),
_sr_oslar (2, 1, 0, 0, 4, "OSLAR_EL1", true, 0x0, _reg_tree),
_sr_sgi1r_el1 (_reg_tree, vm),
_gic(*this, gic, bus),
_timer(env, ep, _gic.irq(27), *this)
{
_state.pstate = 0b1111000101; /* el1 mode and IRQs disabled */
_state.vmpidr_el2 = cpu_id();
}

View File

@ -0,0 +1,331 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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__CPU_H_
#define _SRC__SERVER__VMM__CPU_H_
#include <exception.h>
#include <generic_timer.h>
#include <base/env.h>
#include <base/heap.h>
#include <cpu/vm_state_virtualization.h>
#include <util/mmio.h>
#include <vm_session/connection.h>
namespace Vmm {
class Vm;
class Cpu;
Genode::Lock & lock();
}
class Vmm::Cpu
{
public:
using State = Genode::Vm_state;
struct Esr : Genode::Register<32>
{
struct Ec : Bitfield<26, 6>
{
enum {
WFI = 0x1,
HVC = 0x16,
MRS_MSR = 0x18,
DA = 0x24,
BRK = 0x3c
};
};
};
Cpu(Vm & vm,
Genode::Vm_connection & vm_session,
Mmio_bus & bus,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep);
unsigned cpu_id() const;
void run();
void pause();
bool active() const;
State & state() const;
Gic::Gicd_banked & gic();
void dump();
void handle_exception();
void recall();
template <typename FUNC>
void handle_signal(FUNC handler)
{
Genode::Lock::Guard guard(lock());
if (active()) {
pause();
handle_exception();
}
handler();
_update_state();
if (active()) run();
}
template <typename T>
struct Signal_handler : Genode::Vm_handler<Signal_handler<T>>
{
using Base = Genode::Vm_handler<Signal_handler<T>>;
Cpu & cpu;
T & obj;
void (T::*member)();
void handle()
{
try {
cpu.handle_signal([this] () { (obj.*member)(); });
} catch(Exception &e) {
Genode::error(e);
cpu.dump();
}
}
Signal_handler(Cpu & cpu,
Genode::Entrypoint & ep,
T & o,
void (T::*f)())
: Base(ep, *this, &Signal_handler::handle),
cpu(cpu), obj(o), member(f) {}
};
private:
enum Exception_type {
AARCH64_SYNC = 0x400,
AARCH64_IRQ = 0x480,
AARCH64_FIQ = 0x500,
AARCH64_SERROR = 0x580,
AARCH32_SYNC = 0x600,
AARCH32_IRQ = 0x680,
AARCH32_FIQ = 0x700,
AARCH32_SERROR = 0x780,
NO_EXCEPTION = 0xffff
};
class System_register : public Genode::Avl_node<System_register>
{
private:
const Esr::access_t _encoding;
const char *_name;
const bool _writeable;
Genode::uint64_t _value;
public:
struct Iss : Esr
{
struct Direction : Bitfield<0, 1> {};
struct Crm : Bitfield<1, 4> {};
struct Register : Bitfield<5, 5> {};
struct Crn : Bitfield<10, 4> {};
struct Opcode1 : Bitfield<14, 3> {};
struct Opcode2 : Bitfield<17, 3> {};
struct Opcode0 : Bitfield<20, 2> {};
static access_t value(unsigned op0,
unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2);
static access_t mask_encoding(access_t v);
};
System_register(unsigned op0,
unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2,
const char * name,
bool writeable,
Genode::addr_t v,
Genode::Avl_tree<System_register> & tree);
const char * name() const { return _name; }
const bool writeable() const { return _writeable; }
System_register * find_by_encoding(Iss::access_t e)
{
if (e == _encoding) return this;
System_register * r =
Avl_node<System_register>::child(e > _encoding);
return r ? r->find_by_encoding(e) : nullptr;
}
virtual void write(Genode::addr_t v) {
_value = (Genode::addr_t)v; }
virtual Genode::addr_t read() const {
return (Genode::addr_t)(_value); }
/************************
** Avl node interface **
************************/
bool higher(System_register *r) {
return (r->_encoding > _encoding); }
};
class Id_aa64pfr0 : public System_register,
public Genode::Register<64>
{
private:
struct El0 : Bitfield<0, 4> { enum { AARCH64_ONLY = 1 }; };
struct El1 : Bitfield<4, 4> { enum { AARCH64_ONLY = 1 }; };
struct El2 : Bitfield<8, 4> { enum { NOT_IMPLEMENTED }; };
struct El3 : Bitfield<12, 4> { enum { NOT_IMPLEMENTED }; };
struct Ras : Bitfield<28, 4> { enum { NOT_IMPLEMENTED }; };
struct Sve : Bitfield<32, 4> { enum { NOT_IMPLEMENTED }; };
access_t _reset_value(access_t orig)
{
El0::set(orig, El0::AARCH64_ONLY);
El1::set(orig, El1::AARCH64_ONLY);
El2::set(orig, El2::NOT_IMPLEMENTED);
El3::set(orig, El3::NOT_IMPLEMENTED);
Ras::set(orig, Ras::NOT_IMPLEMENTED);
Sve::set(orig, Sve::NOT_IMPLEMENTED);
return orig;
}
public:
Id_aa64pfr0(Genode::uint64_t id_aa64pfr0,
Genode::Avl_tree<System_register> & tree)
: System_register(3, 0, 0, 4, 0, "ID_AA64PFR0_EL1", false,
_reset_value(id_aa64pfr0), tree) {}
};
struct Ccsidr : System_register
{
System_register & csselr;
State & state;
Ccsidr(System_register &csselr,
State & state,
Genode::Avl_tree<System_register> & tree)
: System_register(3, 0, 1, 0, 0, "CCSIDR_EL1", false, 0x0, tree),
csselr(csselr), state(state) {}
virtual Genode::addr_t read() const override;
};
struct Ctr_el0 : System_register
{
Ctr_el0(Genode::Avl_tree<System_register> & tree)
: System_register(3, 0, 3, 0, 1, "CTR_EL0", false, 0x0, tree) {}
virtual Genode::addr_t read() const override;
};
struct Icc_sgi1r_el1 : System_register
{
Vm & vm;
Icc_sgi1r_el1(Genode::Avl_tree<System_register> & tree, Vm & vm)
: System_register(3, 12, 0, 11, 5, "SGI1R_EL1", true, 0x0, tree),
vm(vm) {}
virtual void write(Genode::addr_t v) override;
};
bool _active { true };
Vm & _vm;
Genode::Vm_connection & _vm_session;
Genode::Heap & _heap;
Signal_handler<Cpu> _vm_handler;
Genode::Vm_session::Vcpu_id _vcpu_id;
State & _state;
Genode::Avl_tree<System_register> _reg_tree;
/******************************
** Identification registers **
******************************/
System_register _sr_id_aa64afr0_el1;
System_register _sr_id_aa64afr1_el1;
System_register _sr_id_aa64dfr0_el1;
System_register _sr_id_aa64dfr1_el1;
System_register _sr_id_aa64isar0_el1;
System_register _sr_id_aa64isar1_el1;
System_register _sr_id_aa64mmfr0_el1;
System_register _sr_id_aa64mmfr1_el1;
System_register _sr_id_aa64mmfr2_el1;
Id_aa64pfr0 _sr_id_aa64pfr0_el1;
System_register _sr_id_aa64pfr1_el1;
System_register _sr_id_aa64zfr0_el1;
System_register _sr_aidr_el1;
System_register _sr_revidr_el1;
/*********************
** Cache registers **
*********************/
System_register _sr_clidr_el1;
System_register _sr_csselr_el1;
Ctr_el0 _sr_ctr_el0;
Ccsidr _sr_ccsidr_el1;
/***********************************
** Performance monitor registers **
***********************************/
System_register _sr_pmuserenr_el0;
/*****************************
** Debug monitor registers **
*****************************/
System_register _sr_dbgbcr0;
System_register _sr_dbgbvr0;
System_register _sr_dbgwcr0;
System_register _sr_dbgwvr0;
System_register _sr_mdscr;
System_register _sr_osdlr;
System_register _sr_oslar;
/***********************
** Local peripherals **
***********************/
Icc_sgi1r_el1 _sr_sgi1r_el1;
Gic::Gicd_banked _gic;
Generic_timer _timer;
void _handle_nothing() {}
bool _handle_sys_reg();
void _handle_brk();
void _handle_wfi();
void _handle_sync();
void _handle_irq();
void _handle_data_abort();
void _handle_hyper_call();
void _update_state();
};
#endif /* _SRC__SERVER__VMM__CPU_H_ */

View File

@ -0,0 +1,22 @@
/*
* \brief VMM exception handling
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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__EXCEPTION_H_
#define _SRC__SERVER__VMM__EXCEPTION_H_
#include <util/string.h>
namespace Vmm { using Exception = Genode::String<128>; }
#endif /* _SRC__SERVER__VMM__EXCEPTION_H_ */

View File

@ -0,0 +1,116 @@
/*
* \brief VMM ARM Generic timer device model
* \author Stefan Kalkowski
* \date 2019-08-20
*/
/*
* 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.
*/
#include <cpu.h>
#include <generic_timer.h>
using Vmm::Generic_timer;
Genode::uint64_t Generic_timer::_ticks_per_ms()
{
static Genode::uint64_t ticks_per_ms = 0;
if (!ticks_per_ms) {
Genode::uint32_t freq = 0;
asm volatile("mrs %0, cntfrq_el0" : "=r" (freq));
ticks_per_ms = freq / 1000;
}
return ticks_per_ms;
}
bool Generic_timer::_enabled() {
return Ctrl::Enabled::get(_cpu.state().timer.control); }
bool Generic_timer::_masked() {
return Ctrl::Imask::get(_cpu.state().timer.control); }
bool Generic_timer::_pending() {
return Ctrl::Istatus::get(_cpu.state().timer.control); }
void Generic_timer::_handle_timeout(Genode::Duration)
{
_cpu.handle_signal([this] (void) {
if (_enabled() && !_masked()) handle_irq();
});
}
Genode::uint64_t Generic_timer::_usecs_left()
{
Genode::uint64_t count;
asm volatile("mrs %0, cntpct_el0" : "=r" (count));
count -= _cpu.state().timer.offset;
if (count > _cpu.state().timer.compare) return 0;
return Genode::timer_ticks_to_us(_cpu.state().timer.compare - count,
_ticks_per_ms());
}
Generic_timer::Generic_timer(Genode::Env & env,
Genode::Entrypoint & ep,
Gic::Irq & irq,
Cpu & cpu)
: _timer(env, ep),
_timeout(_timer, *this, &Generic_timer::_handle_timeout),
_irq(irq),
_cpu(cpu)
{
_cpu.state().timer.irq = true;
_irq.handler(*this);
}
void Generic_timer::schedule_timeout()
{
if (_pending()) {
handle_irq();
return;
}
if (_enabled()) {
if (_usecs_left()) {
_timeout.schedule(Genode::Microseconds(_usecs_left()));
} else _handle_timeout(Genode::Duration(Genode::Microseconds(0)));
}
}
void Generic_timer::cancel_timeout()
{
if (_timeout.scheduled()) _timeout.discard();
}
void Generic_timer::handle_irq()
{
_irq.assert();
_cpu.state().timer.irq = false;
}
void Generic_timer::eoi()
{
_cpu.state().timer.irq = true;
};
void Generic_timer::dump()
{
using namespace Genode;
log(" timer.ctl = ", Hex(_cpu.state().timer.control, Hex::PREFIX, Hex::PAD));
log(" timer.cmp = ", Hex(_cpu.state().timer.compare, Hex::PREFIX, Hex::PAD));
}

View File

@ -0,0 +1,74 @@
/*
* \brief VMM ARM Generic timer device model
* \author Stefan Kalkowski
* \date 2019-08-20
*/
/*
* 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__GENERIC_TIMER_H_
#define _SRC__SERVER__VMM__GENERIC_TIMER_H_
#include <gic.h>
#include <cpu/vm_state_virtualization.h>
#include <drivers/timer/util.h>
#include <timer_session/connection.h>
#include <util/register.h>
namespace Vmm {
class Cpu;
class Generic_timer;
}
class Vmm::Generic_timer : Gic::Irq::Irq_handler
{
private:
Timer::Connection _timer;
Timer::One_shot_timeout<Generic_timer> _timeout;
Gic::Irq & _irq;
Cpu & _cpu;
struct Ctrl : Genode::Register<32>
{
struct Enabled : Bitfield<0,1> {};
struct Imask : Bitfield<1,1> {};
struct Istatus : Bitfield<2,1> {};
};
Genode::uint64_t _ticks_per_ms();
bool _enabled();
bool _masked();
bool _pending();
void _handle_timeout(Genode::Duration);
Genode::uint64_t _usecs_left();
public:
Generic_timer(Genode::Env & env,
Genode::Entrypoint & ep,
Gic::Irq & irq,
Cpu & cpu);
void schedule_timeout();
void cancel_timeout();
void handle_irq();
void dump();
/*****************
** Irq_handler **
*****************/
void eoi() override;
};
#endif /* _SRC__SERVER__VMM__GENERIC_TIMER_H_ */

View File

@ -0,0 +1,440 @@
/*
* \brief VMM ARM Generic Interrupt Controller device model
* \author Stefan Kalkowski
* \date 2019-08-05
*/
/*
* 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__GIC_H_
#define _SRC__SERVER__VMM__GIC_H_
#include <mmio.h>
#include <base/env.h>
#include <drivers/defs/arm_v7.h>
#include <util/list.h>
#include <util/register.h>
#include <util/reconstructible.h>
namespace Vmm { class Gic; }
class Vmm::Gic : public Vmm::Mmio_device
{
public:
enum Irqs {
MAX_SGI = 16,
MAX_PPI = 16,
MAX_SPI = 992,
MAX_IRQ = 1020,
SPURIOUS = 1023,
};
class Irq : public Genode::List<Irq>::Element
{
public:
struct List : Genode::List<Irq>
{
void insert(Irq & irq);
Irq * highest_enabled();
};
struct Irq_handler {
virtual void eoi() {};
virtual void enabled() {};
virtual void disabled() {};
};
enum Type { SGI, PPI, SPI };
enum State { INACTIVE, ACTIVE, PENDING, ACTIVE_PENDING };
enum Config { LEVEL, EDGE };
Irq(unsigned num, Type t, List &pending_list);
bool enabled() const;
bool active() const;
bool pending() const;
bool level() const;
unsigned number() const;
Genode::uint8_t priority() const;
Genode::uint8_t target() const;
void enable();
void disable();
void activate();
void deactivate();
void assert();
void deassert();
void level(bool l);
void priority(Genode::uint8_t p);
void target(Genode::uint8_t t);
void handler(Irq_handler & handler);
private:
Irq(Irq const &);
Irq &operator = (Irq const &);
bool _enabled { false };
Type _type { SPI };
State _state { INACTIVE };
Config _config { LEVEL };
unsigned _num { 0 };
Genode::uint8_t _prio { 0 };
Genode::uint8_t _target { 1 };
List & _pending_list;
Irq_handler * _handler { nullptr };
};
struct Irq_reg: Mmio_register
{
unsigned const irq_count;
virtual Register read(Irq & irq) { return 0; }
virtual void write(Irq & irq, Register reg) { }
Register read(Address_range & access, Cpu&) override;
void write(Address_range & access, Cpu&, Register value) override;
Irq_reg(Mmio_register::Name name,
Mmio_register::Type type,
Genode::uint64_t start,
unsigned bits_per_irq,
unsigned irq_count)
: Mmio_register(name, type, start, bits_per_irq*irq_count/8),
irq_count(irq_count) {}
};
class Gicd_banked
{
public:
Irq & irq(unsigned num);
void handle_irq();
bool pending_irq();
Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus);
private:
Cpu & _cpu;
Gic & _gic;
Genode::Constructible<Irq> _sgi[MAX_SGI];
Genode::Constructible<Irq> _ppi[MAX_PPI];
Irq::List _pending_list;
struct Redistributor : Mmio_device
{
unsigned cpu_id;
bool last;
Mmio_register gicr_ctlr { "GICR_CTLR", Mmio_register::RO,
0x0, 4, 0b10010 };
Mmio_register gicr_typer { "GICR_TYPER", Mmio_register::RO,
0x8, 8, (Genode::uint64_t)cpu_id<<32 | cpu_id<<8 | (last ? 1<<4 : 0) };
Mmio_register gicr_waker { "GICR_WAKER", Mmio_register::RO,
0x14, 4, 0 };
Mmio_register gicr_pidr2 { "GICR_PIDR2", Mmio_register::RO,
0xffe8, 4, (3<<4) };
Mmio_register gicr_igroupr0 { "GICR_IGROUPR0", Mmio_register::RO,
0x10080, 4, 0 };
struct Gicr_isenabler0 : Irq_reg
{
Register read(Irq & irq) { return irq.enabled(); }
void write(Irq & irq, Register v) { if (v) irq.enable(); }
Gicr_isenabler0()
: Irq_reg("GICR_ISENABLER0", Mmio_register::RW, 0x10100, 1, 32) {}
} gicr_isenabler0;
struct Gicr_icenabler0 : Irq_reg
{
Register read(Irq & irq) { return irq.enabled(); }
void write(Irq & irq, Register v) { if (v) irq.disable(); }
Gicr_icenabler0()
: Irq_reg("GICR_ICENABLER0", Mmio_register::RW, 0x10180, 1, 32) {}
} gicr_icenabler0;
struct Gicr_ispendr0 : Irq_reg
{
Register read(Irq & irq) { return irq.pending(); }
void write(Irq & irq, Register v) { if (v) irq.assert(); }
Gicr_ispendr0()
: Irq_reg("GICR_ISPENDR0", Mmio_register::RW, 0x10200, 1, 32) {}
} gicr_ispendr0;
struct Gicr_icpendr0 : Irq_reg
{
Register read(Irq & irq) { return irq.pending(); }
void write(Irq & irq, Register v) { if (v) irq.deassert(); }
Gicr_icpendr0()
: Irq_reg("GICR_ICPENDR0", Mmio_register::RW, 0x10280, 1, 32) {}
} gicr_icpendr0;
struct Gicr_isactiver0 : Irq_reg
{
Register read(Irq & irq) { return irq.active(); }
void write(Irq & irq, Register v) { if (v) irq.activate(); }
Gicr_isactiver0()
: Irq_reg("GICR_ISACTIVER0", Mmio_register::RW, 0x10300, 1, 32) {}
} gicr_isactiver0;
struct Gicr_icactiver0 : Irq_reg
{
Register read(Irq & irq) { return irq.active(); }
void write(Irq & irq, Register v) { if (v) irq.deactivate(); }
Gicr_icactiver0()
: Irq_reg("GICR_ICACTIVER0", Mmio_register::RW, 0x10380, 1, 32) {}
} gicr_icactiver0;
struct Gicr_ipriorityr : Irq_reg
{
Register read(Irq & irq) { return irq.priority(); }
void write(Irq & irq, Register v) { irq.priority(v); }
Gicr_ipriorityr()
: Irq_reg("GICR_IPRIORITYR", Mmio_register::RW, 0x10400, 8, 32) {}
} gicr_ipriorityr;
struct Gicr_icfgr : Irq_reg
{
Register read(Irq & irq) { return irq.level() ? 0 : 1; }
void write(Irq & irq, Register v) { irq.level(!v); }
Gicr_icfgr()
: Irq_reg("GICR_ICFGR", Mmio_register::RW, 0x10c00, 8, 32) {}
} gicr_icfgr;
Redistributor(const Genode::uint64_t addr,
const Genode::uint64_t size,
unsigned cpu_id,
bool last)
: Mmio_device("GICR", addr, size), cpu_id(cpu_id), last(last)
{
add(gicr_ctlr);
add(gicr_typer);
add(gicr_waker);
add(gicr_pidr2);
add(gicr_igroupr0);
add(gicr_isenabler0);
add(gicr_icenabler0);
add(gicr_ispendr0);
add(gicr_icpendr0);
add(gicr_isactiver0);
add(gicr_icactiver0);
add(gicr_ipriorityr);
add(gicr_icfgr);
}
};
Genode::Constructible<Redistributor> _rdist;
};
Gic(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
Mmio_bus & bus,
Genode::Env & env);
private:
friend struct Gicd_banked;
Genode::Constructible<Irq> _spi[MAX_SPI];
Irq::List _pending_list;
unsigned _cpu_cnt { 2 }; /* FIXME: smp support */
unsigned _version { 3 }; /* FIXME: version support */
struct Gicd_ctlr : Genode::Register<32>, Mmio_register
{
struct Enable : Bitfield<0, 1> {};
struct Disable : Bitfield<6, 1> {};
void write(Address_range & access, Cpu & cpu,
Mmio_register::Register value) override
{
access_t v = value;
Disable::set(v, 0);
Mmio_register::write(access, cpu, v);
}
Gicd_ctlr()
: Mmio_register("GICD_CTLR", Mmio_register::RW, 0, 4, 0) {}
} _ctrl;
struct Gicd_typer : Genode::Register<32>, Mmio_register
{
struct It_lines_number : Bitfield<0, 5> {};
struct Cpu_number : Bitfield<5, 3> {};
struct Id_bits : Bitfield<19, 5> {};
Gicd_typer(unsigned cpus)
: Mmio_register("GICD_TYPER", Mmio_register::RO, 0x4, 4,
It_lines_number::bits(31) | Cpu_number::bits(cpus-1) | Id_bits::bits(9)) {}
} _typer { _cpu_cnt };
struct Gicd_iidr : Genode::Register<32>, Mmio_register
{
struct Implementer : Bitfield<0, 12> {};
struct Revision : Bitfield<12, 4> {};
struct Variant : Bitfield<16, 4> {};
struct Product_id : Bitfield<24, 8> {};
Gicd_iidr()
: Mmio_register("GICD_IIDR", Mmio_register::RO, 0x8, 4, 0x123) {}
} _iidr;
struct Gicd_igroupr : Irq_reg
{
Gicd_igroupr()
: Irq_reg("GICD_IGROUPR", Mmio_register::RW, 0x80, 1, 1024) {}
} _igroupr;
struct Gicd_isenabler : Irq_reg
{
Register read(Irq & irq) { return irq.enabled(); }
void write(Irq & irq, Register v) { if (v) irq.enable(); }
Gicd_isenabler()
: Irq_reg("GICD_ISENABLER", Mmio_register::RW, 0x100, 1, 1024) {}
} _isenabler;
struct Gicd_icenabler : Irq_reg
{
Register read(Irq & irq) { return irq.enabled(); }
void write(Irq & irq, Register v) { if (v) irq.disable(); }
Gicd_icenabler()
: Irq_reg("GICD_ICENABLER", Mmio_register::RW, 0x180, 1, 1024) {}
} _csenabler;
struct Gicd_ispendr : Irq_reg
{
Register read(Irq & irq) { return irq.pending(); }
void write(Irq & irq, Register v) { if (v) irq.assert(); }
Gicd_ispendr()
: Irq_reg("GICD_ISPENDR", Mmio_register::RW, 0x200, 1, 1024) {}
} _ispendr;
struct Gicd_icpendr : Irq_reg
{
Register read(Irq & irq) { return irq.pending(); }
void write(Irq & irq, Register v) { if (v) irq.deassert(); }
Gicd_icpendr()
: Irq_reg("GICD_ICPENDR", Mmio_register::RW, 0x280, 1, 1024) {}
} _icpendr;
struct Gicd_isactiver : Irq_reg
{
Register read(Irq & irq) { return irq.active(); }
void write(Irq & irq, Register v) { if (v) irq.activate(); }
Gicd_isactiver()
: Irq_reg("GICD_ISACTIVER", Mmio_register::RW, 0x300, 1, 1024) {}
} _isactiver;
struct Gicd_icactiver : Irq_reg
{
Register read(Irq & irq) { return irq.active(); }
void write(Irq & irq, Register v) { if (v) irq.deactivate(); }
Gicd_icactiver()
: Irq_reg("GICD_ICACTIVER", Mmio_register::RW, 0x380, 1, 1024) {}
} _icactiver;
struct Gicd_ipriorityr : Irq_reg
{
Register read(Irq & irq) { return irq.priority(); }
void write(Irq & irq, Register v) { irq.priority(v); }
Gicd_ipriorityr()
: Irq_reg("GICD_IPRIORITYR", Mmio_register::RW, 0x400, 8, 1024) {}
} _ipriorityr;
struct Gicd_itargetr : Irq_reg
{
Register read(Irq & irq) { return irq.target(); }
void write(Irq & irq, Register v) { irq.target(v); }
Gicd_itargetr()
: Irq_reg("GICD_ITARGETSR", Mmio_register::RW, 0x800, 8, 1024) {}
} _itargetr;
struct Gicd_icfgr : Irq_reg
{
Register read(Irq & irq) { return irq.level() ? 0 : 1; }
void write(Irq & irq, Register v) { irq.level(!v); }
Gicd_icfgr()
: Irq_reg("GICD_ICFGR", Mmio_register::RW, 0xc00, 8, 1024) {}
} _icfgr;
struct Gicd_sgir : Genode::Register<32>, Mmio_register
{
struct Enable : Bitfield<0, 1> {};
struct Disable : Bitfield<6, 1> {};
void write(Address_range & access, Cpu & cpu,
Mmio_register::Register value) override
{
Genode::error("SGIR WRITE ", value);
}
Gicd_sgir()
: Mmio_register("GICD_SGIR", Mmio_register::WO, 0xf00, 4, 0) {}
} _sgir;
struct Gicd_irouter : Irq_reg
{
Register read(Irq &) { return 0x0; } // FIXME smp
void write(Irq &, Register) { }
Gicd_irouter()
: Irq_reg("GICD_IROUTER", Mmio_register::RW, 0x6100, 64, 1024) {}
} _irouter;
/**
* FIXME: missing registers:
* GICD_IGROUP = 0x80,
* GICD_NSACR = 0xe00,
* GICD_SGIR = 0xf00,
* GICD_CPENDSGIR0 = 0xf10,
* GICD_SPENDSGIR0 = 0xf20,
* GICD identification registers 0xfd0...
*/
/**
* Dummy container for holding array of noncopyable objects
* Workaround for gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70395
*/
struct Dummy {
Mmio_register regs[8];
} _reg_container { .regs = {
{ "GICD_PIDR4", Mmio_register::RO, 0xffd0, 4, 0x0 },
{ "GICD_PIDR5", Mmio_register::RO, 0xffd4, 4, 0x0 },
{ "GICD_PIDR6", Mmio_register::RO, 0xffd8, 4, 0x0 },
{ "GICD_PIDR7", Mmio_register::RO, 0xffdc, 4, 0x0 },
{ "GICD_PIDR0", Mmio_register::RO, 0xffe0, 4, 0x492 },
{ "GICD_PIDR1", Mmio_register::RO, 0xffe4, 4, 0xb0 },
{ "GICD_PIDR2", Mmio_register::RO, 0xffe8, 4, (_version<<4) | 0xb },
{ "GICD_PIDR3", Mmio_register::RO, 0xffec, 4, 0x44 }
}};
};
#endif /* _SRC__SERVER__VMM__GIC_H_ */

View File

@ -0,0 +1,251 @@
/*
* \brief VMM ARM Generic Interrupt Controller v2 device model
* \author Stefan Kalkowski
* \date 2019-08-05
*/
/*
* 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.
*/
#include <cpu.h>
#include <gic.h>
#include <vm.h>
using Vmm::Gic;
using Register = Vmm::Mmio_register::Register;
bool Gic::Irq::enabled() const { return _enabled; }
bool Gic::Irq::active() const {
return _state == ACTIVE || _state == ACTIVE_PENDING; }
bool Gic::Irq::pending() const {
return _state == PENDING || _state == ACTIVE_PENDING; }
bool Gic::Irq::level() const { return _config == LEVEL; }
unsigned Gic::Irq::number() const { return _num; }
Genode::uint8_t Gic::Irq::priority() const { return _prio; }
Genode::uint8_t Gic::Irq::target() const { return _target; }
void Gic::Irq::enable()
{
_enabled = true;
if (_handler) _handler->enabled();
}
void Gic::Irq::disable()
{
_enabled = false;
if (_handler) _handler->disabled();
}
void Gic::Irq::activate()
{
switch (_state) {
case INACTIVE: return;
case PENDING: return;
case ACTIVE_PENDING: _state = PENDING; return;
case ACTIVE: _state = INACTIVE; return;
};
}
void Gic::Irq::deactivate()
{
switch (_state) {
case INACTIVE: return;
case PENDING: return;
case ACTIVE_PENDING: _state = PENDING; return;
case ACTIVE: _state = INACTIVE; return;
};
}
void Gic::Irq::assert()
{
if (pending()) return;
_state = PENDING;
_pending_list.insert(*this);
}
void Gic::Irq::deassert()
{
if (_state == INACTIVE) return;
_state = INACTIVE;
_pending_list.remove(this);
if (_handler) _handler->eoi();
}
void Gic::Irq::target(Genode::uint8_t t)
{
if (_target == t) return;
_target = t;
}
void Gic::Irq::level(bool l)
{
if (level() == l) return;
_config = l ? LEVEL : EDGE;
}
void Gic::Irq::priority(Genode::uint8_t p)
{
if (_prio == p) return;
_prio = p;
}
void Gic::Irq::handler(Gic::Irq::Irq_handler & handler) { _handler = &handler; }
Gic::Irq::Irq(unsigned num, Type t, Irq::List & l)
: _type(t), _num(num), _pending_list(l) {}
void Gic::Irq::List::insert(Irq & irq)
{
Irq * i = first();
while (i && i->priority() < irq.priority() && i->next()) i = i->next();
Genode::List<Irq>::insert(&irq, i);
}
Gic::Irq * Gic::Irq::List::highest_enabled()
{
Irq * i = first();
while(i) {
if (i->enabled()) return i;
i = i->next();
}
return nullptr;
}
Gic::Irq & Gic::Gicd_banked::irq(unsigned i)
{
if (i < MAX_SGI)
return *_sgi[i];
if (i < MAX_SGI+MAX_PPI)
return *_ppi[i-MAX_SGI];
return *_gic._spi[i-MAX_SGI-MAX_PPI];
}
void Gic::Gicd_banked::handle_irq()
{
unsigned i = _cpu.state().irqs.virtual_irq;
if (i > MAX_IRQ) return;
irq(i).deassert();
_cpu.state().irqs.virtual_irq = 1023;
}
bool Gic::Gicd_banked::pending_irq()
{
if (_cpu.state().irqs.virtual_irq != 1023) return true;
Irq * i = _gic._pending_list.highest_enabled();
Irq * j = _pending_list.highest_enabled();
Irq * n = j;
if (i && ((j && j->priority() > i->priority()) || !j)) n = i;
if (!n) return false;
_cpu.state().irqs.virtual_irq = n->number();
n->activate();
return true;
}
Gic::Gicd_banked::Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus)
: _cpu(cpu), _gic(gic)
{
for (unsigned i = 0; i < MAX_SGI; i++)
_sgi[i].construct(i, Irq::SGI, _pending_list);
for (unsigned i = 0; i < MAX_PPI; i++)
_ppi[i].construct(i+MAX_SGI, Irq::PPI, _pending_list);
_cpu.state().irqs.last_irq = 1023;
_cpu.state().irqs.virtual_irq = 1023;
_rdist.construct(0x80a0000 + (cpu.cpu_id()*0x20000), 0x20000, cpu.cpu_id(), Vm::last_cpu() == cpu.cpu_id());
bus.add(*_rdist);
}
Register Gic::Irq_reg::read(Address_range & access, Cpu & cpu)
{
Register ret = 0;
Register bits_per_irq = size * 8 / irq_count;
for (unsigned i = (access.start * 8) / bits_per_irq;
i < ((access.start+access.size) * 8) / bits_per_irq; i++)
ret |= read(cpu.gic().irq(i)) << (i % 32/bits_per_irq);
return ret;
}
void Gic::Irq_reg::write(Address_range & access, Cpu & cpu, Register value)
{
Register bits_per_irq = size * 8 / irq_count;
Register irq_value_mask = (1<<bits_per_irq) - 1;
for (unsigned i = (access.start * 8) / bits_per_irq;
i < ((access.start+access.size) * 8) / bits_per_irq; i++)
write(cpu.gic().irq(i), (value >> (i % 32/bits_per_irq))
& irq_value_mask);
}
Gic::Gic(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
Mmio_bus & bus,
Genode::Env & env)
: Mmio_device(name, addr, size)
{
add(_ctrl);
add(_typer);
add(_iidr);
add(_igroupr);
add(_isenabler);
add(_csenabler);
add(_ispendr);
add(_icpendr);
add(_isactiver);
add(_icactiver);
add(_ipriorityr);
add(_itargetr);
add(_icfgr);
add(_irouter);
add(_sgir);
for (unsigned i = 0; i < (sizeof(Dummy::regs) / sizeof(Mmio_register)); i++)
add(_reg_container.regs[i]);
for (unsigned i = 0; i < MAX_SPI; i++)
_spi[i].construct(i+MAX_SGI+MAX_PPI, Irq::SPI, _pending_list);
bus.add(*this);
}

View File

@ -0,0 +1,125 @@
/*
* \brief VMM dedicated hardware device passed-through to VM
* \author Stefan Kalkowski
* \date 2019-09-25
*/
/*
* 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__HW_DEVICE_H_
#define _SRC__SERVER__VMM__HW_DEVICE_H_
#include <exception.h>
#include <mmio.h>
#include <gic.h>
#include <base/attached_io_mem_dataspace.h>
#include <irq_session/connection.h>
namespace Vmm {
template <unsigned, unsigned> class Hw_device;
}
template <unsigned MMIO_COUNT, unsigned IRQ_COUNT>
class Vmm::Hw_device
{
private:
class Irq : Gic::Irq::Irq_handler
{
private:
using Session = Genode::Constructible<Genode::Irq_connection>;
Gic::Irq & _irq;
Genode::Env & _env;
Session _session;
Cpu::Signal_handler<Irq> _handler;
void _assert()
{
_irq.assert();
}
public:
void enabled() override
{
if (!_session.constructed()) {
_session.construct(_env, _irq.number(),
_irq.level() ?
Genode::Irq_session::TRIGGER_LEVEL :
Genode::Irq_session::TRIGGER_EDGE);
_session->sigh(_handler);
_session->ack_irq();
}
}
void disabled() override
{
if (_session.constructed())
_session.destruct();
}
void eoi() override
{
if (_session.constructed()) _session->ack_irq();
}
Irq(Gic::Irq & irq, Cpu & cpu, Genode::Env & env)
: _irq(irq),
_env(env),
_handler(cpu, env.ep(), *this, &Irq::_assert)
{
_irq.handler(*this);
}
};
Genode::Env & _env;
Genode::Vm_connection & _vm;
Cpu & _cpu;
Genode::Constructible<Genode::Attached_io_mem_dataspace> _ds[MMIO_COUNT];
Genode::Constructible<Irq> _irqs[IRQ_COUNT];
void mmio() { }
void irqs() { }
public:
Hw_device(Genode::Env &env,
Genode::Vm_connection &vm,
Cpu & cpu)
: _env(env), _vm(vm), _cpu(cpu) { };
template <typename... ARGS>
void mmio(Genode::addr_t start, Genode::size_t size, ARGS &&... args)
{
mmio(args...);
for (unsigned i = 0; i < MMIO_COUNT; i++) {
if (_ds[i].constructed()) continue;
_ds[i].construct(_env, start, size);
_vm.attach(_ds[i]->cap(), start);
return;
}
}
template <typename... ARGS>
void irqs(unsigned irq, ARGS &&... args)
{
irqs(args...);
for (unsigned i = 0; i < IRQ_COUNT; i++) {
if (_irqs[i].constructed()) continue;
_irqs[i].construct(_cpu.gic().irq(irq), _cpu, _env);
return;
}
}
};
#endif /* _SRC__SERVER__VMM__HW_DEVICE_H_ */

View File

@ -0,0 +1,17 @@
/*
* \brief VMM example for ARMv8 virtualization
* \author Stefan Kalkowski
* \date 2014-07-08
*/
/*
* Copyright (C) 2014-2017 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 <vm.h>
#include <base/component.h>
void Component::construct(Genode::Env & env) { static Vmm::Vm vm(env); }

View File

@ -0,0 +1,126 @@
/*
* \brief VMM mmio abstractions
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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.
*/
#include <cpu.h>
#include <mmio.h>
using namespace Vmm;
Mmio_register::Register Mmio_register::read(Address_range & access, Cpu & cpu)
{
if (_type == WO)
throw Exception("Invalid read access to register ",
_name, " ", access);
using namespace Genode;
switch (access.size) {
case 1: return *(uint8_t*) ((addr_t)&_value + access.start);
case 2: return *(uint16_t*) ((addr_t)&_value + access.start);
case 4: return *(uint32_t*) ((addr_t)&_value + access.start);
case 8: return _value;
default: return 0;
}
}
void Mmio_register::write(Address_range & access, Cpu & cpu, Register value)
{
if (_type == RO)
throw Exception("Invalid write access to register ",
_name, " ", access);
using namespace Genode;
switch (access.size) {
case 1: *((uint8_t*) ((addr_t)&_value + access.start)) = value;
case 2: *((uint16_t*) ((addr_t)&_value + access.start)) = value;
case 4: *((uint32_t*) ((addr_t)&_value + access.start)) = value;
case 8: _value = value;
}
}
Mmio_register::Register Mmio_register::value() { return _value; }
void Mmio_register::set(Register value) { _value = value; }
Mmio_device::Register Mmio_device::read(Address_range & access, Cpu & cpu)
{
Mmio_register & reg = _registers.get<Mmio_register>(access);
Address_range ar(access.start - reg.start, access.size);
return reg.read(ar, cpu);
}
void Mmio_device::write(Address_range & access, Cpu & cpu, Register value)
{
Mmio_register & reg = _registers.get<Mmio_register>(access);
Address_range ar(access.start - reg.start, access.size);
reg.write(ar, cpu, value);
}
void Mmio_device::add(Mmio_register & reg) { _registers.add(reg); }
void Vmm::Mmio_bus::handle_memory_access(Vmm::Cpu & cpu)
{
using namespace Genode;
Cpu::State & state = cpu.state();
struct Iss : Cpu::Esr
{
struct Write : Bitfield<6, 1> {};
struct Register : Bitfield<16, 5> {};
struct Sign_extend : Bitfield<21, 1> {};
struct Access_size : Bitfield<22, 2> {
enum { BYTE, HALFWORD, WORD, DOUBLEWORD }; };
struct Valid : Bitfield<24, 1> {};
static bool valid(access_t v) {
return Valid::get(v) && !Sign_extend::get(v); }
static bool write(access_t v) { return Write::get(v); }
static unsigned r(access_t v) { return Register::get(v); }
};
if (!Iss::valid(state.esr_el2))
throw Exception("Mmio_bus: unknown ESR=", Genode::Hex(state.esr_el2));
bool wr = Iss::Write::get(state.esr_el2);
unsigned idx = Iss::Register::get(state.esr_el2);
uint64_t ipa = ((uint64_t)state.hpfar_el2 << 8)
+ (state.far_el2 & ((1 << 12) - 1));
uint64_t width = 1 << Iss::Access_size::get(state.esr_el2);
try {
Address_range bus_range(ipa, width);
Mmio_device & dev = get<Mmio_device>(bus_range);
Address_range dev_range(ipa - dev.start,width);
if (wr) {
dev.write(dev_range, cpu, (idx == 31) ? 0 : state.r[idx]);
} else {
if (idx > 30)
throw Exception("Wrong register index when reading ", bus_range);
state.r[idx] = dev.read(dev_range, cpu);
}
} catch(Exception & e) {
Genode::warning(e);
Genode::warning("Will ignore invalid bus access (IPA=",
Genode::Hex(ipa), ")");
}
}

View File

@ -0,0 +1,90 @@
/*
* \brief VMM mmio abstractions
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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__MMIO_H_
#define _SRC__SERVER__VMM__MMIO_H_
#include <address_space.h>
namespace Vmm {
class Cpu;
class Mmio_register;
class Mmio_device;
class Mmio_bus;
}
class Vmm::Mmio_register : public Vmm::Address_range
{
public:
enum Type { RO, WO, RW };
using Name = Genode::String<64>;
using Register = Genode::uint64_t;
virtual Register read(Address_range & access, Cpu&);
virtual void write(Address_range & access, Cpu&, Register value);
void set(Register value);
Register value();
Mmio_register(Name name,
Type type,
Genode::uint64_t start,
Genode::uint64_t size,
Register reset_value = 0)
: Address_range(start, size),
_name(name),
_type(type),
_value(reset_value) { }
protected:
Name const _name;
Type const _type;
Register _value;
bool _unaligned(Address_range & access);
};
class Vmm::Mmio_device : public Vmm::Address_range
{
public:
using Name = Genode::String<64>;
using Register = Genode::uint64_t;
virtual Register read(Address_range & access, Cpu&);
virtual void write(Address_range & access, Cpu&, Register value);
void add(Mmio_register & reg);
Mmio_device(Name name,
Genode::uint64_t start,
Genode::uint64_t size)
: Address_range(start, size), _name(name) { }
private:
Name const _name;
Address_space _registers;
};
struct Vmm::Mmio_bus : Vmm::Address_space
{
void handle_memory_access(Cpu & cpu);
};
#endif /* _SRC__SERVER__VMM__MMIO_H_ */

View File

@ -0,0 +1,102 @@
/*
* \brief VMM PL011 serial device model
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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.
*/
#include <cpu.h>
#include <pl011.h>
using Vmm::Pl011;
using Register = Vmm::Mmio_register::Register;
Register Pl011::Uartdr::read(Address_range&, Cpu&)
{
ris.set(ris.value() & ~RX_MASK);
if (!rx.empty()) return rx.get();
return 0;
}
void Pl011::Uartdr::write(Address_range&, Cpu&, Register reg)
{
terminal.write(&reg, 1);
}
Register Pl011::Uartfr::read(Address_range&, Cpu&)
{
return rx.empty() ? Rx_empty::bits(1) : Rx_full::bits(1);
}
void Pl011::Uartimsc::write(Address_range&, Cpu&, Register mask)
{
if ((mask & RX_MASK) && !(value() & RX_MASK) && (ris.value() & RX_MASK))
irq.assert();
set(mask);
}
Register Pl011::Uartmis::read(Address_range&, Cpu&)
{
return ris.value() & imsc.value();
}
void Pl011::Uarticr::write(Address_range & ar, Cpu &, Register value)
{
ris.set(ris.value() & ~value);
}
void Pl011::_read()
{
if (!_terminal.avail()) return;
while (_terminal.avail()) {
unsigned char c = 0;
_terminal.read(&c, 1);
_rx_buf.add(c);
}
_uart_ris.set(_uart_ris.value() | RX_MASK);
if (_uart_imsc.value() & RX_MASK) _irq.assert();
}
Pl011::Pl011(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
unsigned irq,
Cpu & cpu,
Mmio_bus & bus,
Genode::Env & env)
: Mmio_device(name, addr, size),
_terminal(env),
_handler(cpu, env.ep(), *this, &Pl011::_read),
_irq(cpu.gic().irq(irq))
{
for (unsigned i = 0; i < (sizeof(Dummy::regs) / sizeof(Mmio_register)); i++)
add(_reg_container.regs[i]);
add(_uart_ris);
add(_uart_dr);
add(_uart_fr);
add(_uart_imsc);
add(_uart_mis);
add(_uart_icr);
_terminal.read_avail_sigh(_handler);
bus.add(*this);
}

View File

@ -0,0 +1,145 @@
/*
* \brief VMM PL011 serial device model
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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__PL011_H_
#define _SRC__SERVER__VMM__PL011_H_
#include <os/ring_buffer.h>
#include <terminal_session/connection.h>
#include <gic.h>
namespace Vmm {
class Cpu;
class Pl011;
}
class Vmm::Pl011 : public Vmm::Mmio_device
{
private:
using Ring_buffer =
Genode::Ring_buffer<char, 1024,
Genode::Ring_buffer_unsynchronized>;
enum Mask : Genode::uint16_t {
RX_MASK = 1 << 4,
TX_MASK = 1 << 5
};
Terminal::Connection _terminal;
Cpu::Signal_handler<Pl011> _handler;
Gic::Irq & _irq;
Ring_buffer _rx_buf;
Mmio_register _uart_ris { "UARTRIS", Mmio_register::RO,
0x3c, 2 };
/**
* Dummy container for holding array of noncopyable objects
* Workaround for gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70395
*/
struct Dummy {
Mmio_register regs[13];
} _reg_container { .regs = {
{ "UARTIBRD", Mmio_register::RW, 0x24, 2, 0 },
{ "UARTFBRD", Mmio_register::RW, 0x28, 2, 0 },
{ "UARTLCR_H", Mmio_register::RW, 0x2c, 2, 0 },
{ "UARTCR", Mmio_register::RW, 0x30, 2, 0x300 },
{ "UARTIFLS", Mmio_register::RW, 0x34, 2, 0x12 },
{ "UARTPERIPHID0", Mmio_register::RO, 0xfe0, 4, 0x11 },
{ "UARTPERIPHID1", Mmio_register::RO, 0xfe4, 4, 0x10 },
{ "UARTPERIPHID2", Mmio_register::RO, 0xfe8, 4, 0x14 },
{ "UARTPERIPHID3", Mmio_register::RO, 0xfec, 4, 0x0 },
{ "UARTPCELLID0", Mmio_register::RO, 0xff0, 4, 0xd },
{ "UARTPCELLID1", Mmio_register::RO, 0xff4, 4, 0xf0 },
{ "UARTPCELLID2", Mmio_register::RO, 0xff8, 4, 0x5 },
{ "UARTPCELLID3", Mmio_register::RO, 0xffc, 4, 0xb1 }
}};
struct Uartdr : Mmio_register
{
Terminal::Connection & terminal;
Ring_buffer & rx;
Mmio_register & ris;
Register read(Address_range&, Cpu&) override;
void write(Address_range&, Cpu&, Register) override;
Uartdr(Terminal::Connection & terminal,
Ring_buffer & rx,
Mmio_register & ris)
: Mmio_register("UARTDR", Mmio_register::RW, 0x0, 2),
terminal(terminal), rx(rx), ris(ris) {}
} _uart_dr { _terminal, _rx_buf, _uart_ris };
struct Uartfr : Mmio_register, Genode::Register<32>
{
struct Rx_empty : Bitfield<4, 1> {};
struct Rx_full : Bitfield<6, 1> {};
Ring_buffer & rx;
Mmio_register::Register read(Address_range&, Cpu&) override;
Uartfr(Ring_buffer & rx)
: Mmio_register("UARTFR", Mmio_register::RO, 0x18, 4), rx(rx) {}
} _uart_fr { _rx_buf };
struct Uartimsc : Mmio_register
{
Gic::Irq & irq;
Mmio_register & ris;
void write(Address_range&, Cpu&, Register) override;
Uartimsc(Gic::Irq & irq, Mmio_register & ris)
: Mmio_register("UARTIMSC", Mmio_register::RW, 0x38, 2, 0xf),
irq(irq), ris(ris) {}
} _uart_imsc { _irq, _uart_ris };
struct Uartmis : Mmio_register
{
Mmio_register & ris;
Uartimsc & imsc;
Register read(Address_range&, Cpu&) override;
Uartmis(Mmio_register & ris, Uartimsc & imsc)
: Mmio_register("UARTMIS", Mmio_register::RO, 0x40, 2),
ris(ris), imsc(imsc) {}
} _uart_mis { _uart_ris, _uart_imsc };
struct Uarticr : Mmio_register
{
Mmio_register & ris;
void write(Address_range&, Cpu&, Register) override;
Uarticr(Mmio_register & ris)
: Mmio_register("UARTICR", Mmio_register::WO, 0x44, 2), ris(ris) {}
} _uart_icr { _uart_ris };
void _read();
public:
Pl011(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
unsigned irq,
Cpu & cpu,
Mmio_bus & bus,
Genode::Env & env);
};
#endif /* _SRC__SERVER__VMM__PL011_H_ */

View File

@ -0,0 +1,35 @@
/*
* \brief Power State Coordination Interface for ARMv8 virtualization
* \author Stefan Kalkowski
* \date 2019-10-21
*/
/*
* 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__PSCI_H_
#define _SRC__SERVER__VMM__PSCI_H_
namespace Vmm {
namespace Psci {
enum Function_id {
PSCI_VERSION = 0x84000000,
MIGRATE_INFO_TYPE = 0x84000006,
PSCI_FEATURES = 0x8400000a,
CPU_ON = 0xc4000003,
};
enum {
VERSION = 1 << 16, /* 1.0 */
SUCCESS = 0,
NOT_SUPPORTED = -1,
};
};
};
#endif /* _SRC__SERVER__VMM__PSCI_H_ */

View File

@ -0,0 +1,39 @@
/*
* \brief VMM ram object
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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__RAM_H_
#define _SRC__SERVER__VMM__RAM_H_
#include <base/stdint.h>
class Ram {
private:
Genode::addr_t const _base;
Genode::size_t const _size;
Genode::addr_t const _local;
public:
Ram(Genode::addr_t const addr,
Genode::size_t const sz,
Genode::addr_t const local)
: _base(addr), _size(sz), _local(local) { }
Genode::addr_t base() const { return _base; }
Genode::size_t size() const { return _size; }
Genode::addr_t local() const { return _local; }
};
#endif /* _SRC__SERVER__VMM__RAM_H_ */

View File

@ -0,0 +1,14 @@
TARGET = vmm
REQUIRES = hw arm_v8
LIBS = base
SRC_CC += address_space.cc
SRC_CC += cpu.cc
SRC_CC += generic_timer.cc
SRC_CC += gicv2.cc
SRC_CC += main.cc
SRC_CC += mmio.cc
SRC_CC += pl011.cc
SRC_CC += vm.cc
INC_DIR += $(PRG_DIR)
CC_CXX_WARN_STRICT :=

View File

@ -0,0 +1,65 @@
/dts-v1/;
/ {
compatible = "linux,dummy-virt";
#address-cells = <0x02>;
#size-cells = <0x02>;
interrupt-parent = <0x8001>;
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
cpu@0 {
compatible = "arm,cortex-a53";
reg = <0x00>;
device_type = "cpu";
};
};
timer {
interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>;
compatible = "arm,armv8-timer\0arm,armv7-timer";
always-on;
};
apb-pclk {
compatible = "fixed-clock";
phandle = <0x8000>;
clock-output-names = "clk24mhz";
clock-frequency = <0x16e3600>;
#clock-cells = <0x00>;
};
pl011@9000000 {
interrupts = <0x00 0x01 0x04>;
compatible = "arm,pl011\0arm,primecell";
clock-names = "uartclk\0apb_pclk";
reg = <0x00 0x9000000 0x00 0x1000>;
clocks = <0x8000 0x8000>;
};
memory@40000000 {
reg = <0x00 0x40000000 0x00 0x8000000>;
device_type = "memory";
};
chosen {
bootargs = "rdinit=/bin/sh";
linux,initrd-start = <0x42000000>;
linux,initrd-end = <0x42113b86>;
stdout-path = "/pl011@9000000";
};
intc@8000000 {
compatible = "arm,gic-v3";
phandle = <0x8001>;
reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>;
ranges;
#address-cells = <0x02>;
#redistributor-regions = <0x01>;
interrupt-controller;
#interrupt-cells = <0x03>;
#size-cells = <0x02>;
};
};

View File

@ -0,0 +1,75 @@
/*
* \brief VMM example for ARMv8 virtualization
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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.
*/
#include <vm.h>
using Vmm::Vm;
void Vm::_load_kernel()
{
Genode::memcpy((void*)(_ram.local() + KERNEL_OFFSET),
_kernel_rom.local_addr<void>(),
_kernel_rom.size());
}
void Vm::_load_dtb()
{
Genode::memcpy((void*)(_ram.local() + DTB_OFFSET),
_dtb_rom.local_addr<void>(),
_dtb_rom.size());
}
void Vm::_load_initrd()
{
Genode::memcpy((void*)(_ram.local() + INITRD_OFFSET),
_initrd_rom.local_addr<void>(),
_initrd_rom.size());
}
Vmm::Cpu & Vm::boot_cpu()
{
if (!_cpus[0].constructed())
_cpus[0].construct(*this, _vm, _bus, _gic, _env, _heap, _env.ep());
return *_cpus[0];
}
Vm::Vm(Genode::Env & env)
: _env(env),
_gic("Gicv3", 0x8000000, 0x10000, _bus, env),
_uart("Pl011", 0x9000000, 0x1000, 33, boot_cpu(), _bus, env)
{
_vm.attach(_vm_ram.cap(), RAM_ADDRESS);
/* FIXME extend for gicv2 by: _vm.attach_pic(0x8010000); */
_load_kernel();
_load_dtb();
_load_initrd();
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]);
}
Genode::log("Start virtual machine ...");
Cpu & cpu = boot_cpu();
cpu.state().ip = _ram.base() + KERNEL_OFFSET;
cpu.state().r[0] = _ram.base() + DTB_OFFSET;
cpu.run();
};

View File

@ -0,0 +1,82 @@
/*
* \brief VMM example for ARMv8 virtualization
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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__VM_H_
#define _SRC__SERVER__VMM__VM_H_
#include <ram.h>
#include <exception.h>
#include <cpu.h>
#include <gic.h>
#include <pl011.h>
#include <base/attached_ram_dataspace.h>
#include <base/attached_rom_dataspace.h>
#include <vm_session/connection.h>
namespace Vmm { class Vm; }
class Vmm::Vm
{
private:
using Ep = Genode::Entrypoint;
enum {
RAM_ADDRESS = 0x40000000,
RAM_SIZE = 128 * 1024 *1024,
KERNEL_OFFSET = 0x80000,
INITRD_OFFSET = 32 * 1024 * 1024,
DTB_OFFSET = 64 * 1024 * 1024,
MAX_CPUS = 1,
STACK_SIZE = sizeof(unsigned long) * 2048,
};
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::UNCACHED };
Ram _ram { RAM_ADDRESS, RAM_SIZE,
(Genode::addr_t)_vm_ram.local_addr<void>()};
Genode::Heap _heap { _env.ram(), _env.rm() };
Mmio_bus _bus;
Gic _gic;
Genode::Constructible<Ep> _eps[MAX_CPUS];
Genode::Constructible<Cpu> _cpus[MAX_CPUS];
Pl011 _uart;
void _load_kernel();
void _load_dtb();
void _load_initrd();
public:
Vm(Genode::Env & env);
Mmio_bus & bus() { return _bus; }
Cpu & boot_cpu();
template <typename F>
void cpu(unsigned cpu, F func)
{
if (cpu >= MAX_CPUS) Genode::error("Cpu number out of bounds ");
else func(*_cpus[cpu]);
}
static unsigned last_cpu() { return MAX_CPUS - 1; }
};
#endif /* _SRC__SERVER__VMM__VM_H_ */