vmm: unify armv7/v8 virtualization

Fix #3638
This commit is contained in:
Stefan Kalkowski
2019-11-14 10:52:20 +01:00
committed by Christian Helmuth
parent 74e75d7fbc
commit 941e918b46
53 changed files with 1555 additions and 2648 deletions

View File

@ -0,0 +1,203 @@
/*
* \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_base;
using Vmm::Cpu;
using Vmm::Gic;
Genode::Lock & Vmm::lock() { static Genode::Lock l {}; return l; }
Cpu_base::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);
}
bool Cpu_base::_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.reg(Iss::Register::get(v), 0);
_state.ip += sizeof(Genode::uint32_t);
return false;
}
if (Iss::Direction::get(v)) { /* read access */
_state.reg(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.reg(Iss::Register::get(v)));
}
_state.ip += sizeof(Genode::uint32_t);
return true;
}
void Cpu_base::_handle_wfi()
{
_state.ip += sizeof(Genode::uint32_t);
if (_state.esr_el2 & 1) return; /* WFE */
_active = false;
_timer.schedule_timeout();
}
void Cpu_base::_handle_sync()
{
/* check device number*/
switch (Esr::Ec::get(_state.esr_el2)) {
case Esr::Ec::HVC:
_handle_hyper_call();
break;
case Esr::Ec::MRC_MCR: [[fallthrough]];
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: ",
Esr::Ec::get(_state.esr_el2));
};
}
void Cpu_base::_handle_irq()
{
switch (_state.irqs.last_irq) {
case VTIMER_IRQ:
_timer.handle_irq();
break;
default:
_gic.handle_irq();
};
}
void Cpu_base::_handle_hyper_call()
{
switch(_state.reg(0)) {
case Psci::PSCI_VERSION:
_state.reg(0, Psci::VERSION);
return;
case Psci::MIGRATE_INFO_TYPE:
_state.reg(0, Psci::NOT_SUPPORTED);
return;
case Psci::PSCI_FEATURES:
_state.reg(0, Psci::NOT_SUPPORTED);
return;
case Psci::CPU_ON:
_vm.cpu((unsigned)_state.reg(1), [&] (Cpu & cpu) {
cpu.state().ip = _state.reg(2);
cpu.state().reg(0, _state.reg(3));
cpu.run();
});
_state.reg(0, Psci::SUCCESS);
return;
default:
Genode::warning("unknown hypercall! ", cpu_id());
dump();
};
}
void Cpu_base::_handle_data_abort()
{
_vm.bus().handle_memory_access(*static_cast<Cpu*>(this));
_state.ip += sizeof(Genode::uint32_t);
}
void Cpu_base::_update_state()
{
if (!_gic.pending_irq()) return;
_active = true;
_timer.cancel_timeout();
}
unsigned Cpu_base::cpu_id() const { return _vcpu_id.id; }
void Cpu_base::run() { _vm_session.run(_vcpu_id); }
void Cpu_base::pause() { _vm_session.pause(_vcpu_id); }
bool Cpu_base::active() const { return _active; }
Cpu_base::State & Cpu_base::state() const { return _state; }
Gic::Gicd_banked & Cpu_base::gic() { return _gic; }
void Cpu_base::recall()
{
Genode::Signal_transmitter(_vm_handler).submit();
};
Cpu_base::Cpu_base(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_base::_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)))),
_gic(*this, gic, bus),
_timer(env, ep, _gic.irq(VTIMER_IRQ), *this) { }

View File

@ -0,0 +1,225 @@
/*
* \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_BASE_H_
#define _SRC__SERVER__VMM__CPU_BASE_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_base;
Genode::Lock & lock();
}
class Vmm::Cpu_base
{
public:
struct State : Genode::Vm_state
{
Genode::uint64_t reg(unsigned idx) const;
void reg(unsigned idx, Genode::uint64_t v);
};
struct Esr : Genode::Register<32>
{
struct Ec : Bitfield<26, 6>
{
enum {
WFI = 0x1,
MRC_MCR = 0x3,
HVC = 0x16,
MRS_MSR = 0x18,
DA = 0x24,
BRK = 0x3c
};
};
};
Cpu_base(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();
void initialize_boot(Genode::addr_t ip,
Genode::addr_t dtb);
template <typename FUNC>
void handle_signal(FUNC handler)
{
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_base & 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_base & cpu,
Genode::Entrypoint & ep,
T & o,
void (T::*f)())
: Base(ep, *this, &Signal_handler::handle),
cpu(cpu), obj(o), member(f) {}
};
protected:
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);
System_register(unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2,
const char * name,
bool writeable,
Genode::addr_t v,
Genode::Avl_tree<System_register> & tree)
: System_register(0, crn, op1, crm, op2,
name, writeable, v, 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); }
};
bool _active { true };
Vm & _vm;
Genode::Vm_connection & _vm_session;
Genode::Heap & _heap;
Signal_handler<Cpu_base> _vm_handler;
Genode::Vm_session::Vcpu_id _vcpu_id;
State & _state;
Genode::Avl_tree<System_register> _reg_tree;
/***********************
** Local peripherals **
***********************/
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_BASE_H_ */

