vmm: port to new VMM library API

Ref #4968
This commit is contained in:
Benjamin Lamowski 2023-08-10 19:08:04 +02:00 committed by Christian Helmuth
parent 7e79128c03
commit cfab4e74a9
17 changed files with 377 additions and 244 deletions

View File

@ -1,11 +1,12 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-07-18
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -37,16 +38,16 @@ Cpu_base::System_register::System_register(unsigned op0,
}
bool Cpu_base::_handle_sys_reg()
bool Cpu_base::_handle_sys_reg(State & state)
{
using Iss = System_register::Iss;
Iss::access_t v = _state.esr_el2;
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("ignore unknown system register access @ ip=", (void*)state.ip, ":");
Genode::error(Iss::Direction::get(v) ? "read" : "write",
": "
"op0=", Iss::Opcode0::get(v), " "
@ -55,125 +56,138 @@ bool Cpu_base::_handle_sys_reg()
"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);
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());
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)));
reg->write(state.reg(Iss::Register::get(v)));
}
_state.ip += sizeof(Genode::uint32_t);
state.ip += sizeof(Genode::uint32_t);
return true;
}
void Cpu_base::_handle_wfi()
void Cpu_base::_handle_wfi(State &state)
{
_state.ip += sizeof(Genode::uint32_t);
state.ip += sizeof(Genode::uint32_t);
if (_state.esr_el2 & 1) return; /* WFE */
if (state.esr_el2 & 1) return; /* WFE */
_active = false;
_timer.schedule_timeout();
_timer.schedule_timeout(state);
}
void Cpu_base::_handle_sync()
void Cpu_base::_handle_startup(State &state)
{
Generic_timer::setup_state(state);
Gic::Gicd_banked::setup_state(state);
setup_state(state);
if (cpu_id() == 0) {
initialize_boot(state, _vm.kernel_addr(), _vm.dtb_addr());
} else {
_cpu_ready.down();
}
_active = true;
}
void Cpu_base::_handle_sync(State &state)
{
/* check device number*/
switch (Esr::Ec::get(_state.esr_el2)) {
switch (Esr::Ec::get(state.esr_el2)) {
case Esr::Ec::HVC_32: [[fallthrough]];
case Esr::Ec::HVC:
_handle_hyper_call();
_handle_hyper_call(state);
break;
case Esr::Ec::MRC_MCR: [[fallthrough]];
case Esr::Ec::MRS_MSR:
_handle_sys_reg();
_handle_sys_reg(state);
break;
case Esr::Ec::DA:
_handle_data_abort();
_handle_data_abort(state);
break;
case Esr::Ec::WFI:
_handle_wfi();
_handle_wfi(state);
return;
case Esr::Ec::BRK:
_handle_brk();
_handle_brk(state);
return;
default:
throw Exception("Unknown trap: ",
Esr::Ec::get(_state.esr_el2));
Esr::Ec::get(state.esr_el2));
};
}
void Cpu_base::_handle_irq()
void Cpu_base::_handle_irq(State &state)
{
switch (_state.irqs.last_irq) {
switch (state.irqs.last_irq) {
case VTIMER_IRQ:
_timer.handle_irq();
_timer.handle_irq(state);
break;
default:
_gic.handle_irq();
_gic.handle_irq(state);
};
}
void Cpu_base::_handle_hyper_call()
void Cpu_base::_handle_hyper_call(State &state)
{
switch(_state.reg(0)) {
switch(state.reg(0)) {
case Psci::PSCI_VERSION:
_state.reg(0, Psci::VERSION);
state.reg(0, Psci::VERSION);
return;
case Psci::MIGRATE_INFO_TYPE:
_state.reg(0, Psci::NOT_SUPPORTED);
state.reg(0, Psci::NOT_SUPPORTED);
return;
case Psci::PSCI_FEATURES:
_state.reg(0, Psci::NOT_SUPPORTED);
state.reg(0, Psci::NOT_SUPPORTED);
return;
case Psci::CPU_ON_32: [[fallthrough]];
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();
_vm.cpu((unsigned)state.reg(1), [&] (Cpu & cpu) {
State & local_state = cpu.state();
cpu.initialize_boot(local_state, state.reg(2), state.reg(3));
cpu.set_ready();
});
_state.reg(0, Psci::SUCCESS);
state.reg(0, Psci::SUCCESS);
return;
default:
Genode::warning("unknown hypercall! ", cpu_id());
dump();
dump(state);
};
}
void Cpu_base::_handle_data_abort()
void Cpu_base::_handle_data_abort(State &state)
{
_vm.bus().handle_memory_access(*static_cast<Cpu*>(this));
_state.ip += sizeof(Genode::uint32_t);
_vm.bus().handle_memory_access(state, *static_cast<Cpu *>(this));
state.ip += sizeof(Genode::uint32_t);
}
void Cpu_base::_update_state()
void Cpu_base::_update_state(State &state)
{
if (!_gic.pending_irq()) return;
if (!_gic.pending_irq(state)) return;
_active = true;
_timer.cancel_timeout();
}
unsigned Cpu_base::cpu_id() const { return _vcpu_id; }
void Cpu_base::run() { _vm_vcpu.run(); }
void Cpu_base::pause() { _vm_vcpu.pause(); }
bool Cpu_base::active() const { return _active; }
Cpu_base::State & Cpu_base::state() const { return _state; }
Gic::Gicd_banked & Cpu_base::gic() { return _gic; }
@ -197,6 +211,5 @@ Cpu_base::Cpu_base(Vm & vm,
_heap(heap),
_vm_handler(*this, ep, *this, &Cpu_base::_handle_nothing),
_vm_vcpu(_vm_session, heap, _vm_handler, _exit_config),
_state(*((State*)(&_vm_vcpu.state()))),
_gic(*this, gic, bus),
_timer(env, ep, _gic.irq(VTIMER_IRQ), *this) { }

View File

@ -1,11 +1,12 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-07-18
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -16,6 +17,7 @@
#include <exception.h>
#include <generic_timer.h>
#include <state.h>
#include <base/env.h>
#include <base/heap.h>
@ -33,12 +35,6 @@ class Vmm::Cpu_base
{
public:
struct State : Genode::Vm_state
{
addr_t reg(addr_t idx) const;
void reg(addr_t idx, addr_t v);
};
struct Esr : Genode::Register<sizeof(addr_t)*8>
{
struct Ec : Bitfield<26, 6>
@ -65,28 +61,55 @@ class Vmm::Cpu_base
unsigned cpu_id);
unsigned cpu_id() const;
void run();
void pause();
bool active() const;
State & state() const;
Gic::Gicd_banked & gic();
void dump();
void handle_exception();
void dump(State & state);
void handle_exception(State &state);
void recall();
void initialize_boot(Genode::addr_t ip,
void initialize_boot(State &state,
Genode::addr_t ip,
Genode::addr_t dtb);
virtual void setup_state(State &) { };
virtual ~Cpu_base() = default;
State & state() {
return _state->ref;
}
template<typename FN>
void with_state(FN const & fn)
{
_vm_vcpu.with_state(fn);
}
void set_ready() {
_cpu_ready.up();
}
template <typename FUNC>
void handle_signal(FUNC handler)
{
if (active()) {
pause();
handle_exception();
}
_vm_vcpu.with_state([this, handler](Vm_state &vmstate) {
State & state = static_cast<State &>(vmstate);
_state.construct(state);
handler();
_update_state();
if (active()) run();
try {
if (active()) {
handle_exception(state);
}
handler(state);
_update_state(state);
} catch(Exception &e) {
Genode::error(e);
dump(state);
return false;
}
_state.destruct();
return active();
});
}
template <typename T>
@ -100,12 +123,7 @@ class Vmm::Cpu_base
void handle()
{
try {
cpu.handle_signal([this] () { (obj.*member)(); });
} catch(Exception &e) {
Genode::error(e);
cpu.dump();
}
cpu.handle_signal([this] (Vm_state &) { (obj.*member)(); });
}
Signal_handler(Cpu_base & cpu,
@ -207,16 +225,20 @@ class Vmm::Cpu_base
return (r->_encoding > _encoding); }
};
unsigned _vcpu_id;
bool _active { true };
Vm & _vm;
Genode::Vm_connection & _vm_session;
Genode::Heap & _heap;
Signal_handler<Cpu_base> _vm_handler;
Genode::Vm_connection::Exit_config _exit_config { };
Genode::Vm_connection::Vcpu _vm_vcpu;
State & _state;
Genode::Avl_tree<System_register> _reg_tree {};
struct State_container { State &ref; };
unsigned _vcpu_id;
bool _active { true };
Vm & _vm;
Genode::Vm_connection & _vm_session;
Genode::Heap & _heap;
Signal_handler<Cpu_base> _vm_handler;
Genode::Vm_connection::Exit_config _exit_config { };
Genode::Vm_connection::Vcpu _vm_vcpu;
Genode::Avl_tree<System_register> _reg_tree {};
Genode::Constructible<State_container> _state {};
Semaphore _cpu_ready {};
/***********************
@ -227,14 +249,15 @@ class Vmm::Cpu_base
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();
void _handle_startup(State &state);
bool _handle_sys_reg(State &state);
void _handle_brk(State &state);
void _handle_wfi(State &state);
void _handle_sync(State &state);
void _handle_irq(State &state);
void _handle_data_abort(State &state);
void _handle_hyper_call(State &state);
void _update_state(State &state);
public:

View File

@ -1,11 +1,12 @@
/*
* \brief VMM ARM Generic timer device model
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-08-20
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -16,22 +17,28 @@
using Vmm::Generic_timer;
bool Generic_timer::_enabled() {
return Ctrl::Enabled::get(_cpu.state().timer.control); }
bool Generic_timer::_enabled(Vm_state &state)
{
return Ctrl::Enabled::get(state.timer.control);
}
bool Generic_timer::_masked(Vm_state &state)
{
return Ctrl::Imask::get(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); }
bool Generic_timer::_pending(Vm_state &state)
{
return Ctrl::Istatus::get(state.timer.control);
}
void Generic_timer::_handle_timeout(Genode::Duration)
{
_cpu.handle_signal([this] (void) {
if (_enabled() && !_masked()) handle_irq();
_cpu.handle_signal([this](Vm_state &state) {
if (_enabled(state) && !_masked(state))
handle_irq(state);
});
}
@ -45,20 +52,19 @@ Generic_timer::Generic_timer(Genode::Env & env,
_irq(irq),
_cpu(cpu)
{
_cpu.state().timer.irq = true;
_irq.handler(*this);
}
void Generic_timer::schedule_timeout()
void Generic_timer::schedule_timeout(Vm_state &state)
{
if (_pending()) {
handle_irq();
if (_pending(state)) {
handle_irq(state);
return;
}
if (_enabled()) {
Genode::uint64_t usecs = _usecs_left();
if (_enabled(state)) {
Genode::uint64_t usecs = _usecs_left(state);
if (usecs) {
_timeout.schedule(Genode::Microseconds(usecs));
} else _handle_timeout(Genode::Duration(Genode::Microseconds(0)));
@ -72,23 +78,29 @@ void Generic_timer::cancel_timeout()
}
void Generic_timer::handle_irq()
void Generic_timer::handle_irq(Vm_state &state)
{
_irq.assert();
_cpu.state().timer.irq = false;
state.timer.irq = false;
}
void Generic_timer::eoi()
{
_cpu.state().timer.irq = true;
Genode::Vm_state &state = _cpu.state();
state.timer.irq = false;
};
void Generic_timer::dump()
void Generic_timer::dump(Vm_state &state)
{
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));
log(" timer.ctl = ", Hex(state.timer.control, Hex::PREFIX, Hex::PAD));
log(" timer.cmp = ", Hex(state.timer.compare, Hex::PREFIX, Hex::PAD));
}
void Generic_timer::setup_state(Vm_state &state)
{
state.timer.irq = true;
}

View File

@ -1,11 +1,12 @@
/*
* \brief VMM ARM Generic timer device model
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-08-20
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -26,6 +27,8 @@ namespace Vmm {
class Generic_timer;
}
using Genode::Vm_state;
class Vmm::Generic_timer : Gic::Irq::Irq_handler
{
private:
@ -44,12 +47,12 @@ class Vmm::Generic_timer : Gic::Irq::Irq_handler
Genode::uint64_t _ticks_per_ms();
bool _enabled();
bool _masked();
bool _pending();
bool _enabled(Vm_state &state);
bool _masked(Vm_state &state);
bool _pending(Vm_state &state);
void _handle_timeout(Genode::Duration);
Genode::uint64_t _usecs_left();
Genode::uint64_t _usecs_left(Vm_state &state);
public:
@ -58,10 +61,11 @@ class Vmm::Generic_timer : Gic::Irq::Irq_handler
Gic::Irq & irq,
Cpu_base & cpu);
void schedule_timeout();
void schedule_timeout(Vm_state &state);
void cancel_timeout();
void handle_irq();
void dump();
void handle_irq(Vm_state &state);
void dump(Vm_state &state);
static void setup_state(Vm_state &state);
/*****************

View File

@ -1,11 +1,12 @@
/*
* \brief VMM ARM Generic Interrupt Controller v2 device model
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-08-05
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -163,29 +164,30 @@ Gic::Irq & Gic::Gicd_banked::irq(unsigned i)
}
void Gic::Gicd_banked::handle_irq()
void Gic::Gicd_banked::handle_irq(State &state)
{
unsigned i = _cpu.state().irqs.virtual_irq;
unsigned i = state.irqs.virtual_irq;
if (i > MAX_IRQ) return;
irq(i).deassert();
_cpu.state().irqs.virtual_irq = SPURIOUS;
state.irqs.virtual_irq = SPURIOUS;
}
bool Gic::Gicd_banked::pending_irq()
bool Gic::Gicd_banked::pending_irq(State &state)
{
Genode::Mutex::Guard guard(big_gic_lock());
if (_cpu.state().irqs.virtual_irq != SPURIOUS) return true;
if (state.irqs.virtual_irq != SPURIOUS)
return true;
Irq * i = _gic._pending_list.highest_enabled();
Irq * j = _pending_list.highest_enabled();
Irq * n = j;
if (i && ((j && j->priority() > i->priority()) || !j)) n = i;
if (!n) return false;
_cpu.state().irqs.virtual_irq = n->number();
state.irqs.virtual_irq = n->number();
n->activate();
return true;
}
@ -200,8 +202,6 @@ Gic::Gicd_banked::Gicd_banked(Cpu_base & 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 = SPURIOUS;
_cpu.state().irqs.virtual_irq = SPURIOUS;
if (gic.version() >= 3) {
_rdist.construct(GICR_MMIO_START +
@ -211,6 +211,12 @@ Gic::Gicd_banked::Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus)
}
}
void Gic::Gicd_banked::setup_state(State &state)
{
state.irqs.last_irq = SPURIOUS;
state.irqs.virtual_irq = SPURIOUS;
}
Register Gic::Irq_reg::read(Address_range & access, Cpu & cpu)
{

View File

@ -1,11 +1,12 @@
/*
* \brief VMM ARM Generic Interrupt Controller device model
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-08-05
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -15,6 +16,7 @@
#define _SRC__SERVER__VMM__GIC_H_
#include <mmio.h>
#include <state.h>
#include <base/env.h>
#include <drivers/defs/arm_v7.h>
@ -134,9 +136,10 @@ class Vmm::Gic : public Vmm::Mmio_device
{
public:
Irq & irq(unsigned num);
void handle_irq();
bool pending_irq();
Irq & irq(unsigned num);
void handle_irq(State &state);
bool pending_irq(State &state);
static void setup_state(State &state);
Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus);

View File

@ -1,11 +1,12 @@
/*
* \brief VMM mmio abstractions
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-07-18
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -79,12 +80,10 @@ void Mmio_device::write(Address_range & access, Cpu & cpu, Register value)
void Mmio_device::add(Mmio_register & reg) { _registers.add(reg); }
void Vmm::Mmio_bus::handle_memory_access(Vmm::Cpu & cpu)
void Vmm::Mmio_bus::handle_memory_access(State &state, Vmm::Cpu &cpu)
{
using namespace Genode;
Cpu::State & state = cpu.state();
struct Iss : Cpu::Esr
{
struct Write : Bitfield<6, 1> {};

View File

@ -1,11 +1,12 @@
/*
* \brief VMM mmio abstractions
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-07-18
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -15,6 +16,7 @@
#define _SRC__SERVER__VMM__MMIO_H_
#include <address_space.h>
#include <state.h>
namespace Vmm {
class Cpu;
@ -98,7 +100,7 @@ class Vmm::Mmio_device : public Vmm::Address_range
struct Vmm::Mmio_bus : Vmm::Address_space
{
void handle_memory_access(Cpu & cpu);
void handle_memory_access(State &state, Cpu &cpu);
};
#endif /* _SRC__SERVER__VMM__MMIO_H_ */

View File

@ -1,11 +1,12 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-07-18
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -13,13 +14,14 @@
#include <cpu.h>
#include <vm.h>
#include <psci.h>
#include <state.h>
using Vmm::Cpu_base;
using Vmm::Cpu;
using Vmm::Gic;
using namespace Genode;
addr_t Cpu_base::State::reg(addr_t idx) const
addr_t Vmm::State::reg(addr_t idx) const
{
if (idx > 15) return 0;
@ -29,7 +31,7 @@ addr_t Cpu_base::State::reg(addr_t idx) const
}
void Cpu_base::State::reg(addr_t idx, addr_t v)
void Vmm::State::reg(addr_t idx, addr_t v)
{
if (idx > 15) return;
@ -62,29 +64,30 @@ Cpu_base::System_register::Iss::mask_encoding(access_t v)
}
void Cpu_base::_handle_brk()
void Cpu_base::_handle_brk(State &)
{
error(__func__, " not implemented yet");
}
void Cpu_base::handle_exception()
void Cpu_base::handle_exception(State & state)
{
/* check exception reason */
switch (_state.cpu_exception) {
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;
case Cpu::IRQ: _handle_irq(state); break;
case Cpu::TRAP: _handle_sync(state); break;
case VCPU_EXCEPTION_STARTUP: _handle_startup(state); break;
default:
throw Exception("Curious exception ",
_state.cpu_exception, " occured");
state.cpu_exception, " occured");
}
_state.cpu_exception = Cpu::NO_EXCEPTION;
state.cpu_exception = Cpu::NO_EXCEPTION;
}
void Cpu_base::dump()
void Cpu_base::dump(State &state)
{
auto lambda = [] (unsigned i) {
switch (i) {
@ -100,37 +103,37 @@ void Cpu_base::dump()
log("VM state (", _active ? "active" : "inactive", ") :");
for (unsigned i = 0; i < 13; i++) {
log(" r", i, " = ",
Hex(_state.reg(i), Hex::PREFIX, Hex::PAD));
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));
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));
Hex(state.mode[i].sp, Hex::PREFIX, Hex::PAD));
log(" lr_", lambda(i), " = ",
Hex(_state.mode[i].lr, Hex::PREFIX, Hex::PAD));
Hex(state.mode[i].lr, Hex::PREFIX, Hex::PAD));
log(" spsr_", lambda(i), " = ",
Hex(_state.mode[i].spsr, Hex::PREFIX, Hex::PAD));
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();
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(state);
}
void Cpu_base::initialize_boot(addr_t ip, addr_t dtb)
void Cpu_base::initialize_boot(State &state, addr_t ip, addr_t dtb)
{
state().reg(1, 0xffffffff); /* invalid machine type */
state().reg(2, dtb);
state().ip = ip;
state.reg(1, 0xffffffff); /* invalid machine type */
state.reg(2, dtb);
state.ip = ip;
}
@ -153,6 +156,13 @@ addr_t Cpu::Ccsidr::read() const
return 0;
}
void Cpu::setup_state(State &state)
{
state.cpsr = 0x93; /* el1 mode and IRQs disabled */
state.sctrl = 0xc50078;
state.vmpidr = (1UL << 31) | cpu_id();
}
Cpu::Cpu(Vm & vm,
Vm_connection & vm_session,
@ -184,7 +194,4 @@ Cpu::Cpu(Vm & vm,
_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;
_state.vmpidr = (1UL << 31) | cpu_id();
}

View File

@ -1,11 +1,12 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-07-18
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -40,9 +41,11 @@ class Vmm::Cpu : public Vmm::Cpu_base
DATA_ABORT,
IRQ,
FIQ,
TRAP
TRAP,
};
void setup_state(State & state) override;
private:
struct Ccsidr : System_register

