mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-18 15:18:20 +00:00
committed by
Christian Helmuth
parent
74e75d7fbc
commit
941e918b46
203
repos/os/src/server/vmm/cpu_base.cc
Normal file
203
repos/os/src/server/vmm/cpu_base.cc
Normal 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) { }
|
225
repos/os/src/server/vmm/cpu_base.h
Normal file
225
repos/os/src/server/vmm/cpu_base.h
Normal 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_ */
|
93
repos/os/src/server/vmm/generic_timer.cc
Normal file
93
repos/os/src/server/vmm/generic_timer.cc
Normal 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));
|
||||
}
|
@ -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();
|
@ -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);
|
||||
}
|
@ -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
|
@ -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);
|
@ -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_ */
|
||||
|
189
repos/os/src/server/vmm/spec/arm_v7/cpu.cc
Normal file
189
repos/os/src/server/vmm/spec/arm_v7/cpu.cc
Normal 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;
|
||||
}
|
91
repos/os/src/server/vmm/spec/arm_v7/cpu.h
Normal file
91
repos/os/src/server/vmm/spec/arm_v7/cpu.h
Normal 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_ */
|
41
repos/os/src/server/vmm/spec/arm_v7/generic_timer.cc
Normal file
41
repos/os/src/server/vmm/spec/arm_v7/generic_timer.cc
Normal 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
@ -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 :=
|
||||
|
@ -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
|
@ -1,4 +0,0 @@
|
||||
TARGET = vmm-test-kernel
|
||||
REQUIRES = arm_v7
|
||||
SRC_S = main.s
|
||||
CC_MARCH = -mcpu=cortex-a15
|
80
repos/os/src/server/vmm/spec/arm_v7/virt.dts
Executable file
80
repos/os/src/server/vmm/spec/arm_v7/virt.dts
Executable 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>;
|
||||
};
|
||||
};
|
53
repos/os/src/server/vmm/spec/arm_v8/board.h
Normal file
53
repos/os/src/server/vmm/spec/arm_v8/board.h
Normal 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_ */
|
@ -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();
|
||||
|
@ -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_ */
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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 :=
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
};
|
@ -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;
|
Reference in New Issue
Block a user