View File

@ -0,0 +1,93 @@
/*
* \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;
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();
});
}
Generic_timer::Generic_timer(Genode::Env & env,
Genode::Entrypoint & ep,
Gic::Irq & irq,
Cpu_base & 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

@ -22,7 +22,7 @@
#include <util/register.h>
namespace Vmm {
class Cpu;
class Cpu_base;
class Generic_timer;
}
@ -33,7 +33,7 @@ class Vmm::Generic_timer : Gic::Irq::Irq_handler
Timer::Connection _timer;
Timer::One_shot_timeout<Generic_timer> _timeout;
Gic::Irq & _irq;
Cpu & _cpu;
Cpu_base & _cpu;
struct Ctrl : Genode::Register<32>
{
@ -56,7 +56,7 @@ class Vmm::Generic_timer : Gic::Irq::Irq_handler
Generic_timer(Genode::Env & env,
Genode::Entrypoint & ep,
Gic::Irq & irq,
Cpu & cpu);
Cpu_base & cpu);
void schedule_timeout();
void cancel_timeout();

View File

@ -159,13 +159,13 @@ void Gic::Gicd_banked::handle_irq()
irq(i).deassert();
_cpu.state().irqs.virtual_irq = 1023;
_cpu.state().irqs.virtual_irq = SPURIOUS;
}
bool Gic::Gicd_banked::pending_irq()
{
if (_cpu.state().irqs.virtual_irq != 1023) return true;
if (_cpu.state().irqs.virtual_irq != SPURIOUS) return true;
Irq * i = _gic._pending_list.highest_enabled();
Irq * j = _pending_list.highest_enabled();
@ -178,7 +178,7 @@ bool Gic::Gicd_banked::pending_irq()
}
Gic::Gicd_banked::Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus)
Gic::Gicd_banked::Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus)
: _cpu(cpu), _gic(gic)
{
for (unsigned i = 0; i < MAX_SGI; i++)
@ -187,11 +187,16 @@ Gic::Gicd_banked::Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus)
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;
_cpu.state().irqs.last_irq = SPURIOUS;
_cpu.state().irqs.virtual_irq = SPURIOUS;
_rdist.construct(0x80a0000 + (cpu.cpu_id()*0x20000), 0x20000, cpu.cpu_id(), Vm::last_cpu() == cpu.cpu_id());
bus.add(*_rdist);
if (gic.version() >= 3) {
_rdist.construct(GICR_MMIO_START +
(cpu.cpu_id()*0x20000), 0x20000,
cpu.cpu_id(),
Vm::last_cpu() == cpu.cpu_id());
bus.add(*_rdist);
}
}
@ -218,12 +223,18 @@ void Gic::Irq_reg::write(Address_range & access, Cpu & cpu, Register value)
}
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)
unsigned Gic::version() { return _version; }
Gic::Gic(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
unsigned cpus,
unsigned version,
Genode::Vm_connection & vm,
Mmio_bus & bus,
Genode::Env & env)
: Mmio_device(name, addr, size), _cpu_cnt(cpus), _version(version)
{
add(_ctrl);
add(_typer);
@ -248,4 +259,6 @@ Gic::Gic(const char * const name,
_spi[i].construct(i+MAX_SGI+MAX_PPI, Irq::SPI, _pending_list);
bus.add(*this);
if (version < 3) vm.attach_pic(GICC_MMIO_START);
}

View File

@ -18,11 +18,15 @@
#include <base/env.h>
#include <drivers/defs/arm_v7.h>
#include <vm_session/connection.h>
#include <util/list.h>
#include <util/register.h>
#include <util/reconstructible.h>
namespace Vmm { class Gic; }
namespace Vmm {
class Cpu_base;
class Gic;
}
class Vmm::Gic : public Vmm::Mmio_device
{
@ -120,11 +124,11 @@ class Vmm::Gic : public Vmm::Mmio_device
void handle_irq();
bool pending_irq();
Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus);
Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus);
private:
Cpu & _cpu;
Cpu_base & _cpu;
Gic & _gic;
Genode::Constructible<Irq> _sgi[MAX_SGI];
Genode::Constructible<Irq> _ppi[MAX_PPI];
@ -137,7 +141,9 @@ class Vmm::Gic : public Vmm::Mmio_device
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) };
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,
@ -242,9 +248,14 @@ class Vmm::Gic : public Vmm::Mmio_device
Genode::Constructible<Redistributor> _rdist;
};
unsigned version();
Gic(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
unsigned cpus,
unsigned version,
Genode::Vm_connection & vm,
Mmio_bus & bus,
Genode::Env & env);
@ -254,8 +265,8 @@ class Vmm::Gic : public Vmm::Mmio_device
Genode::Constructible<Irq> _spi[MAX_SPI];
Irq::List _pending_list;
unsigned _cpu_cnt { 2 }; /* FIXME: smp support */
unsigned _version { 3 }; /* FIXME: version support */
unsigned _cpu_cnt;
unsigned _version;
struct Gicd_ctlr : Genode::Register<32>, Mmio_register
{
@ -282,7 +293,8 @@ class Vmm::Gic : public Vmm::Mmio_device
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)) {}
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