View File

@ -1,11 +1,12 @@
/*
* \brief VMM ARM Generic timer device model
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-08-20
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -28,14 +29,14 @@ Genode::uint64_t Generic_timer::_ticks_per_ms()
}
Genode::uint64_t Generic_timer::_usecs_left()
Genode::uint64_t Generic_timer::_usecs_left(Vm_state &state)
{
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,
count -= state.timer.offset;
if (count > state.timer.compare) return 0;
return Genode::timer_ticks_to_us(state.timer.compare - count,
_ticks_per_ms());
}

View File

@ -1,16 +1,18 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-07-18
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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 <state.h>
#include <vm.h>
#include <psci.h>
@ -19,14 +21,14 @@ using Vmm::Cpu;
using Vmm::Gic;
using namespace Genode;
addr_t Cpu_base::State::reg(addr_t idx) const
addr_t Vmm::State::reg(addr_t idx) const
{
if (idx > 30) return 0;
return r[idx];
}
void Cpu_base::State::reg(addr_t idx, addr_t v)
void Vmm::State::reg(addr_t idx, addr_t v)
{
if (idx > 30) return;
r[idx] = v;
@ -58,40 +60,41 @@ Cpu_base::System_register::Iss::mask_encoding(access_t v)
}
void Cpu_base::_handle_brk()
void Cpu_base::_handle_brk(State & state)
{
addr_t offset = 0x0;
if (!(_state.pstate & 0b100)) {
if (!(state.pstate & 0b100)) {
offset = 0x400;
} else if (_state.pstate & 0b1) {
} else if (state.pstate & 0b1) {
offset = 0x200;
}
/* only the below 32-bit of system register ESR_EL2 and PSTATE are used */
_state.esr_el1 = (uint32_t)_state.esr_el2;
_state.spsr_el1 = (uint32_t)_state.pstate;
_state.elr_el1 = _state.ip;
_state.ip = _state.vbar_el1 + offset;
_state.pstate = 0b1111000101;
state.esr_el1 = (uint32_t)state.esr_el2;
state.spsr_el1 = (uint32_t)state.pstate;
state.elr_el1 = state.ip;
state.ip = state.vbar_el1 + offset;
state.pstate = 0b1111000101;
}
void Cpu_base::handle_exception()
void Cpu_base::handle_exception(State &state)
{
/* check exception reason */
switch (_state.exception_type) {
switch (state.exception_type) {
case Cpu::NO_EXCEPTION: break;
case Cpu::AARCH64_IRQ: _handle_irq(); break;
case Cpu::AARCH64_SYNC: _handle_sync(); break;
case Cpu::AARCH64_IRQ: _handle_irq(state); break;
case Cpu::AARCH64_SYNC: _handle_sync(state); break;
case VCPU_EXCEPTION_STARTUP: _handle_startup(state); break;
default:
throw Exception("Curious exception ",
_state.exception_type, " occured");
state.exception_type, " occured");
}
_state.exception_type = Cpu::NO_EXCEPTION;
state.exception_type = Cpu::NO_EXCEPTION;
}
void Cpu_base::dump()
void Cpu_base::dump(State &state)
{
auto lambda = [] (addr_t exc) {
switch (exc) {
@ -110,22 +113,24 @@ void Cpu_base::dump()
log("VM state (", _active ? "active" : "inactive", ") :");
for (unsigned i = 0; i < 31; i++) {
log(" r", i, " = ",
Hex(_state.r[i], Hex::PREFIX, Hex::PAD));
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();
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(state);
}
addr_t Cpu::Ccsidr::read() const
{
State & state = cpu.state();
struct Clidr : Genode::Register<32>
{
enum Cache_entry {
@ -198,10 +203,25 @@ void Cpu::Icc_sgi1r_el1::write(addr_t v)
};
void Cpu_base::initialize_boot(addr_t ip, addr_t dtb)
void Cpu_base::initialize_boot(State &state, addr_t ip, addr_t dtb)
{
state().reg(0, dtb);
state().ip = ip;
state.reg(0, dtb);
state.ip = ip;
}
void Cpu::setup_state(State &state)
{
_sr_id_aa64isar0_el1.write(state.id_aa64isar0_el1);
_sr_id_aa64isar1_el1.write(state.id_aa64isar1_el1);
_sr_id_aa64mmfr0_el1.write(state.id_aa64mmfr0_el1);
_sr_id_aa64mmfr1_el1.write(state.id_aa64mmfr1_el1);
_sr_id_aa64mmfr2_el1.write(state.id_aa64mmfr2_el1);
_sr_id_aa64pfr0_el1.write( _sr_id_aa64pfr0_el1.reset_value(
state.id_aa64pfr0_el1));
_sr_clidr_el1.write(state.clidr_el1);
state.pstate = 0b1111000101; /* el1 mode and IRQs disabled */
state.vmpidr_el2 = cpu_id();
}
@ -218,20 +238,20 @@ Cpu::Cpu(Vm & vm,
_sr_id_aa64afr1_el1 (3, 0, 0, 5, 5, "ID_AA64AFR1_EL1", false, 0x0, _reg_tree),
_sr_id_aa64dfr0_el1 (3, 0, 0, 5, 0, "ID_AA64DFR0_EL1", false, 0x6, _reg_tree),
_sr_id_aa64dfr1_el1 (3, 0, 0, 5, 1, "ID_AA64DFR1_EL1", false, 0x0, _reg_tree),
_sr_id_aa64isar0_el1(3, 0, 0, 6, 0, "ID_AA64ISAR0_EL1", false, _state.id_aa64isar0_el1, _reg_tree),
_sr_id_aa64isar1_el1(3, 0, 0, 6, 1, "ID_AA64ISAR1_EL1", false, _state.id_aa64isar1_el1, _reg_tree),
_sr_id_aa64mmfr0_el1(3, 0, 0, 7, 0, "ID_AA64MMFR0_EL1", false, _state.id_aa64mmfr0_el1, _reg_tree),
_sr_id_aa64mmfr1_el1(3, 0, 0, 7, 1, "ID_AA64MMFR1_EL1", false, _state.id_aa64mmfr1_el1, _reg_tree),
_sr_id_aa64mmfr2_el1(3, 0, 0, 7, 2, "ID_AA64MMFR2_EL1", false, _state.id_aa64mmfr2_el1, _reg_tree),
_sr_id_aa64pfr0_el1 (_state.id_aa64pfr0_el1, _reg_tree),
_sr_id_aa64isar0_el1(3, 0, 0, 6, 0, "ID_AA64ISAR0_EL1", false, 0x0, _reg_tree),
_sr_id_aa64isar1_el1(3, 0, 0, 6, 1, "ID_AA64ISAR1_EL1", false, 0x0, _reg_tree),
_sr_id_aa64mmfr0_el1(3, 0, 0, 7, 0, "ID_AA64MMFR0_EL1", false, 0x0, _reg_tree),
_sr_id_aa64mmfr1_el1(3, 0, 0, 7, 1, "ID_AA64MMFR1_EL1", false, 0x0, _reg_tree),
_sr_id_aa64mmfr2_el1(3, 0, 0, 7, 2, "ID_AA64MMFR2_EL1", false, 0x0, _reg_tree),
_sr_id_aa64pfr0_el1 (0x0, _reg_tree),
_sr_id_aa64pfr1_el1 (3, 0, 0, 4, 1, "ID_AA64PFR1_EL1", false, 0x0, _reg_tree),
_sr_id_aa64zfr0_el1 (3, 0, 0, 4, 4, "ID_AA64ZFR0_EL1", false, 0x0, _reg_tree),
_sr_aidr_el1 (3, 0, 1, 0, 7, "AIDR_EL1", false, 0x0, _reg_tree),
_sr_revidr_el1 (3, 0, 0, 0, 6, "REVIDR_EL1", false, 0x0, _reg_tree),
_sr_clidr_el1 (3, 0, 1, 0, 1, "CLIDR_EL1", false, _state.clidr_el1, _reg_tree),
_sr_clidr_el1 (3, 0, 1, 0, 1, "CLIDR_EL1", false, 0x0, _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_ccsidr_el1 (_sr_csselr_el1, *this, _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),
@ -241,7 +261,4 @@ Cpu::Cpu(Vm & vm,
_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)
{
_state.pstate = 0b1111000101; /* el1 mode and IRQs disabled */
_state.vmpidr_el2 = cpu_id();
}
{ }

View File

@ -1,11 +1,12 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-07-18
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -40,9 +41,11 @@ class Vmm::Cpu : public Vmm::Cpu_base
AARCH32_IRQ = 0x680,
AARCH32_FIQ = 0x700,
AARCH32_SERROR = 0x780,
NO_EXCEPTION = 0xffff
NO_EXCEPTION = 0xffff,
};
void setup_state(State & state) override;
private:
class Id_aa64pfr0 : public System_register,
@ -57,7 +60,9 @@ class Vmm::Cpu : public Vmm::Cpu_base
struct Ras : Bitfield<28, 4> { enum { NOT_IMPLEMENTED }; };
struct Sve : Bitfield<32, 4> { enum { NOT_IMPLEMENTED }; };
access_t _reset_value(access_t orig)
public:
access_t reset_value(access_t orig)
{
El0::set(orig, El0::AARCH64_ONLY);
El1::set(orig, El1::AARCH64_ONLY);
@ -68,24 +73,22 @@ class Vmm::Cpu : public Vmm::Cpu_base
return orig;
}
public:
Id_aa64pfr0(Genode::uint64_t id_aa64pfr0,
Genode::Avl_tree<System_register> & tree)
: System_register(3, 0, 0, 4, 0, "ID_AA64PFR0_EL1", false,
_reset_value(id_aa64pfr0), tree) {}
reset_value(id_aa64pfr0), tree) {}
};
struct Ccsidr : System_register
{
System_register & csselr;
State & state;
Cpu & cpu;
Ccsidr(System_register &csselr,
State & state,
Cpu & cpu,
Genode::Avl_tree<System_register> & tree)
: System_register(3, 0, 1, 0, 0, "CCSIDR_EL1", false, 0x0, tree),
csselr(csselr), state(state) {}
csselr(csselr), cpu(cpu) {}
virtual Genode::addr_t read() const override;
};

View File

@ -1,11 +1,12 @@
/*
* \brief VMM ARM Generic timer device model
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-08-20
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -28,12 +29,12 @@ Genode::uint64_t Generic_timer::_ticks_per_ms()
}
Genode::uint64_t Generic_timer::_usecs_left()
Genode::uint64_t Generic_timer::_usecs_left(Vm_state &state)
{
Genode::uint64_t count;
asm volatile("mrs %0, cntpct_el0" : "=r" (count));
count -= _cpu.state().timer.offset;
if (count > _cpu.state().timer.compare) return 0;
return Genode::timer_ticks_to_us(_cpu.state().timer.compare - count,
count -= state.timer.offset;
if (count > state.timer.compare) return 0;
return Genode::timer_ticks_to_us(state.timer.compare - count,
_ticks_per_ms());
}

View File

@ -0,0 +1,32 @@
/*
* \brief vCPU state
* \author Benjamin Lamowski
* \date 2023-06-22
*/
/*
* Copyright (C) 2023 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__STATE_H_
#define _SRC__SERVER__VMM__STATE_H_
#include <cpu/vm_state_virtualization.h>
using Genode::addr_t;
using Genode::Vm_state;
namespace Genode { struct Vcpu_state : Vm_state { }; };
namespace Vmm {
struct State : Genode::Vcpu_state
{
addr_t reg(addr_t idx) const;
void reg(addr_t idx, addr_t v);
};
}
#endif /* _SRC__SERVER__VMM__STATE_H_ */

View File

@ -1,11 +1,12 @@
/*
* \brief VMM example for ARMv8 virtualization
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-07-18
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -161,10 +162,7 @@ Vm::Vm(Genode::Env & env, Heap & heap, Config & config)
Genode::log("Start virtual machine ...");
Cpu & cpu = boot_cpu();
cpu.initialize_boot(_ram.base() + KERNEL_OFFSET,
_ram.base() + _dtb_offset());
cpu.run();
boot_cpu();
};

View File

@ -1,11 +1,12 @@
/*
* \brief VMM example for ARMv8 virtualization
* \author Stefan Kalkowski
* \author Benjamin Lamowski
* \date 2019-07-18
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2023 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.
@ -96,6 +97,14 @@ class Vmm::Vm
for (Cpu_entry * ce = _cpu_list.first(); ce; ce = ce->next())
func(ce->cpu);
}
addr_t dtb_addr() {
return _ram.base() + _dtb_offset();
}
addr_t kernel_addr() {
return _ram.base() + KERNEL_OFFSET;
}
};
#endif /* _SRC__SERVER__VMM__VM_H_ */