mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 10:46:25 +00:00
parent
af29dcf557
commit
972e1893c9
@ -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
|
||||
|
||||
#
|
||||
|
32
repos/os/src/server/vmm/spec/arm_v8/address_space.cc
Normal file
32
repos/os/src/server/vmm/spec/arm_v8/address_space.cc
Normal 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);
|
||||
}
|
82
repos/os/src/server/vmm/spec/arm_v8/address_space.h
Normal file
82
repos/os/src/server/vmm/spec/arm_v8/address_space.h
Normal 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_ */
|
403
repos/os/src/server/vmm/spec/arm_v8/cpu.cc
Normal file
403
repos/os/src/server/vmm/spec/arm_v8/cpu.cc
Normal 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();
|
||||
}
|
331
repos/os/src/server/vmm/spec/arm_v8/cpu.h
Normal file
331
repos/os/src/server/vmm/spec/arm_v8/cpu.h
Normal 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_ */
|
22
repos/os/src/server/vmm/spec/arm_v8/exception.h
Normal file
22
repos/os/src/server/vmm/spec/arm_v8/exception.h
Normal 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_ */
|
||||
|
116
repos/os/src/server/vmm/spec/arm_v8/generic_timer.cc
Normal file
116
repos/os/src/server/vmm/spec/arm_v8/generic_timer.cc
Normal 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));
|
||||
}
|
74
repos/os/src/server/vmm/spec/arm_v8/generic_timer.h
Normal file
74
repos/os/src/server/vmm/spec/arm_v8/generic_timer.h
Normal 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_ */
|
440
repos/os/src/server/vmm/spec/arm_v8/gic.h
Normal file
440
repos/os/src/server/vmm/spec/arm_v8/gic.h
Normal 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_ */
|
251
repos/os/src/server/vmm/spec/arm_v8/gicv2.cc
Normal file
251
repos/os/src/server/vmm/spec/arm_v8/gicv2.cc
Normal 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);
|
||||
}
|
125
repos/os/src/server/vmm/spec/arm_v8/hw_device.h
Normal file
125
repos/os/src/server/vmm/spec/arm_v8/hw_device.h
Normal 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_ */
|
17
repos/os/src/server/vmm/spec/arm_v8/main.cc
Normal file
17
repos/os/src/server/vmm/spec/arm_v8/main.cc
Normal 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); }
|
126
repos/os/src/server/vmm/spec/arm_v8/mmio.cc
Normal file
126
repos/os/src/server/vmm/spec/arm_v8/mmio.cc
Normal 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), ")");
|
||||
}
|
||||
}
|
90
repos/os/src/server/vmm/spec/arm_v8/mmio.h
Normal file
90
repos/os/src/server/vmm/spec/arm_v8/mmio.h
Normal 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_ */
|
102
repos/os/src/server/vmm/spec/arm_v8/pl011.cc
Normal file
102
repos/os/src/server/vmm/spec/arm_v8/pl011.cc
Normal 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(®, 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);
|
||||
}
|
145
repos/os/src/server/vmm/spec/arm_v8/pl011.h
Normal file
145
repos/os/src/server/vmm/spec/arm_v8/pl011.h
Normal 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_ */
|
35
repos/os/src/server/vmm/spec/arm_v8/psci.h
Normal file
35
repos/os/src/server/vmm/spec/arm_v8/psci.h
Normal 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_ */
|
39
repos/os/src/server/vmm/spec/arm_v8/ram.h
Normal file
39
repos/os/src/server/vmm/spec/arm_v8/ram.h
Normal 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_ */
|
14
repos/os/src/server/vmm/spec/arm_v8/target.mk
Normal file
14
repos/os/src/server/vmm/spec/arm_v8/target.mk
Normal 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 :=
|
65
repos/os/src/server/vmm/spec/arm_v8/virt.dts
Executable file
65
repos/os/src/server/vmm/spec/arm_v8/virt.dts
Executable 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>;
|
||||
};
|
||||
};
|
75
repos/os/src/server/vmm/spec/arm_v8/vm.cc
Normal file
75
repos/os/src/server/vmm/spec/arm_v8/vm.cc
Normal 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();
|
||||
};
|
82
repos/os/src/server/vmm/spec/arm_v8/vm.h
Normal file
82
repos/os/src/server/vmm/spec/arm_v8/vm.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user