View File

@ -112,11 +112,9 @@ void Vmm::Mmio_bus::handle_memory_access(Vmm::Cpu & cpu)
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]);
dev.write(dev_range, cpu, state.reg(idx));
} else {
if (idx > 30)
throw Exception("Wrong register index when reading ", bus_range);
state.r[idx] = dev.read(dev_range, cpu);
state.reg(idx, dev.read(dev_range, cpu));
}
} catch(Exception & e) {
Genode::warning(e);

View File

@ -1,77 +1,53 @@
/*
* \brief Driver for the Versatile Express A9X4 board
* \author Martin stein
* \date 2011-11-03
* \brief VMM address space utility
* \author Stefan Kalkowski
* \date 2019-11-13
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
* 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 _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_
#define _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_
#ifndef _SRC__SERVER__VMM__BOARD_H_
#define _SRC__SERVER__VMM__BOARD_H_
namespace Vea9x4 { struct Board; }
namespace Vmm {
enum {
SIZE_1_MB = 1024 * 1024,
KERNEL_OFFSET = 54 * SIZE_1_MB,
DTB_OFFSET = 64 * SIZE_1_MB,
INITRD_OFFSET = 96 * SIZE_1_MB,
/**
* Driver for the Versatile Express A9X4 board
*
* Implies the uATX motherboard and the CoreTile Express A9X4 daughterboard
*/
struct Vea9x4::Board
{
enum
{
/* MMIO */
MMIO_0_BASE = 0x10000000,
MMIO_0_SIZE = 0x10000000,
MMIO_1_BASE = 0x4C000000,
MMIO_1_SIZE = 0x04000000,
GIC_VERSION = 2,
GICD_MMIO_START = 0x8000000,
GICD_MMIO_SIZE = 0x10000,
GICC_MMIO_START = 0x8010000,
GICR_MMIO_START = 0x80a0000,
GICR_MMIO_SIZE = 0xf60000,
/* RAM */
RAM_0_BASE = 0x60000000,
RAM_0_SIZE = 0x20000000,
RAM_1_BASE = 0x84000000,
RAM_1_SIZE = 0x1c000000,
RAM_2_BASE = 0x48000000,
RAM_2_SIZE = 0x02000000,
PL011_MMIO_START = 0x9000000,
PL011_MMIO_SIZE = 0x1000,
PL011_IRQ = 33,
/* UART */
PL011_0_MMIO_BASE = MMIO_0_BASE + 0x9000,
PL011_0_MMIO_SIZE = 0x1000,
PL011_0_CLOCK = 24*1000*1000,
PL011_0_IRQ = 37,
PL011_1_IRQ = 38,
PL011_2_IRQ = 39,
PL011_3_IRQ = 40,
VIRTIO_CONSOLE_MMIO_START = 0xa000000,
VIRTIO_CONSOLE_MMIO_SIZE = 0x200,
VIRTIO_CONSOLE_IRQ = 48,
/* timer/counter */
SP804_0_1_MMIO_BASE = MMIO_0_BASE + 0x11000,
SP804_0_1_MMIO_SIZE = 0x1000,
SP804_0_1_CLOCK = 1000*1000,
SP804_0_1_IRQ = 34,
VIRTIO_NET_MMIO_START = 0xa000200,
VIRTIO_NET_MMIO_SIZE = 0x200,
VIRTIO_NET_IRQ = 49,
/* PS2 */
KMI_0_IRQ = 44,
KMI_1_IRQ = 45,
RAM_START = 0x40000000,
RAM_SIZE = 128 * 1024 *1024,
/* LAN */
LAN9118_IRQ = 47,
VTIMER_IRQ = 27,
/* card reader */
PL180_0_IRQ = 9,
PL180_1_IRQ = 10,
/* CPU */
CORTEX_A9_PRIVATE_MEM_BASE = 0x1e000000,
CORTEX_A9_PRIVATE_MEM_SIZE = 0x2000,
CORTEX_A9_PRIVATE_TIMER_CLK = 200010000,
MAX_CPUS = 1,
};
};
#endif /* _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_ */
}
#endif /* _SRC__SERVER__VMM__BOARD_H_ */

View File

@ -0,0 +1,189 @@
/*
* \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_base;
using Vmm::Cpu;
using Vmm::Gic;
Genode::uint64_t Cpu_base::State::reg(unsigned idx) const
{
if (idx > 15) return 0;
Genode::uint32_t * r = (Genode::uint32_t*)this;
r += idx;
return *r;
}
void Cpu_base::State::reg(unsigned idx, Genode::uint64_t v)
{
if (idx > 15) return;
Genode::uint32_t * r = (Genode::uint32_t*)this;
r += idx;
*r = v;
}
Cpu_base::System_register::Iss::access_t
Cpu_base::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);
Opcode1::set(v, op1);
Opcode2::set(v, op2);
return v;
};
Cpu_base::System_register::Iss::access_t
Cpu_base::System_register::Iss::mask_encoding(access_t v)
{
return Crm::masked(v) |
Crn::masked(v) |
Opcode1::masked(v) |
Opcode2::masked(v);
}
void Cpu_base::_handle_brk()
{
Genode::error(__func__, " not implemented yet");
}
void Cpu_base::handle_exception()
{
/* check exception reason */
switch (_state.cpu_exception) {
case Cpu::NO_EXCEPTION: break;
case Cpu::FIQ: [[fallthrough]];
case Cpu::IRQ: _handle_irq(); break;
case Cpu::TRAP: _handle_sync(); break;
default:
throw Exception("Curious exception ",
_state.cpu_exception, " occured");
}
_state.cpu_exception = Cpu::NO_EXCEPTION;
}
void Cpu_base::dump()
{
using namespace Genode;
auto lambda = [] (unsigned i) {
switch (i) {
case 0: return "und";
case 1: return "svc";
case 2: return "abt";
case 3: return "irq";
case 4: return "fiq";
default: return "unknown";
};
};
log("VM state (", _active ? "active" : "inactive", ") :");
for (unsigned i = 0; i < 13; i++) {
log(" r", i, " = ",
Hex(_state.reg(i), Hex::PREFIX, Hex::PAD));
}
log(" sp = ", Hex(_state.sp, Hex::PREFIX, Hex::PAD));
log(" lr = ", Hex(_state.lr, Hex::PREFIX, Hex::PAD));
log(" ip = ", Hex(_state.ip, Hex::PREFIX, Hex::PAD));
log(" cpsr = ", Hex(_state.cpsr, Hex::PREFIX, Hex::PAD));
for (unsigned i = 0; i < State::Mode_state::MAX; i++) {
log(" sp_", lambda(i), " = ",
Hex(_state.mode[i].sp, Hex::PREFIX, Hex::PAD));
log(" lr_", lambda(i), " = ",
Hex(_state.mode[i].lr, Hex::PREFIX, Hex::PAD));
log(" spsr_", lambda(i), " = ",
Hex(_state.mode[i].spsr, Hex::PREFIX, Hex::PAD));
}
log(" exception = ", _state.cpu_exception);
log(" esr_el2 = ", Hex(_state.esr_el2, Hex::PREFIX, Hex::PAD));
log(" hpfar_el2 = ", Hex(_state.hpfar_el2, Hex::PREFIX, Hex::PAD));
log(" far_el2 = ", Hex(_state.far_el2, Hex::PREFIX, Hex::PAD));
log(" hifar = ", Hex(_state.hifar, Hex::PREFIX, Hex::PAD));
log(" dfsr = ", Hex(_state.dfsr, Hex::PREFIX, Hex::PAD));
log(" ifsr = ", Hex(_state.ifsr, Hex::PREFIX, Hex::PAD));
log(" sctrl = ", Hex(_state.sctrl, Hex::PREFIX, Hex::PAD));
_timer.dump();
}
void Cpu_base::initialize_boot(Genode::addr_t ip, Genode::addr_t dtb)
{
state().reg(1, 0xffffffff); /* invalid machine type */
state().reg(2, dtb);
state().ip = ip;
}
Genode::addr_t Cpu::Ccsidr::read() const
{
struct Csselr : Genode::Register<32>
{
struct Level : Bitfield<1, 4> {};
};
enum { INVALID = 0xffffffff };
unsigned level = Csselr::Level::get(csselr.read());
if (level > 6) {
Genode::warning("Invalid Csselr value!");
return INVALID;
}
return 0;
}
Cpu::Cpu(Vm & vm,
Genode::Vm_connection & vm_session,
Mmio_bus & bus,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep)
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep),
_sr_midr (0, 0, 0, 0, "MIDR", false, 0x412fc0f1, _reg_tree),
_sr_mpidr (0, 0, 0, 5, "MPIDR", false, 1<<31|cpu_id(), _reg_tree),
_sr_mmfr0 (0, 0, 1, 4, "MMFR0", false, 0x10201105, _reg_tree),
_sr_mmfr1 (0, 0, 1, 5, "MMFR1", false, 0x20000000, _reg_tree),
_sr_mmfr2 (0, 0, 1, 6, "MMFR2", false, 0x01240000, _reg_tree),
_sr_mmfr3 (0, 0, 1, 7, "MMFR3", false, 0x02102211, _reg_tree),
_sr_isar0 (0, 0, 2, 0, "ISAR0", false, 0x02101110, _reg_tree),
_sr_isar1 (0, 0, 2, 1, "ISAR1", false, 0x13112111, _reg_tree),
_sr_isar2 (0, 0, 2, 2, "ISAR2", false, 0x21232041, _reg_tree),
_sr_isar3 (0, 0, 2, 3, "ISAR3", false, 0x11112131, _reg_tree),
_sr_isar4 (0, 0, 2, 4, "ISAR4", false, 0x10011142, _reg_tree),
_sr_isar5 (0, 0, 2, 5, "ISAR5", false, 0x0, _reg_tree),
_sr_pfr0 (0, 0, 1, 0, "PFR0", false, 0x00001131, _reg_tree),
_sr_pfr1 (0, 0, 1, 1, "PFR1", false, 0x00011011, _reg_tree),
_sr_clidr (0, 1, 0, 1, "CLIDR", false, 0xa200023, _reg_tree),
_sr_csselr (0, 2, 0, 0, "CSSELR", true, 0x0, _reg_tree),
_sr_ctr (0, 0, 0, 1, "CTR", true, 0x8444c004, _reg_tree),
_sr_revidr (0, 0, 0, 6, "REVIDR", true, 0x0, _reg_tree),
_sr_ccsidr (_sr_csselr, _reg_tree),
_sr_actlr (1, 0, 0, 1, "ACTLR", true, 0x0, _reg_tree)
{
_state.cpsr = 0x93; /* el1 mode and IRQs disabled */
_state.sctrl = 0xc50078;
}

View File

@ -0,0 +1,91 @@
/*
* \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 <cpu_base.h>
namespace Vmm { class Cpu; }
class Vmm::Cpu : public Vmm::Cpu_base
{
public:
Cpu(Vm & vm,
Genode::Vm_connection & vm_session,
Mmio_bus & bus,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep);
enum Exception_type {
NO_EXCEPTION,
RESET,
UNDEFINED,
HVC,
PF_ABORT,
DATA_ABORT,
IRQ,
FIQ,
TRAP
};
private:
struct Ccsidr : System_register
{
System_register & csselr;
Ccsidr(System_register &csselr,
Genode::Avl_tree<System_register> & tree)
: System_register(0, 1, 0, 0, "CCSIDR", false, 0x0, tree),
csselr(csselr) {}
virtual Genode::addr_t read() const override;
};
/******************************
** Identification registers **
******************************/
System_register _sr_midr;
System_register _sr_mpidr;
System_register _sr_mmfr0;
System_register _sr_mmfr1;
System_register _sr_mmfr2;
System_register _sr_mmfr3;
System_register _sr_isar0;
System_register _sr_isar1;
System_register _sr_isar2;
System_register _sr_isar3;
System_register _sr_isar4;
System_register _sr_isar5;
System_register _sr_pfr0;
System_register _sr_pfr1;
System_register _sr_clidr;
System_register _sr_csselr;
System_register _sr_ctr;
System_register _sr_revidr;
Ccsidr _sr_ccsidr;
/*********************
** System register **
*********************/
System_register _sr_actlr;
};
#endif /* _SRC__SERVER__VMM__CPU_H_ */

View File

@ -0,0 +1,41 @@
/*
* \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("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq));
ticks_per_ms = freq / 1000;
}
return ticks_per_ms;
}
Genode::uint64_t Generic_timer::_usecs_left()
{
Genode::uint64_t count;
Genode::uint32_t low, high;
asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (low), "=r" (high));
count = (Genode::uint64_t)high << 32 | (Genode::uint64_t)low;
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());
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,19 @@
TARGET = vmm
REQUIRES = hw arm_v7
LIBS = base
SRC_CC = main.cc
INC_DIR += $(PRG_DIR)
SRC_CC += spec/arm_v7/generic_timer.cc
SRC_CC += address_space.cc
SRC_CC += cpu.cc
SRC_CC += cpu_base.cc
SRC_CC += generic_timer.cc
SRC_CC += gic.cc
SRC_CC += main.cc
SRC_CC += mmio.cc
SRC_CC += pl011.cc
SRC_CC += vm.cc
SRC_CC += virtio_device.cc
INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)
vpath %.cc $(PRG_DIR)/../..
CC_CXX_WARN_STRICT :=

View File

@ -1,30 +0,0 @@
.section ".text.crt0"
.global _start
_start:
/* idle a little initially because U-Boot likes it this way */
mov r8, r8
mov r8, r8
mov r8, r8
mov r8, r8
mov r8, r8
mov r8, r8
mov r8, r8
mov r8, r8
/* zero-fill BSS segment */
ldr r0, =_bss_start
ldr r1, =_bss_end
mov r2, #0
1:
cmp r1, r0
ble 2f
str r2, [r0]
add r0, r0, #4
b 1b
2:
hvc #0
1: b 1b

View File

@ -1,4 +0,0 @@
TARGET = vmm-test-kernel
REQUIRES = arm_v7
SRC_S = main.s
CC_MARCH = -mcpu=cortex-a15

View File

@ -0,0 +1,80 @@
/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-a15";
reg = <0x00>;
device_type = "cpu";
};
};
timer {
interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>;
compatible = "arm,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 ip=dhcp console=hvc0";
linux,initrd-start = <0x46000000>;
linux,initrd-end = <0x460b04b6>;
stdout-path = "/pl011@9000000";
};
intc@8000000 {
compatible = "arm,cortex-a15-gic";
phandle = <0x8001>;
reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000>;
ranges;
#address-cells = <0x02>;
#redistributor-regions = <0x01>;
interrupt-controller;
#interrupt-cells = <0x03>;
#size-cells = <0x02>;
};
virtio_mmio@a000000 {
interrupts = <0x00 0x10 0x01>;
compatible = "virtio,mmio";
dma-coherent;
reg = <0x00 0xa000000 0x00 0x200>;
};
virtio_mmio@a000200 {
interrupts = <0x00 0x11 0x01>;
compatible = "virtio,mmio";
dma-coherent;
reg = <0x00 0xa000200 0x00 0x200>;
};
};

View File

@ -0,0 +1,53 @@
/*
* \brief VMM address space utility
* \author Stefan Kalkowski
* \date 2019-11-13
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SRC__SERVER__VMM__BOARD_H_
#define _SRC__SERVER__VMM__BOARD_H_
namespace Vmm {
enum {
SIZE_1_MB = 1024 * 1024,
KERNEL_OFFSET = 0x80000,
INITRD_OFFSET = 32 * SIZE_1_MB,
DTB_OFFSET = 64 * SIZE_1_MB,
GIC_VERSION = 3,
GICD_MMIO_START = 0x8000000,
GICD_MMIO_SIZE = 0x10000,
GICC_MMIO_START = 0x8010000,
GICR_MMIO_START = 0x80a0000,
GICR_MMIO_SIZE = 0xf60000,
PL011_MMIO_START = 0x9000000,
PL011_MMIO_SIZE = 0x1000,
PL011_IRQ = 33,
VIRTIO_CONSOLE_MMIO_START = 0xa000000,
VIRTIO_CONSOLE_MMIO_SIZE = 0x200,
VIRTIO_CONSOLE_IRQ = 48,
VIRTIO_NET_MMIO_START = 0xa000200,
VIRTIO_NET_MMIO_SIZE = 0x200,
VIRTIO_NET_IRQ = 49,
RAM_START = 0x40000000,
RAM_SIZE = 128 * 1024 *1024,
VTIMER_IRQ = 27,
MAX_CPUS = 1,
};
}
#endif /* _SRC__SERVER__VMM__BOARD_H_ */

View File

@ -14,15 +14,27 @@
#include <vm.h>
#include <psci.h>
using Vmm::Cpu_base;
using Vmm::Cpu;
using Vmm::Gic;
Genode::Lock & Vmm::lock() { static Genode::Lock l {}; return l; }
Genode::uint64_t Cpu_base::State::reg(unsigned idx) const
{
if (idx > 30) return 0;
return r[idx];
}
Cpu::System_register::Iss::access_t
Cpu::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
unsigned crm, unsigned op2)
void Cpu_base::State::reg(unsigned idx, Genode::uint64_t v)
{
if (idx > 30) return;
r[idx] = v;
}
Cpu_base::System_register::Iss::access_t
Cpu_base::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
unsigned crm, unsigned op2)
{
access_t v = 0;
Crn::set(v, crn);
@ -34,8 +46,8 @@ Cpu::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
};
Cpu::System_register::Iss::access_t
Cpu::System_register::Iss::mask_encoding(access_t v)
Cpu_base::System_register::Iss::access_t
Cpu_base::System_register::Iss::mask_encoding(access_t v)
{
return Crm::masked(v) |
Crn::masked(v) |
@ -45,21 +57,69 @@ Cpu::System_register::Iss::mask_encoding(access_t 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)
void Cpu_base::_handle_brk()
{
tree.insert(this);
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_base::handle_exception()
{
/* check exception reason */
switch (_state.exception_type) {
case Cpu::NO_EXCEPTION: break;
case Cpu::AARCH64_IRQ: _handle_irq(); break;
case Cpu::AARCH64_SYNC: _handle_sync(); break;
default:
throw Exception("Curious exception ",
_state.exception_type, " occured");
}
_state.exception_type = Cpu::NO_EXCEPTION;
}
void Cpu_base::dump()
{
using namespace Genode;
auto lambda = [] (addr_t exc) {
switch (exc) {
case Cpu::AARCH64_SYNC: return "aarch64 sync";
case Cpu::AARCH64_IRQ: return "aarch64 irq";
case Cpu::AARCH64_FIQ: return "aarch64 fiq";
case Cpu::AARCH64_SERROR: return "aarch64 serr";
case Cpu::AARCH32_SYNC: return "aarch32 sync";
case Cpu::AARCH32_IRQ: return "aarch32 irq";
case Cpu::AARCH32_FIQ: return "aarch32 fiq";
case Cpu::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();
}
@ -139,216 +199,13 @@ void Cpu::Icc_sgi1r_el1::write(Genode::addr_t v)
};
bool Cpu::_handle_sys_reg()
void Cpu_base::initialize_boot(Genode::addr_t ip, Genode::addr_t dtb)
{
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;
state().reg(0, dtb);
state().ip = ip;
}
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,
@ -356,15 +213,7 @@ Cpu::Cpu(Vm & vm,
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
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep),
_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),
@ -379,13 +228,10 @@ Cpu::Cpu(Vm & vm,
_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),
@ -394,9 +240,7 @@ Cpu::Cpu(Vm & vm,
_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)
_sr_sgi1r_el1 (_reg_tree, vm)
{
_state.pstate = 0b1111000101; /* el1 mode and IRQs disabled */
_state.vmpidr_el2 = cpu_id();

View File

@ -14,41 +14,14 @@
#ifndef _SRC__SERVER__VMM__CPU_H_
#define _SRC__SERVER__VMM__CPU_H_
#include <exception.h>
#include <generic_timer.h>
#include <cpu_base.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 Cpu; }
namespace Vmm {
class Vm;
class Cpu;
Genode::Lock & lock();
}
class Vmm::Cpu
class Vmm::Cpu : public Vmm::Cpu_base
{
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,
@ -57,58 +30,6 @@ class Vmm::Cpu
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)
{
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,
@ -121,72 +42,7 @@ class Vmm::Cpu
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); }
};
private:
class Id_aa64pfr0 : public System_register,
public Genode::Register<64>
@ -252,15 +108,6 @@ class Vmm::Cpu
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 **
******************************/
@ -308,22 +155,10 @@ class Vmm::Cpu
System_register _sr_oslar;
/***********************
** Local peripherals **
** GIC cpu interface **
***********************/
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

@ -28,26 +28,6 @@ Genode::uint64_t Generic_timer::_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;
@ -57,60 +37,3 @@ Genode::uint64_t Generic_timer::_usecs_left()
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

@ -1,15 +1,19 @@
TARGET = vmm
REQUIRES = hw arm_v8
LIBS = base
SRC_CC += spec/arm_v8/generic_timer.cc
SRC_CC += address_space.cc
SRC_CC += cpu.cc
SRC_CC += cpu_base.cc
SRC_CC += generic_timer.cc
SRC_CC += gicv2.cc
SRC_CC += gic.cc
SRC_CC += main.cc
SRC_CC += mmio.cc
SRC_CC += pl011.cc
SRC_CC += virtio_device.cc
SRC_CC += vm.cc
INC_DIR += $(PRG_DIR)
INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)
vpath %.cc $(PRG_DIR)/../..
CC_CXX_WARN_STRICT :=

View File

@ -48,9 +48,12 @@ struct Vmm::Virtio_queue_data
uint32_t ready { 0 };
bool tx { false };
addr_t descr() const { return ((addr_t)descr_high << 32) | descr_low; }
addr_t driver() const { return ((addr_t)driver_high << 32) | driver_low; }
addr_t device() const { return ((addr_t)device_high << 32) | device_low; }
uint64_t descr() const {
return ((uint64_t)descr_high << 32) | descr_low; }
uint64_t driver() const {
return ((uint64_t)driver_high << 32) | driver_low; }
uint64_t device() const {
return ((uint64_t)device_high << 32) | device_low; }
enum { MAX_QUEUE_SIZE = 1 << 15 };
};
@ -83,10 +86,10 @@ class Vmm::Virtio_descriptor : Genode::Mmio
return Virtio_descriptor(base() + (size() * idx));
}
addr_t address() const { return read<Address>(); }
size_t length () const { return read<Length>(); }
uint16_t flags() const { return read<Flags>(); }
uint16_t next() const { return read<Next>(); }
uint64_t address() const { return read<Address>(); }
size_t length () const { return read<Length>(); }
uint16_t flags() const { return read<Flags>(); }
uint16_t next() const { return read<Next>(); }
};
@ -173,7 +176,7 @@ class Vmm::Virtio_queue
id %= _length;
Virtio_descriptor descr = _descr.index(id);
addr_t address = descr.address();
uint64_t address = descr.address();
size_t length = descr.length();
if (!address || !length) break;

View File

@ -48,14 +48,16 @@ Vmm::Cpu & Vm::boot_cpu()
Vm::Vm(Genode::Env & env)
: _env(env),
_gic("Gicv3", 0x8000000, 0x10000, _bus, env),
_uart("Pl011", 0x9000000, 0x1000, 33, boot_cpu(), _bus, env),
_virtio_console("HVC", 0xa000000, 0x200, 48, boot_cpu(), _bus, _ram, env),
_virtio_net("Net", 0xa000200, 0x200, 49, boot_cpu(), _bus, _ram, env)
_gic("Gicv3", GICD_MMIO_START, GICD_MMIO_SIZE,
MAX_CPUS, GIC_VERSION, _vm, _bus, env),
_uart("Pl011", PL011_MMIO_START, PL011_MMIO_SIZE,
PL011_IRQ, boot_cpu(), _bus, env),
_virtio_console("HVC", VIRTIO_CONSOLE_MMIO_START, VIRTIO_CONSOLE_MMIO_SIZE,
VIRTIO_CONSOLE_IRQ, boot_cpu(), _bus, _ram, env),
_virtio_net("Net", VIRTIO_NET_MMIO_START, VIRTIO_NET_MMIO_SIZE,
VIRTIO_NET_IRQ, boot_cpu(), _bus, _ram, env)
{
_vm.attach(_vm_ram.cap(), RAM_ADDRESS);
/* FIXME extend for gicv2 by: _vm.attach_pic(0x8010000); */
_vm.attach(_vm_ram.cap(), RAM_START);
_load_kernel();
_load_dtb();
@ -71,7 +73,7 @@ Vm::Vm(Genode::Env & env)
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.initialize_boot(_ram.base() + KERNEL_OFFSET,
_ram.base() + DTB_OFFSET);
cpu.run();
};

View File

@ -14,6 +14,7 @@
#ifndef _SRC__SERVER__VMM__VM_H_
#define _SRC__SERVER__VMM__VM_H_
#include <board.h>
#include <ram.h>
#include <exception.h>
#include <cpu.h>
@ -34,15 +35,7 @@ class Vmm::Vm
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,
};
enum { STACK_SIZE = sizeof(unsigned long) * 2048, };
Genode::Env & _env;
Genode::Vm_connection _vm { _env };
@ -51,7 +44,7 @@ class Vmm::Vm
Genode::Attached_rom_dataspace _initrd_rom { _env, "initrd" };
Genode::Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(),
RAM_SIZE, Genode::CACHED };
Ram _ram { RAM_ADDRESS, RAM_SIZE,
Ram _ram { RAM_START, RAM_SIZE,
(Genode::addr_t)_vm_ram.local_addr<void>()};
Genode::Heap _heap { _env.ram(), _env.rm() };
Mmio_bus _bus;