2015-02-19 14:50:27 +01:00
|
|
|
/*
|
|
|
|
* \brief VMM example for ARM Virtualization
|
|
|
|
* \author Stefan Kalkowski
|
|
|
|
* \date 2014-07-08
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2017-02-20 13:23:52 +01:00
|
|
|
* Copyright (C) 2014-2017 Genode Labs GmbH
|
2015-02-19 14:50:27 +01:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 13:23:52 +01:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2015-02-19 14:50:27 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Genode includes */
|
2017-01-06 12:48:59 +01:00
|
|
|
#include <base/attached_ram_dataspace.h>
|
|
|
|
#include <base/attached_rom_dataspace.h>
|
|
|
|
#include <base/component.h>
|
2015-02-19 14:50:27 +01:00
|
|
|
#include <base/exception.h>
|
2018-09-26 15:53:18 +02:00
|
|
|
#include <base/heap.h>
|
2016-10-10 16:22:43 +02:00
|
|
|
#include <base/log.h>
|
|
|
|
#include <cpu/cpu_state.h>
|
2019-04-10 12:43:17 +02:00
|
|
|
#include <drivers/defs/arm_v7.h>
|
2016-10-10 16:22:43 +02:00
|
|
|
#include <os/ring_buffer.h>
|
2015-02-19 14:50:27 +01:00
|
|
|
#include <terminal_session/connection.h>
|
2016-10-10 16:22:43 +02:00
|
|
|
#include <timer_session/connection.h>
|
2015-02-19 14:50:27 +01:00
|
|
|
#include <util/avl_tree.h>
|
|
|
|
#include <util/mmio.h>
|
2016-10-10 16:22:43 +02:00
|
|
|
#include <vm_session/connection.h>
|
|
|
|
|
2019-03-21 13:59:30 +01:00
|
|
|
#include <cpu/vm_state_virtualization.h>
|
2015-07-02 11:21:20 +02:00
|
|
|
#include <board.h>
|
2015-02-19 14:50:27 +01:00
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
|
2015-02-19 14:50:27 +01:00
|
|
|
struct State : Genode::Vm_state
|
|
|
|
{
|
|
|
|
Genode::uint32_t midr;
|
|
|
|
Genode::uint32_t mpidr;
|
|
|
|
Genode::uint32_t ctr;
|
|
|
|
Genode::uint32_t ccsidr;
|
|
|
|
Genode::uint32_t clidr;
|
|
|
|
Genode::uint32_t pfr0;
|
|
|
|
Genode::uint32_t mmfr0;
|
|
|
|
Genode::uint32_t isar0;
|
|
|
|
Genode::uint32_t isar3;
|
|
|
|
Genode::uint32_t isar4;
|
|
|
|
Genode::uint32_t csselr;
|
|
|
|
Genode::uint32_t actrl;
|
|
|
|
|
|
|
|
class Invalid_register : Genode::Exception {};
|
|
|
|
|
|
|
|
struct Gp_register { Genode::addr_t r[16]; };
|
|
|
|
|
|
|
|
struct Psr : Genode::Register<32>
|
|
|
|
{
|
|
|
|
struct Mode : Bitfield<0,5>
|
|
|
|
{
|
|
|
|
enum {
|
|
|
|
USR = 16,
|
|
|
|
FIQ = 17,
|
|
|
|
IRQ = 18,
|
|
|
|
SVC = 19,
|
|
|
|
ABORT = 23,
|
|
|
|
UND = 27
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
static int mode_offset(access_t v)
|
|
|
|
{
|
|
|
|
switch(Mode::get(v)) {
|
|
|
|
case Mode::FIQ: return Mode_state::Mode::FIQ;
|
|
|
|
case Mode::IRQ: return Mode_state::Mode::IRQ;
|
|
|
|
case Mode::SVC: return Mode_state::Mode::SVC;
|
|
|
|
case Mode::ABORT: return Mode_state::Mode::ABORT;
|
|
|
|
case Mode::UND: return Mode_state::Mode::UND;
|
|
|
|
default: return -1;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Genode::addr_t * r(unsigned i)
|
|
|
|
{
|
|
|
|
unsigned mo = Psr::mode_offset(cpsr);
|
|
|
|
switch (i) {
|
|
|
|
case 13: return (mo < 0) ? &sp : &(mode[mo].sp);
|
|
|
|
case 14: return (mo < 0) ? &lr : &(mode[mo].lr);
|
|
|
|
default: return &(reinterpret_cast<Gp_register*>(this)->r[i]);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Ram {
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
Genode::addr_t const _base;
|
|
|
|
Genode::size_t const _size;
|
|
|
|
Genode::addr_t const _local;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Ram(Genode::addr_t const addr, Genode::size_t const sz,
|
|
|
|
Genode::addr_t const local)
|
|
|
|
: _base(addr), _size(sz), _local(local) { }
|
|
|
|
|
|
|
|
Genode::addr_t base() const { return _base; }
|
|
|
|
Genode::size_t size() const { return _size; }
|
|
|
|
Genode::addr_t local() const { return _local; }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Vm {
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
enum {
|
|
|
|
RAM_ADDRESS = 0x80000000,
|
|
|
|
MACH_TYPE = 2272, /* ARNDALE = 4274; VEXPRESS = 2272 */
|
|
|
|
KERNEL_OFFSET = 0x8000,
|
|
|
|
DTB_OFFSET = 64 * 1024 * 1024,
|
|
|
|
};
|
|
|
|
|
2018-09-26 15:53:18 +02:00
|
|
|
Genode::Vm_connection _vm;
|
2015-02-19 14:50:27 +01:00
|
|
|
Genode::Attached_rom_dataspace _kernel_rom;
|
|
|
|
Genode::Attached_rom_dataspace _dtb_rom;
|
|
|
|
Genode::Attached_ram_dataspace _vm_ram;
|
|
|
|
Ram _ram;
|
2018-09-26 15:53:18 +02:00
|
|
|
Genode::Heap _heap;
|
|
|
|
Genode::Vm_session::Vcpu_id _vcpu_id;
|
2017-01-06 12:48:59 +01:00
|
|
|
State & _state;
|
2015-02-19 14:50:27 +01:00
|
|
|
bool _active = true;
|
|
|
|
|
|
|
|
void _load_kernel()
|
|
|
|
{
|
|
|
|
Genode::memcpy((void*)(_ram.local() + KERNEL_OFFSET),
|
|
|
|
_kernel_rom.local_addr<void>(),
|
|
|
|
_kernel_rom.size());
|
2017-01-06 12:48:59 +01:00
|
|
|
_state.ip = _ram.base() + KERNEL_OFFSET;
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void _load_dtb()
|
|
|
|
{
|
|
|
|
Genode::memcpy((void*)(_ram.local() + DTB_OFFSET),
|
|
|
|
_dtb_rom.local_addr<void>(),
|
|
|
|
_dtb_rom.size());
|
2017-01-06 12:48:59 +01:00
|
|
|
_state.r2 = _ram.base() + DTB_OFFSET;
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
class Exception : Genode::Exception
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
enum { BUF_SIZE = 128 };
|
Follow practices suggested by "Effective C++"
The patch adjust the code of the base, base-<kernel>, and os repository.
To adapt existing components to fix violations of the best practices
suggested by "Effective C++" as reported by the -Weffc++ compiler
argument. The changes follow the patterns outlined below:
* A class with virtual functions can no longer publicly inherit base
classed without a vtable. The inherited object may either be moved
to a member variable, or inherited privately. The latter would be
used for classes that inherit 'List::Element' or 'Avl_node'. In order
to enable the 'List' and 'Avl_tree' to access the meta data, the
'List' must become a friend.
* Instead of adding a virtual destructor to abstract base classes,
we inherit the new 'Interface' class, which contains a virtual
destructor. This way, single-line abstract base classes can stay
as compact as they are now. The 'Interface' utility resides in
base/include/util/interface.h.
* With the new warnings enabled, all member variables must be explicitly
initialized. Basic types may be initialized with '='. All other types
are initialized with braces '{ ... }' or as class initializers. If
basic types and non-basic types appear in a row, it is nice to only
use the brace syntax (also for basic types) and align the braces.
* If a class contains pointers as members, it must now also provide a
copy constructor and assignment operator. In the most cases, one
would make them private, effectively disallowing the objects to be
copied. Unfortunately, this warning cannot be fixed be inheriting
our existing 'Noncopyable' class (the compiler fails to detect that
the inheriting class cannot be copied and still gives the error).
For now, we have to manually add declarations for both the copy
constructor and assignment operator as private class members. Those
declarations should be prepended with a comment like this:
/*
* Noncopyable
*/
Thread(Thread const &);
Thread &operator = (Thread const &);
In the future, we should revisit these places and try to replace
the pointers with references. In the presence of at least one
reference member, the compiler would no longer implicitly generate
a copy constructor. So we could remove the manual declaration.
Issue #465
2017-12-21 15:42:15 +01:00
|
|
|
char _buf[BUF_SIZE];
|
2015-02-19 14:50:27 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Exception(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
Follow practices suggested by "Effective C++"
The patch adjust the code of the base, base-<kernel>, and os repository.
To adapt existing components to fix violations of the best practices
suggested by "Effective C++" as reported by the -Weffc++ compiler
argument. The changes follow the patterns outlined below:
* A class with virtual functions can no longer publicly inherit base
classed without a vtable. The inherited object may either be moved
to a member variable, or inherited privately. The latter would be
used for classes that inherit 'List::Element' or 'Avl_node'. In order
to enable the 'List' and 'Avl_tree' to access the meta data, the
'List' must become a friend.
* Instead of adding a virtual destructor to abstract base classes,
we inherit the new 'Interface' class, which contains a virtual
destructor. This way, single-line abstract base classes can stay
as compact as they are now. The 'Interface' utility resides in
base/include/util/interface.h.
* With the new warnings enabled, all member variables must be explicitly
initialized. Basic types may be initialized with '='. All other types
are initialized with braces '{ ... }' or as class initializers. If
basic types and non-basic types appear in a row, it is nice to only
use the brace syntax (also for basic types) and align the braces.
* If a class contains pointers as members, it must now also provide a
copy constructor and assignment operator. In the most cases, one
would make them private, effectively disallowing the objects to be
copied. Unfortunately, this warning cannot be fixed be inheriting
our existing 'Noncopyable' class (the compiler fails to detect that
the inheriting class cannot be copied and still gives the error).
For now, we have to manually add declarations for both the copy
constructor and assignment operator as private class members. Those
declarations should be prepended with a comment like this:
/*
* Noncopyable
*/
Thread(Thread const &);
Thread &operator = (Thread const &);
In the future, we should revisit these places and try to replace
the pointers with references. In the presence of at least one
reference member, the compiler would no longer implicitly generate
a copy constructor. So we could remove the manual declaration.
Issue #465
2017-12-21 15:42:15 +01:00
|
|
|
Genode::String_console sc(_buf, BUF_SIZE);
|
|
|
|
sc.vprintf(fmt, args);
|
2015-02-19 14:50:27 +01:00
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
Exception() : Exception("undefined") {}
|
|
|
|
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
void print() { Genode::error(Genode::Cstring(_buf)); }
|
2015-02-19 14:50:27 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Vm(const char *kernel, const char *dtb, Genode::size_t const ram_size,
|
2018-09-26 15:53:18 +02:00
|
|
|
Genode::Vm_handler_base &handler, Genode::Env & env)
|
|
|
|
: _vm(env),
|
2017-01-06 12:48:59 +01:00
|
|
|
_kernel_rom(env, kernel),
|
|
|
|
_dtb_rom(env, dtb),
|
|
|
|
_vm_ram(env.ram(), env.rm(), ram_size, Genode::UNCACHED),
|
2015-02-19 14:50:27 +01:00
|
|
|
_ram(RAM_ADDRESS, ram_size, (Genode::addr_t)_vm_ram.local_addr<void>()),
|
2018-09-26 15:53:18 +02:00
|
|
|
_heap(env.ram(), env.rm()),
|
|
|
|
_vcpu_id(_vm.create_vcpu(_heap, env, handler)),
|
|
|
|
_state(*((State*)env.rm().attach(_vm.cpu_state(_vcpu_id))))
|
2015-02-19 14:50:27 +01:00
|
|
|
{
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
Genode::log("ram is at ",
|
|
|
|
Genode::Hex(Genode::Dataspace_client(_vm_ram.cap()).phys_addr()));
|
|
|
|
|
2018-09-26 15:53:18 +02:00
|
|
|
_vm.attach(_vm_ram.cap(), RAM_ADDRESS);
|
|
|
|
_vm.attach_pic(0x2C002000);
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void start()
|
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
Genode::memset((void*)&_state, 0, sizeof(Genode::Cpu_state_modes));
|
2015-02-19 14:50:27 +01:00
|
|
|
_load_kernel();
|
|
|
|
_load_dtb();
|
2017-01-06 12:48:59 +01:00
|
|
|
_state.r1 = MACH_TYPE;
|
|
|
|
_state.cpsr = 0x93; /* SVC mode and IRQs disabled */
|
2015-02-19 14:50:27 +01:00
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
_state.timer_ctrl = 0;
|
|
|
|
_state.timer_val = 0;
|
|
|
|
_state.timer_irq = false;
|
2015-02-19 14:50:27 +01:00
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
_state.gic_hcr = 0b101;
|
|
|
|
_state.gic_vmcr = 0x4c0000;
|
|
|
|
_state.gic_apr = 0;
|
|
|
|
_state.gic_lr[0] = 0;
|
|
|
|
_state.gic_lr[1] = 0;
|
|
|
|
_state.gic_lr[2] = 0;
|
|
|
|
_state.gic_lr[3] = 0;
|
2015-02-19 14:50:27 +01:00
|
|
|
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
Genode::log("ready to run");
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
2018-09-26 15:53:18 +02:00
|
|
|
void run() { if (_active) _vm.run(_vcpu_id); }
|
|
|
|
void pause() { _vm.pause(_vcpu_id); }
|
2015-02-19 14:50:27 +01:00
|
|
|
void wait_for_interrupt() { _active = false; }
|
|
|
|
void interrupt() { _active = true; }
|
|
|
|
bool active() { return _active; }
|
|
|
|
|
|
|
|
void dump()
|
|
|
|
{
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
const char * const modes[] =
|
|
|
|
{ "und", "svc", "abt", "irq", "fiq" };
|
|
|
|
const char * const exc[] =
|
|
|
|
{ "nope", "reset", "undefined", "svc", "pf_abort",
|
|
|
|
"data_abort", "irq", "fiq", "trap" };
|
|
|
|
|
2016-10-10 16:22:43 +02:00
|
|
|
log("Cpu state:");
|
2017-01-06 12:48:59 +01:00
|
|
|
log(" r0 = ", Hex(_state.r0, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r1 = ", Hex(_state.r1, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r2 = ", Hex(_state.r2, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r3 = ", Hex(_state.r3, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r4 = ", Hex(_state.r4, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r5 = ", Hex(_state.r5, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r6 = ", Hex(_state.r6, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r7 = ", Hex(_state.r7, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r8 = ", Hex(_state.r8, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r9 = ", Hex(_state.r9, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r10 = ", Hex(_state.r10, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r11 = ", Hex(_state.r11, Hex::PREFIX, Hex::PAD));
|
|
|
|
log(" r12 = ", Hex(_state.r12, 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));
|
2015-02-19 14:50:27 +01:00
|
|
|
for (unsigned i = 0;
|
|
|
|
i < State::Mode_state::MAX; i++) {
|
2016-10-10 16:22:43 +02:00
|
|
|
log(" sp_", modes[i], " = ",
|
2017-01-06 12:48:59 +01:00
|
|
|
Hex(_state.mode[i].sp, Hex::PREFIX, Hex::PAD));
|
2016-10-10 16:22:43 +02:00
|
|
|
log(" lr_", modes[i], " = ",
|
2017-01-06 12:48:59 +01:00
|
|
|
Hex(_state.mode[i].lr, Hex::PREFIX, Hex::PAD));
|
2016-10-10 16:22:43 +02:00
|
|
|
log(" spsr_", modes[i], " = ",
|
2017-01-06 12:48:59 +01:00
|
|
|
Hex(_state.mode[i].spsr, Hex::PREFIX, Hex::PAD));
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
2017-01-06 12:48:59 +01:00
|
|
|
log(" exception = ", exc[_state.cpu_exception]);
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
State & state() const { return _state; }
|
2015-02-19 14:50:27 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
|
2015-02-19 14:50:27 +01:00
|
|
|
class Vmm
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
template <typename T>
|
2018-09-26 15:53:18 +02:00
|
|
|
struct Signal_handler : Genode::Vm_handler<Signal_handler<T>>
|
2017-01-06 12:48:59 +01:00
|
|
|
{
|
2018-09-26 15:53:18 +02:00
|
|
|
using Base = Genode::Vm_handler<Signal_handler<T>>;
|
2017-01-06 12:48:59 +01:00
|
|
|
|
|
|
|
Vmm & vmm;
|
|
|
|
T & obj;
|
|
|
|
void (T::*member)();
|
|
|
|
|
|
|
|
void handle()
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
vmm.handle_vm([this] () { (obj.*member)(); });
|
|
|
|
} catch(Vm::Exception &e) {
|
|
|
|
e.print();
|
|
|
|
vmm.vm().dump();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Signal_handler(Vmm & vmm, Genode::Entrypoint &ep, T & o,
|
|
|
|
void (T::*f)())
|
|
|
|
: Base(ep, *this, &Signal_handler::handle),
|
|
|
|
vmm(vmm), obj(o), member(f) {}
|
|
|
|
};
|
|
|
|
|
2015-02-19 14:50:27 +01:00
|
|
|
struct Hsr : Genode::Register<32>
|
|
|
|
{
|
|
|
|
struct Ec : Bitfield<26, 6>
|
|
|
|
{
|
|
|
|
enum {
|
|
|
|
WFI = 0x1,
|
|
|
|
CP15 = 0x3,
|
|
|
|
HVC = 0x12,
|
|
|
|
DA = 0x24
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
class Coprocessor
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
|
|
|
|
struct Iss : Hsr
|
|
|
|
{
|
|
|
|
struct Direction : Bitfield<0, 1> {};
|
|
|
|
struct Crm : Bitfield<1, 4> {};
|
|
|
|
struct Register : Bitfield<5, 4> {};
|
|
|
|
struct Crn : Bitfield<10, 4> {};
|
|
|
|
struct Opcode1 : Bitfield<14, 3> {};
|
|
|
|
struct Opcode2 : Bitfield<17, 3> {};
|
|
|
|
|
|
|
|
static access_t value(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;
|
|
|
|
};
|
|
|
|
|
|
|
|
static access_t mask_encoding(access_t v)
|
|
|
|
{
|
|
|
|
return Crm::masked(v) |
|
|
|
|
Crn::masked(v) |
|
|
|
|
Opcode1::masked(v) |
|
|
|
|
Opcode2::masked(v);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Register : public Genode::Avl_node<Register>
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
const Iss::access_t _encoding;
|
|
|
|
const char *_name;
|
|
|
|
const bool _writeable;
|
|
|
|
Genode::uint32_t State::*_r;
|
|
|
|
const Genode::addr_t _init_value;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Register(unsigned crn, unsigned op1,
|
|
|
|
unsigned crm, unsigned op2,
|
|
|
|
const char * name,
|
|
|
|
bool writeable,
|
|
|
|
Genode::uint32_t State::*r,
|
|
|
|
Genode::addr_t v)
|
|
|
|
: _encoding(Iss::value(crn, op1, crm, op2)),
|
|
|
|
_name(name),
|
|
|
|
_writeable(writeable), _r(r), _init_value(v) {}
|
|
|
|
|
|
|
|
const char * name() const { return _name; }
|
|
|
|
const bool writeable() const { return _writeable; }
|
|
|
|
const Genode::addr_t init_value() const {
|
|
|
|
return _init_value; }
|
|
|
|
|
|
|
|
Register * find_by_encoding(Iss::access_t e)
|
|
|
|
{
|
|
|
|
if (e == _encoding) return this;
|
|
|
|
|
|
|
|
Register * r =
|
|
|
|
Avl_node<Register>::child(e > _encoding);
|
2017-01-06 12:48:59 +01:00
|
|
|
return r ? r->find_by_encoding(e) : nullptr;
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
void write(State & state, Genode::addr_t v) {
|
|
|
|
state.*_r = (Genode::uint32_t)v; }
|
2015-02-19 14:50:27 +01:00
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
Genode::addr_t read(State & state) const {
|
|
|
|
return (Genode::addr_t)(state.*_r); }
|
2015-02-19 14:50:27 +01:00
|
|
|
|
|
|
|
|
|
|
|
/************************
|
|
|
|
** Avl node interface **
|
|
|
|
************************/
|
|
|
|
|
|
|
|
bool higher(Register *r) {
|
|
|
|
return (r->_encoding > _encoding); }
|
|
|
|
};
|
|
|
|
|
|
|
|
Genode::Avl_tree<Register> _reg_tree;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
bool handle_trap(State & state)
|
2015-02-19 14:50:27 +01:00
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
Iss::access_t v = state.hsr;
|
2015-02-19 14:50:27 +01:00
|
|
|
Register * reg = _reg_tree.first();
|
|
|
|
if (reg) reg = reg->find_by_encoding(Iss::mask_encoding(v));
|
|
|
|
|
|
|
|
if (!reg) {
|
2017-01-06 12:48:59 +01:00
|
|
|
Genode::error("unknown cp15 access @ ip=", state.ip, ":");
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
Genode::error(Iss::Direction::get(v) ? "read" : "write",
|
|
|
|
": "
|
|
|
|
"c15 ", Iss::Opcode1::get(v), " "
|
|
|
|
"r", Iss::Register::get(v), " "
|
|
|
|
"c", Iss::Crn::get(v), " "
|
|
|
|
"c", Iss::Crm::get(v), " ",
|
|
|
|
Iss::Opcode2::get(v));
|
2015-02-19 14:50:27 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Iss::Direction::get(v)) { /* read access */
|
2017-01-06 12:48:59 +01:00
|
|
|
*(state.r(Iss::Register::get(v))) = reg->read(state);
|
2015-02-19 14:50:27 +01:00
|
|
|
} else { /* write access */
|
|
|
|
if (!reg->writeable()) {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
Genode::error("writing to cp15 register ",
|
|
|
|
reg->name(), " not allowed!");
|
2015-02-19 14:50:27 +01:00
|
|
|
return false;
|
|
|
|
}
|
2017-01-06 12:48:59 +01:00
|
|
|
reg->write(state, *(state.r(Iss::Register::get(v))));
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
2017-01-06 12:48:59 +01:00
|
|
|
state.ip += sizeof(Genode::addr_t);
|
2015-02-19 14:50:27 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Cp15 : public Coprocessor
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2018-01-24 14:02:35 +01:00
|
|
|
Register _regs_0 { 0, 0, 0, 0, "MIDR", false, &State::midr, 0x412fc0f1 };
|
|
|
|
Register _regs_1 { 0, 0, 0, 5, "MPIDR", false, &State::mpidr, 0x40000000 };
|
|
|
|
Register _regs_2 { 0, 0, 0, 1, "CTR", false, &State::ctr, 0x8444c004 };
|
|
|
|
Register _regs_3 { 0, 1, 0, 0, "CCSIDR", false, &State::ccsidr, 0x701fe00a };
|
|
|
|
Register _regs_4 { 0, 1, 0, 1, "CLIDR", false, &State::clidr, 0x0a200023 };
|
|
|
|
Register _regs_5 { 0, 0, 1, 0, "PFR0", false, &State::pfr0, 0x00001031 };
|
|
|
|
Register _regs_6 { 0, 0, 1, 4, "MMFR0", false, &State::mmfr0, 0x10201105 };
|
|
|
|
Register _regs_7 { 0, 0, 2, 0, "ISAR0", false, &State::isar0, 0x02101110 };
|
|
|
|
Register _regs_8 { 0, 0, 2, 3, "ISAR3", false, &State::isar3, 0x11112131 };
|
|
|
|
Register _regs_9 { 0, 0, 2, 4, "ISAR4", false, &State::isar4, 0x10011142 };
|
|
|
|
Register _regs_10 { 0, 2, 0, 0, "CSSELR", true, &State::csselr, 0x00000000 };
|
|
|
|
Register _regs_11 { 1, 0, 0, 0, "SCTRL", true, &State::sctrl, 0 /* 0xc5007a 0x00c5187a*/ };
|
|
|
|
Register _regs_12 { 1, 0, 0, 1, "ACTRL", true, &State::actrl, 0x00000040 };
|
|
|
|
Register _regs_13 { 1, 0, 0, 2, "CPACR", true, &State::cpacr, 0x00000000 };
|
|
|
|
Register _regs_14 { 2, 0, 0, 0, "TTBR0", true, &State::ttbr0, 0x00000000 };
|
|
|
|
Register _regs_15 { 2, 0, 0, 1, "TTBR1", true, &State::ttbr1, 0x00000000 };
|
|
|
|
Register _regs_16 { 2, 0, 0, 2, "TTBCR", true, &State::ttbcr, 0x00000000 };
|
|
|
|
Register _regs_17 { 3, 0, 0, 0, "DACR", true, &State::dacr, 0x55555555 };
|
|
|
|
Register _regs_18 { 5, 0, 0, 0, "DFSR", true, &State::dfsr, 0x00000000 };
|
|
|
|
Register _regs_19 { 5, 0, 0, 1, "IFSR", true, &State::ifsr, 0x00000000 };
|
|
|
|
Register _regs_20 { 5, 0, 1, 0, "ADFSR", true, &State::adfsr, 0x00000000 };
|
|
|
|
Register _regs_21 { 5, 0, 1, 1, "AIFSR", true, &State::aifsr, 0x00000000 };
|
|
|
|
Register _regs_22 { 6, 0, 0, 0, "DFAR", true, &State::dfar, 0x00000000 };
|
|
|
|
Register _regs_23 { 6, 0, 0, 2, "IFAR", true, &State::ifar, 0x00000000 };
|
|
|
|
Register _regs_24 { 10, 0, 2, 0, "PRRR", true, &State::prrr, 0x00098aa4 };
|
|
|
|
Register _regs_25 { 10, 0, 2, 1, "NMRR", true, &State::nmrr, 0x44e048e0 };
|
|
|
|
Register _regs_26 { 13, 0, 0, 1, "CONTEXTIDR", true, &State::cidr, 0x00000000 };
|
|
|
|
|
|
|
|
void _init_reg(Register ®, State &state)
|
|
|
|
{
|
|
|
|
_reg_tree.insert(®);
|
|
|
|
reg.write(state, reg.init_value());
|
|
|
|
}
|
2015-02-19 14:50:27 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
Cp15(State & state)
|
2015-02-19 14:50:27 +01:00
|
|
|
{
|
2018-01-24 14:02:35 +01:00
|
|
|
_init_reg(_regs_0, state);
|
|
|
|
_init_reg(_regs_1, state);
|
|
|
|
_init_reg(_regs_2, state);
|
|
|
|
_init_reg(_regs_3, state);
|
|
|
|
_init_reg(_regs_4, state);
|
|
|
|
_init_reg(_regs_5, state);
|
|
|
|
_init_reg(_regs_6, state);
|
|
|
|
_init_reg(_regs_7, state);
|
|
|
|
_init_reg(_regs_8, state);
|
|
|
|
_init_reg(_regs_9, state);
|
|
|
|
_init_reg(_regs_10, state);
|
|
|
|
_init_reg(_regs_11, state);
|
|
|
|
_init_reg(_regs_12, state);
|
|
|
|
_init_reg(_regs_13, state);
|
|
|
|
_init_reg(_regs_14, state);
|
|
|
|
_init_reg(_regs_15, state);
|
|
|
|
_init_reg(_regs_16, state);
|
|
|
|
_init_reg(_regs_17, state);
|
|
|
|
_init_reg(_regs_18, state);
|
|
|
|
_init_reg(_regs_19, state);
|
|
|
|
_init_reg(_regs_20, state);
|
|
|
|
_init_reg(_regs_21, state);
|
|
|
|
_init_reg(_regs_22, state);
|
|
|
|
_init_reg(_regs_23, state);
|
|
|
|
_init_reg(_regs_24, state);
|
|
|
|
_init_reg(_regs_25, state);
|
|
|
|
_init_reg(_regs_26, state);
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Device : public Genode::Avl_node<Device>
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
|
|
|
|
struct Iss : Hsr
|
|
|
|
{
|
|
|
|
struct Write : Bitfield<6, 1> {};
|
|
|
|
struct Register : Bitfield<16, 4> {};
|
|
|
|
struct Sign_extend : Bitfield<21, 1> {};
|
|
|
|
struct Access_size : Bitfield<22, 2> {
|
|
|
|
enum { BYTE, HALFWORD, WORD }; };
|
|
|
|
struct Valid : Bitfield<24, 1> {};
|
|
|
|
|
|
|
|
static bool valid(access_t v) {
|
|
|
|
return Valid::get(v) && !Sign_extend::get(v); }
|
|
|
|
|
|
|
|
static bool write(access_t v) { return Write::get(v); }
|
|
|
|
static unsigned r(access_t v) { return Register::get(v); }
|
|
|
|
};
|
|
|
|
|
|
|
|
const char * const _name;
|
|
|
|
const Genode::uint64_t _addr;
|
|
|
|
const Genode::uint64_t _size;
|
2017-01-06 12:48:59 +01:00
|
|
|
Vm & _vm;
|
2015-02-19 14:50:27 +01:00
|
|
|
|
|
|
|
using Error = Vm::Exception;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Device(const char * const name,
|
|
|
|
const Genode::uint64_t addr,
|
|
|
|
const Genode::uint64_t size,
|
2017-01-06 12:48:59 +01:00
|
|
|
Vm & vm)
|
2015-02-19 14:50:27 +01:00
|
|
|
: _name(name), _addr(addr), _size(size), _vm(vm) { }
|
|
|
|
|
|
|
|
Genode::uint64_t addr() { return _addr; }
|
|
|
|
Genode::uint64_t size() { return _size; }
|
|
|
|
const char * name() { return _name; }
|
|
|
|
|
|
|
|
virtual void read (Genode::uint32_t * reg,
|
|
|
|
Genode::uint64_t off) {
|
|
|
|
throw Error("Device %s: word-wise read of %llx not allowed",
|
|
|
|
name(), off); }
|
|
|
|
|
|
|
|
virtual void write (Genode::uint32_t * reg,
|
|
|
|
Genode::uint64_t off) {
|
|
|
|
throw Error("Device %s: word-wise write of %llx not allowed",
|
|
|
|
name(), off); }
|
|
|
|
|
|
|
|
virtual void read (Genode::uint16_t * reg,
|
|
|
|
Genode::uint64_t off) {
|
|
|
|
throw Error("Device %s: halfword read of %llx not allowed",
|
|
|
|
name(), off); }
|
|
|
|
|
|
|
|
virtual void write (Genode::uint16_t * reg,
|
|
|
|
Genode::uint64_t off) {
|
|
|
|
throw Error("Device %s: halfword write of %llx not allowed",
|
|
|
|
name(), off); }
|
|
|
|
|
|
|
|
virtual void read (Genode::uint8_t * reg,
|
|
|
|
Genode::uint64_t off) {
|
|
|
|
throw Error("Device %s: byte-wise read of %llx not allowed",
|
|
|
|
name(), off); }
|
|
|
|
|
|
|
|
virtual void write (Genode::uint8_t * reg,
|
|
|
|
Genode::uint64_t off) {
|
|
|
|
throw Error("Device %s: byte-wise write of %llx not allowed",
|
|
|
|
name(), off); }
|
|
|
|
|
|
|
|
virtual void irq_enabled (unsigned irq) { }
|
|
|
|
virtual void irq_disabled(unsigned irq) { }
|
|
|
|
virtual void irq_handled (unsigned irq) { }
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
void handle_memory_access(State & state)
|
2015-02-19 14:50:27 +01:00
|
|
|
{
|
|
|
|
using namespace Genode;
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
if (!Iss::valid(state.hsr))
|
2015-02-19 14:50:27 +01:00
|
|
|
throw Error("Device %s: unknown HSR=%lx",
|
2017-01-06 12:48:59 +01:00
|
|
|
name(), state.hsr);
|
2015-02-19 14:50:27 +01:00
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
bool wr = Iss::Write::get(state.hsr);
|
|
|
|
unsigned idx = Iss::Register::get(state.hsr);
|
|
|
|
uint64_t ipa = (uint64_t)state.hpfar << 8;
|
|
|
|
uint64_t off = ipa - addr() + (state.hdfar & ((1 << 13) - 1));
|
2015-02-19 14:50:27 +01:00
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
switch (Iss::Access_size::get(state.hsr)) {
|
2015-02-19 14:50:27 +01:00
|
|
|
case Iss::Access_size::BYTE:
|
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
uint8_t * p = (uint8_t*)state.r(idx) + (off & 0b11);
|
2015-02-19 14:50:27 +01:00
|
|
|
wr ? write(p, off) : read(p, off);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Iss::Access_size::HALFWORD:
|
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
uint16_t * p = (uint16_t*) state.r(idx) + (off & 0b1);
|
2015-02-19 14:50:27 +01:00
|
|
|
wr ? write(p, off) : read(p, off);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Iss::Access_size::WORD:
|
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
uint32_t * p = (uint32_t*) state.r(idx);
|
2015-02-19 14:50:27 +01:00
|
|
|
wr ? write(p, off) : read(p, off);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
throw Error("Device %s: invalid alignment", name());
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/************************
|
|
|
|
** Avl node interface **
|
|
|
|
************************/
|
|
|
|
|
|
|
|
bool higher(Device *d) { return d->addr() > addr(); }
|
|
|
|
|
|
|
|
Device *find_by_addr(Genode::uint64_t a)
|
|
|
|
{
|
|
|
|
if ((a >= addr()) && (a < (addr()+size())))
|
|
|
|
return this;
|
|
|
|
|
|
|
|
Device *d = Avl_node<Device>::child(a > addr());
|
2017-01-06 12:48:59 +01:00
|
|
|
return d ? d->find_by_addr(a) : nullptr;
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Gic : public Device
|
|
|
|
{
|
|
|
|
enum {
|
|
|
|
GICD_CTLR = 0,
|
|
|
|
GICD_TYPER = 0x4,
|
|
|
|
GICD_ISENABLER0 = 0x100,
|
|
|
|
GICD_ISENABLERL = 0x17c,
|
|
|
|
GICD_ICENABLER0 = 0x180,
|
|
|
|
GICD_ICENABLERL = 0x1fc,
|
|
|
|
GICD_IPRIORITYR0 = 0x400,
|
|
|
|
GICD_IPRIORITYRL = 0x7f8,
|
|
|
|
GICD_ITARGETSR0 = 0x800,
|
|
|
|
GICD_ITARGETSRL = 0xbf8,
|
|
|
|
GICD_ICFGR2 = 0xc08,
|
|
|
|
GICD_ICFGRL = 0xcfc,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum Irqs {
|
|
|
|
SGI_MAX = 15,
|
2019-04-10 12:43:17 +02:00
|
|
|
TIMER = Arm_v7::VT_TIMER_IRQ,
|
2015-02-19 14:50:27 +01:00
|
|
|
MAX_IRQ = 256,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Irq {
|
|
|
|
|
|
|
|
enum Cpu_state { INACTIVE, PENDING };
|
|
|
|
enum Distr_state { ENABLED, DISABLED };
|
|
|
|
|
|
|
|
Cpu_state cpu_state = INACTIVE;
|
|
|
|
Distr_state distr_state = DISABLED;
|
|
|
|
Device * device = nullptr;
|
|
|
|
bool eoi = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
Irq _irqs[MAX_IRQ+1];
|
|
|
|
bool _distr_enabled = false;
|
|
|
|
|
|
|
|
using Error = Vm::Exception;
|
|
|
|
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
** GICH interface **
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
struct Gich_lr : Genode::Register<32>
|
|
|
|
{
|
|
|
|
struct Virt_id : Bitfield<0, 10> { };
|
|
|
|
struct Phys_id : Bitfield<10, 10> { };
|
|
|
|
struct Prio : Bitfield<23, 5> { };
|
|
|
|
struct State : Bitfield<28, 2> { };
|
|
|
|
struct Hw : Bitfield<31, 1> { };
|
|
|
|
};
|
|
|
|
|
|
|
|
void _handle_eoi()
|
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
if (!(_vm.state().gic_misr & 1)) return;
|
2015-02-19 14:50:27 +01:00
|
|
|
|
|
|
|
for (unsigned i = 0; i < State::NR_IRQ; i++) {
|
2017-01-06 12:48:59 +01:00
|
|
|
if (_vm.state().gic_eisr & (1 << i)) {
|
|
|
|
unsigned irq = Gich_lr::Virt_id::get(_vm.state().gic_lr[i]);
|
2015-02-19 14:50:27 +01:00
|
|
|
if (irq > MAX_IRQ)
|
|
|
|
throw Error("IRQ out of bounds");
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().gic_lr[i] = 0;
|
|
|
|
_vm.state().gic_elrsr0 |= 1 << i;
|
2015-02-19 14:50:27 +01:00
|
|
|
if (irq == TIMER &&
|
|
|
|
_irqs[irq].distr_state == Irq::ENABLED)
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().timer_irq = true;
|
2015-02-19 14:50:27 +01:00
|
|
|
_irqs[irq].cpu_state = Irq::INACTIVE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().gic_misr = 0;
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void _inject_irq(unsigned irq, bool eoi)
|
|
|
|
{
|
|
|
|
if (irq == TIMER)
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().timer_irq = false;
|
2015-02-19 14:50:27 +01:00
|
|
|
|
|
|
|
for (unsigned i = 0; i < State::NR_IRQ; i++) {
|
2017-01-06 12:48:59 +01:00
|
|
|
if (!(_vm.state().gic_elrsr0 & (1 << i))) {
|
|
|
|
Gich_lr::access_t v = _vm.state().gic_lr[i];
|
2015-02-19 14:50:27 +01:00
|
|
|
if (Gich_lr::Virt_id::get(v) == irq)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < State::NR_IRQ; i++) {
|
2017-01-06 12:48:59 +01:00
|
|
|
if (!(_vm.state().gic_elrsr0 & (1 << i)))
|
2015-02-19 14:50:27 +01:00
|
|
|
continue;
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().gic_elrsr0 &= ~(1 << i);
|
2015-02-19 14:50:27 +01:00
|
|
|
Gich_lr::access_t v = 0;
|
|
|
|
Gich_lr::Virt_id::set(v, irq);
|
|
|
|
Gich_lr::Phys_id::set(v, eoi ? 1 << 9 : 0);
|
|
|
|
Gich_lr::Prio::set(v, 0);
|
|
|
|
Gich_lr::State::set(v, 0b1);
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().gic_lr[i] = v;
|
2015-02-19 14:50:27 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw Error("IRQ queue full, can't inject irq %u", irq);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _enable_irq(unsigned irq)
|
|
|
|
{
|
|
|
|
if (irq > MAX_IRQ || !_irqs[irq].device)
|
|
|
|
throw Error("GIC: can't enable unknown IRQ %d", irq);
|
|
|
|
|
|
|
|
if (_irqs[irq].distr_state == Irq::ENABLED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_irqs[irq].distr_state = Irq::ENABLED;
|
|
|
|
_irqs[irq].device->irq_enabled(irq);
|
|
|
|
|
|
|
|
if (irq == TIMER)
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().timer_irq = true;
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void _disable_irq(unsigned irq)
|
|
|
|
{
|
|
|
|
if (irq > MAX_IRQ)
|
|
|
|
throw Error("IRQ out of bounds");
|
|
|
|
|
|
|
|
if (_irqs[irq].distr_state == Irq::DISABLED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_irqs[irq].distr_state = Irq::DISABLED;
|
|
|
|
_irqs[irq].device->irq_disabled(irq);
|
|
|
|
|
|
|
|
if (irq == TIMER)
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().timer_irq = false;
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Gic(const char * const name,
|
|
|
|
const Genode::uint64_t addr,
|
|
|
|
const Genode::uint64_t size,
|
2017-01-06 12:48:59 +01:00
|
|
|
Vm & vm)
|
2015-02-19 14:50:27 +01:00
|
|
|
: Device(name, addr, size, vm)
|
|
|
|
{
|
|
|
|
for (unsigned i = 0; i <= MAX_IRQ; i++) {
|
|
|
|
_irqs[i] = Irq();
|
|
|
|
if (i <= SGI_MAX)
|
|
|
|
_irqs[i].device = this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void read (Genode::uint32_t * reg, Genode::uint64_t off)
|
|
|
|
{
|
|
|
|
if (off >= GICD_ICFGR2 && off <= GICD_ICFGRL) {
|
|
|
|
*reg = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read enable registers */
|
|
|
|
if (off >= GICD_ISENABLER0 && off <= GICD_ISENABLERL) {
|
|
|
|
*reg = 0;
|
|
|
|
Genode::addr_t idx = ((Genode::addr_t)off - GICD_ISENABLER0) * 8;
|
|
|
|
for (unsigned i = 0; i < 32; i++) {
|
|
|
|
if (_irqs[idx + i].distr_state == Irq::ENABLED)
|
|
|
|
*reg |= 1 << i;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (off >= GICD_ITARGETSR0 && off <= GICD_ITARGETSRL) {
|
|
|
|
*reg = 0x01010101;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (off) {
|
|
|
|
case GICD_CTLR:
|
|
|
|
*reg = _distr_enabled ? 1 : 0;
|
|
|
|
return;
|
|
|
|
case GICD_TYPER:
|
|
|
|
*reg = 0b101;
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
throw Error("GIC: unsupported read offset %llx", off);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void write(Genode::uint32_t * reg, Genode::uint64_t off)
|
|
|
|
{
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
/* only allow cpu0 as target by now */
|
|
|
|
if (off >= GICD_ITARGETSR0 && off <= GICD_ITARGETSRL &&
|
|
|
|
*reg == 0x01010101)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* only allow level triggered && active low */
|
|
|
|
if (off >= GICD_ICFGR2 && off <= GICD_ICFGRL &&
|
|
|
|
*reg == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* ignore priority settings */
|
|
|
|
if (off >= GICD_IPRIORITYR0 && off <= GICD_IPRIORITYRL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* set enable registers */
|
|
|
|
if (off >= GICD_ISENABLER0 && off <= GICD_ISENABLERL) {
|
|
|
|
addr_t idx = ((addr_t)off - GICD_ISENABLER0) * 8;
|
|
|
|
for (unsigned i = 0; i < 32; i++)
|
|
|
|
if (((*reg >> i) & 1))
|
|
|
|
_enable_irq(idx+i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clear enable registers */
|
|
|
|
if (off >= GICD_ICENABLER0 && off <= GICD_ICENABLERL) {
|
|
|
|
addr_t idx = ((addr_t)off - GICD_ICENABLER0) * 8;
|
|
|
|
for (unsigned i = 0; i < 32; i++)
|
|
|
|
if (((*reg >> i) & 1))
|
|
|
|
_disable_irq(idx+i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (off) {
|
|
|
|
case GICD_CTLR:
|
|
|
|
_distr_enabled = (*reg & 0b1);
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
throw Error("GIC: unsupported write offset %llx", off);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void register_irq(unsigned irq, Device * d, bool eoi)
|
|
|
|
{
|
|
|
|
_irqs[irq].device = d;
|
|
|
|
_irqs[irq].eoi = eoi;
|
|
|
|
}
|
|
|
|
|
|
|
|
void inject_irq(unsigned irq)
|
|
|
|
{
|
|
|
|
if (!_irqs[irq].device)
|
|
|
|
throw Error("No device registered for IRQ %u", irq);
|
|
|
|
|
|
|
|
if (_irqs[irq].cpu_state == Irq::PENDING)
|
|
|
|
throw Error("Pending IRQ should not trigger again");;
|
|
|
|
|
|
|
|
if (_irqs[irq].eoi)
|
|
|
|
_irqs[irq].cpu_state = Irq::PENDING;
|
|
|
|
|
|
|
|
if (_irqs[irq].distr_state == Irq::DISABLED) {
|
base: avoid use of deprecated base/printf.h
Besides adapting the components to the use of base/log.h, the patch
cleans up a few base headers, i.e., it removes unused includes from
root/component.h, specifically base/heap.h and
ram_session/ram_session.h. Hence, components that relied on the implicit
inclusion of those headers have to manually include those headers now.
While adjusting the log messages, I repeatedly stumbled over the problem
that printing char * arguments is ambiguous. It is unclear whether to
print the argument as pointer or null-terminated string. To overcome
this problem, the patch introduces a new type 'Cstring' that allows the
caller to express that the argument should be handled as null-terminated
string. As a nice side effect, with this type in place, the optional len
argument of the 'String' class could be removed. Instead of supplying a
pair of (char const *, size_t), the constructor accepts a 'Cstring'.
This, in turn, clears the way let the 'String' constructor use the new
output mechanism to assemble a string from multiple arguments (and
thereby getting rid of snprintf within Genode in the near future).
To enforce the explicit resolution of the char * ambiguity, the 'char *'
overload of the 'print' function is marked as deleted.
Issue #1987
2016-07-13 19:07:09 +02:00
|
|
|
Genode::warning("disabled irq ", irq, " injected");
|
2015-02-19 14:50:27 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_inject_irq(irq, _irqs[irq].eoi);
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.interrupt();
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void irq_occured()
|
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
switch(_vm.state().gic_irq) {
|
2019-04-10 12:43:17 +02:00
|
|
|
case Arm_v7::VT_MAINTAINANCE_IRQ:
|
2015-02-19 14:50:27 +01:00
|
|
|
_handle_eoi();
|
|
|
|
return;
|
|
|
|
case TIMER:
|
|
|
|
inject_irq(TIMER);
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
throw Error("Unknown IRQ %u occured",
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().gic_irq);
|
2015-02-19 14:50:27 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Generic_timer : public Device
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
Timer::Connection _timer;
|
|
|
|
Signal_handler<Generic_timer> _handler;
|
|
|
|
Gic &_gic;
|
2015-02-19 14:50:27 +01:00
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
void _timeout()
|
2015-02-19 14:50:27 +01:00
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().timer_ctrl = 5;
|
|
|
|
_vm.state().timer_val = 0xffffffff;
|
2019-04-10 12:43:17 +02:00
|
|
|
_gic.inject_irq(Arm_v7::VT_TIMER_IRQ);
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Generic_timer(const char * const name,
|
|
|
|
const Genode::uint64_t addr,
|
|
|
|
const Genode::uint64_t size,
|
2017-01-06 12:48:59 +01:00
|
|
|
Vmm &vmm,
|
|
|
|
Genode::Env &env,
|
2015-02-19 14:50:27 +01:00
|
|
|
Gic &gic)
|
2017-01-06 12:48:59 +01:00
|
|
|
: Device(name, addr, size, vmm.vm()),
|
|
|
|
_timer(env),
|
|
|
|
_handler(vmm, env.ep(), *this, &Generic_timer::_timeout),
|
|
|
|
_gic(gic)
|
|
|
|
{
|
2015-02-19 14:50:27 +01:00
|
|
|
_timer.sigh(_handler);
|
2019-04-10 12:43:17 +02:00
|
|
|
_gic.register_irq(Arm_v7::VT_TIMER_IRQ, this, true);
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void schedule_timeout()
|
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
if ((_vm.state().timer_ctrl & 0b101) != 0b101)
|
|
|
|
_timer.trigger_once(_vm.state().timer_val / 24);
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class System_register : public Device
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
enum {
|
|
|
|
SYS_LED = 0x8,
|
|
|
|
SYS_FLASH = 0x4c,
|
|
|
|
SYS_24MHZ = 0x5c,
|
|
|
|
SYS_MCI = 0x48,
|
|
|
|
SYS_MISC = 0x60,
|
|
|
|
SYS_PROCID0 = 0x84,
|
|
|
|
SYS_CFGDATA = 0xa0,
|
|
|
|
SYS_CFGCTRL = 0xa4,
|
|
|
|
SYS_CFGSTAT = 0xa8,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Sys_cfgctrl : Genode::Register<32>
|
|
|
|
{
|
|
|
|
struct Device : Bitfield<0,12> {};
|
|
|
|
struct Position : Bitfield<12,4> {};
|
|
|
|
struct Site : Bitfield<16,2> {};
|
|
|
|
struct Function : Bitfield<20,6> {};
|
|
|
|
struct Write : Bitfield<30,1> {};
|
|
|
|
struct Start : Bitfield<31,1> {};
|
|
|
|
};
|
|
|
|
|
|
|
|
Timer::Connection _timer;
|
|
|
|
Genode::uint32_t _spi_data = 0;
|
|
|
|
Genode::uint32_t _spi_stat = 1;
|
|
|
|
|
|
|
|
using Error = Vm::Exception;
|
|
|
|
|
|
|
|
void _mcc_control(unsigned device, unsigned func, bool write)
|
|
|
|
{
|
|
|
|
if (func == 1 && !write) {
|
|
|
|
switch (device) {
|
|
|
|
case 0: /* OSCCLK0 */
|
|
|
|
_spi_data = 60000000;
|
|
|
|
return;
|
|
|
|
case 2: /* OSCCLK2 */
|
|
|
|
_spi_data = 24000000;
|
|
|
|
return;
|
|
|
|
case 4: /* OSCCLK4 */
|
|
|
|
_spi_data = 40000000;
|
|
|
|
return;
|
|
|
|
case 5: /* OSCCLK5 */
|
|
|
|
_spi_data = 23750000;
|
|
|
|
return;
|
|
|
|
case 6: /* OSCCLK6 */
|
|
|
|
_spi_data = 50000000;
|
|
|
|
return;
|
|
|
|
case 7: /* OSCCLK7 */
|
|
|
|
_spi_data = 60000000;
|
|
|
|
return;
|
|
|
|
case 8: /* OSCCLK8 */
|
|
|
|
_spi_data = 40000000;
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
throw Error("Sys regs: unsupported MCC device");
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (func == 2 && !write) {
|
|
|
|
switch (device) {
|
|
|
|
case 0: /* VOLT0 */
|
|
|
|
_spi_data = 900000;
|
|
|
|
return;
|
|
|
|
default: ;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
throw Error("Unknown device %u func=%u write=%d",
|
|
|
|
device, func, write);
|
|
|
|
};
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
System_register(const char * const name,
|
|
|
|
const Genode::uint64_t addr,
|
|
|
|
const Genode::uint64_t size,
|
2017-01-06 12:48:59 +01:00
|
|
|
Vm & vm,
|
|
|
|
Genode::Env & env)
|
|
|
|
: Device(name, addr, size, vm), _timer(env) {}
|
2015-02-19 14:50:27 +01:00
|
|
|
|
|
|
|
void read(Genode::uint32_t * reg, Genode::uint64_t off)
|
|
|
|
{
|
|
|
|
switch (off) {
|
|
|
|
case SYS_LED:
|
|
|
|
*reg = 0xff;
|
|
|
|
return;
|
|
|
|
case SYS_FLASH:
|
|
|
|
*reg = 0;
|
|
|
|
return;
|
|
|
|
case SYS_24MHZ: /* 24 MHz counter */
|
2019-04-09 15:46:36 +02:00
|
|
|
*reg = (Genode::uint32_t)_timer.elapsed_ms() * 24000;
|
2015-02-19 14:50:27 +01:00
|
|
|
return;
|
|
|
|
case SYS_MISC:
|
|
|
|
*reg = 1 << 12;
|
|
|
|
return;
|
|
|
|
case SYS_PROCID0:
|
|
|
|
*reg = 0x14000237; /* daughterboard ID */
|
|
|
|
return;
|
|
|
|
case SYS_MCI:
|
|
|
|
*reg = 0; /* no mmc inside */
|
|
|
|
return;
|
|
|
|
case SYS_CFGSTAT:
|
|
|
|
*reg = _spi_stat;
|
|
|
|
return;
|
|
|
|
case SYS_CFGCTRL:
|
|
|
|
*reg = 0;
|
|
|
|
return;
|
|
|
|
case SYS_CFGDATA:
|
|
|
|
*reg = _spi_data;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
throw Error("Sys regs: read of offset %llx forbidden", off);
|
|
|
|
}
|
|
|
|
|
|
|
|
void write(Genode::uint32_t * reg, Genode::uint64_t off)
|
|
|
|
{
|
|
|
|
switch (off) {
|
|
|
|
case SYS_CFGDATA:
|
|
|
|
_spi_data = *reg;
|
|
|
|
return;
|
|
|
|
case SYS_CFGSTAT:
|
|
|
|
_spi_stat = *reg;
|
|
|
|
return;
|
|
|
|
case SYS_CFGCTRL:
|
|
|
|
if (Sys_cfgctrl::Start::get(*reg)) {
|
|
|
|
_spi_stat = 1;
|
|
|
|
_mcc_control(Sys_cfgctrl::Device::get(*reg),
|
|
|
|
Sys_cfgctrl::Function::get(*reg),
|
|
|
|
Sys_cfgctrl::Write::get(*reg));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case SYS_24MHZ:
|
|
|
|
case SYS_MISC:
|
|
|
|
case SYS_PROCID0:
|
|
|
|
case SYS_MCI:
|
|
|
|
;
|
|
|
|
};
|
|
|
|
throw Error("Sys regs: write of offset %llx forbidden", off);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Pl011 : public Device
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2015-07-02 11:21:20 +02:00
|
|
|
using Board = Vea9x4::Board;
|
2015-04-14 10:51:53 +02:00
|
|
|
using Ring_buffer = Genode::Ring_buffer<char, 1024,
|
|
|
|
Genode::Ring_buffer_unsynchronized>;
|
2015-02-19 14:50:27 +01:00
|
|
|
|
|
|
|
class Wrong_offset {};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
UARTDR = 0x0,
|
|
|
|
UARTFR = 0x18,
|
|
|
|
UARTIBRD = 0x24,
|
|
|
|
UARTFBRD = 0x28,
|
|
|
|
UARTLCR_H = 0x2c,
|
|
|
|
UARTCR = 0x30,
|
|
|
|
UARTIFLS = 0x34,
|
|
|
|
UARTIMSC = 0x38,
|
|
|
|
UARTMIS = 0x40,
|
|
|
|
UARTICR = 0x44,
|
|
|
|
UARTPERIPHID0 = 0xfe0,
|
|
|
|
UARTPERIPHID1 = 0xfe4,
|
|
|
|
UARTPERIPHID2 = 0xfe8,
|
|
|
|
UARTPERIPHID3 = 0xfec,
|
|
|
|
UARTPCELLID0 = 0xff0,
|
|
|
|
UARTPCELLID1 = 0xff4,
|
|
|
|
UARTPCELLID2 = 0xff8,
|
|
|
|
UARTPCELLID3 = 0xffc,
|
|
|
|
};
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
Terminal::Connection _terminal;
|
|
|
|
Signal_handler<Pl011> _handler;
|
|
|
|
Gic &_gic;
|
|
|
|
Ring_buffer _rx_buf;
|
|
|
|
Genode::uint16_t _ibrd = 0;
|
|
|
|
Genode::uint16_t _fbrd = 0;
|
|
|
|
Genode::uint16_t _lcr_h = 0;
|
|
|
|
Genode::uint16_t _imsc = 0b1111;
|
|
|
|
Genode::uint16_t _ris = 0;
|
|
|
|
Genode::uint16_t _cr = 0x300;
|
2015-02-19 14:50:27 +01:00
|
|
|
|
|
|
|
void _out_char(unsigned char c) {
|
|
|
|
_terminal.write(&c, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char _get_char()
|
|
|
|
{
|
|
|
|
if (_rx_buf.empty()) return 0;
|
|
|
|
return _rx_buf.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
Genode::uint16_t _get(Genode::uint64_t off)
|
|
|
|
{
|
|
|
|
switch (off) {
|
|
|
|
case UARTDR: return _get_char();
|
|
|
|
case UARTPERIPHID0: return 0x11;
|
|
|
|
case UARTPERIPHID1: return 0x10;
|
|
|
|
case UARTPERIPHID2: return 0x14;
|
|
|
|
case UARTPERIPHID3: return 0x0;
|
|
|
|
case UARTPCELLID0: return 0xd;
|
|
|
|
case UARTPCELLID1: return 0xf0;
|
|
|
|
case UARTPCELLID2: return 0x5;
|
|
|
|
case UARTPCELLID3: return 0xb1;
|
|
|
|
case UARTFR: return _rx_buf.empty() ? 16 : 64;
|
|
|
|
case UARTCR: return _cr;
|
|
|
|
case UARTIMSC: return _imsc;
|
|
|
|
case UARTMIS: return _ris & _imsc;
|
|
|
|
case UARTFBRD: return _fbrd;
|
|
|
|
case UARTIBRD: return _ibrd;
|
|
|
|
case UARTLCR_H: return _lcr_h;
|
|
|
|
default:
|
|
|
|
throw Wrong_offset();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void _mask_irqs(Genode::uint32_t mask)
|
|
|
|
{
|
|
|
|
/* TX IRQ unmask */
|
|
|
|
if (mask & (1 << 5) && !(_imsc & (1 << 5))) {
|
|
|
|
_gic.inject_irq(Board::PL011_0_IRQ);
|
|
|
|
_ris |= 1 << 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RX IRQ unmask */
|
|
|
|
if (mask & (1 << 4) && !(_imsc & (1 << 4)) &&
|
|
|
|
!_rx_buf.empty()) {
|
|
|
|
_gic.inject_irq(Board::PL011_0_IRQ);
|
|
|
|
_ris |= 1 << 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
_imsc = mask;
|
|
|
|
}
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
void _read()
|
2015-02-19 14:50:27 +01:00
|
|
|
{
|
|
|
|
if (!_terminal.avail()) return;
|
|
|
|
|
2019-04-04 16:38:28 +02:00
|
|
|
while (_terminal.avail()) {
|
|
|
|
unsigned char c = 0;
|
|
|
|
_terminal.read(&c, 1);
|
|
|
|
_rx_buf.add(c);
|
|
|
|
}
|
2015-02-19 14:50:27 +01:00
|
|
|
|
|
|
|
_gic.inject_irq(Board::PL011_0_IRQ);
|
|
|
|
_ris |= 1 << 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
using Error = Vm::Exception;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Pl011(const char * const name,
|
|
|
|
const Genode::uint64_t addr,
|
|
|
|
const Genode::uint64_t size,
|
2017-01-06 12:48:59 +01:00
|
|
|
Vmm &vmm,
|
|
|
|
Genode::Env &env,
|
2015-02-19 14:50:27 +01:00
|
|
|
Gic &gic)
|
2017-01-06 12:48:59 +01:00
|
|
|
: Device(name, addr, size, vmm.vm()),
|
|
|
|
_terminal(env),
|
|
|
|
_handler(vmm, env.ep(), *this, &Pl011::_read),
|
2015-02-19 14:50:27 +01:00
|
|
|
_gic(gic) {
|
|
|
|
_terminal.read_avail_sigh(_handler);
|
|
|
|
_gic.register_irq(Board::PL011_0_IRQ, this, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void read(Genode::uint16_t * reg, Genode::uint64_t off)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
*reg = _get(off);
|
|
|
|
} catch(Wrong_offset &e) {
|
|
|
|
throw Error("UART: halfword read of offset %llx", off);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void read(Genode::uint32_t * reg, Genode::uint64_t off) {
|
|
|
|
read((Genode::uint16_t*) reg, off); }
|
|
|
|
|
|
|
|
void write(Genode::uint8_t * reg, Genode::uint64_t off)
|
|
|
|
{
|
|
|
|
if (off != UARTDR)
|
|
|
|
throw Error("UART: byte write %x to offset %llx",
|
|
|
|
*reg, off);
|
|
|
|
_terminal.write(reg, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void write(Genode::uint16_t * reg, Genode::uint64_t off)
|
|
|
|
{
|
|
|
|
switch (off) {
|
|
|
|
case UARTDR:
|
|
|
|
_terminal.write(reg, 1);
|
|
|
|
return;
|
|
|
|
case UARTFBRD:
|
|
|
|
_fbrd = *reg;
|
|
|
|
return;
|
|
|
|
case UARTIMSC:
|
|
|
|
_mask_irqs(*reg);
|
|
|
|
return;
|
|
|
|
case UARTIBRD:
|
|
|
|
_ibrd = *reg;
|
|
|
|
return;
|
|
|
|
case UARTLCR_H:
|
|
|
|
_lcr_h = *reg;
|
|
|
|
return;
|
|
|
|
case UARTICR:
|
|
|
|
_ris = _ris & ~*reg;
|
|
|
|
return;
|
|
|
|
case UARTCR:
|
|
|
|
_cr = *reg;
|
|
|
|
return;
|
|
|
|
case UARTIFLS:
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
throw Error("UART: halfword write %x to offset %llx",
|
|
|
|
*reg, off);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
Signal_handler<Vmm> _vm_handler;
|
2015-02-19 14:50:27 +01:00
|
|
|
Vm _vm;
|
|
|
|
Cp15 _cp15;
|
|
|
|
Genode::Avl_tree<Device> _device_tree;
|
|
|
|
Gic _gic;
|
|
|
|
Generic_timer _timer;
|
|
|
|
System_register _sys_regs;
|
|
|
|
Pl011 _uart;
|
|
|
|
|
|
|
|
void _handle_hyper_call() {
|
|
|
|
throw Vm::Exception("Unknown hyper call!"); }
|
|
|
|
|
|
|
|
void _handle_data_abort()
|
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
Genode::uint64_t ipa = (Genode::uint64_t)_vm.state().hpfar << 8;
|
2015-02-19 14:50:27 +01:00
|
|
|
Device * device = _device_tree.first()
|
|
|
|
? _device_tree.first()->find_by_addr(ipa) : nullptr;
|
|
|
|
if (!device)
|
|
|
|
throw Vm::Exception("No device at IPA=%llx", ipa);
|
|
|
|
device->handle_memory_access(_vm.state());
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().ip += sizeof(Genode::addr_t);
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void _handle_wfi()
|
|
|
|
{
|
2017-01-06 12:48:59 +01:00
|
|
|
if (_vm.state().hsr & 1)
|
2015-02-19 14:50:27 +01:00
|
|
|
throw Vm::Exception("WFE not implemented yet");
|
|
|
|
|
|
|
|
_vm.wait_for_interrupt();
|
|
|
|
_timer.schedule_timeout();
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.state().ip += sizeof(Genode::addr_t);
|
2015-02-19 14:50:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void _handle_trap()
|
|
|
|
{
|
|
|
|
/* check device number*/
|
2017-01-06 12:48:59 +01:00
|
|
|
switch (Hsr::Ec::get(_vm.state().hsr)) {
|
2015-02-19 14:50:27 +01:00
|
|
|
case Hsr::Ec::HVC:
|
|
|
|
_handle_hyper_call();
|
|
|
|
break;
|
|
|
|
case Hsr::Ec::CP15:
|
|
|
|
_cp15.handle_trap(_vm.state());
|
|
|
|
break;
|
|
|
|
case Hsr::Ec::DA:
|
|
|
|
_handle_data_abort();
|
|
|
|
break;
|
|
|
|
case Hsr::Ec::WFI:
|
|
|
|
_handle_wfi();
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
throw Vm::Exception("Unknown trap: %x",
|
2017-01-06 12:48:59 +01:00
|
|
|
Hsr::Ec::get(_vm.state().hsr));
|
2015-02-19 14:50:27 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
void _handle() {} /* dummy handler */
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Vmm(Genode::Env & env)
|
|
|
|
: _vm_handler(*this, env.ep(), *this, &Vmm::_handle),
|
|
|
|
_vm("linux", "dtb", 1024 * 1024 * 128, _vm_handler, env),
|
|
|
|
_cp15(_vm.state()),
|
|
|
|
_gic ("Gic", 0x2c001000, 0x2000, _vm),
|
|
|
|
_timer ("Timer", 0x2a430000, 0x1000, *this, env, _gic),
|
|
|
|
_sys_regs ("System Register", 0x1c010000, 0x1000, _vm, env),
|
|
|
|
_uart ("Pl011", 0x1c090000, 0x1000, *this, env, _gic)
|
|
|
|
{
|
|
|
|
_device_tree.insert(&_gic);
|
|
|
|
_device_tree.insert(&_sys_regs);
|
|
|
|
_device_tree.insert(&_uart);
|
|
|
|
|
|
|
|
Genode::log("Start virtual machine ...");
|
|
|
|
|
|
|
|
_vm.start();
|
|
|
|
_vm.run();
|
|
|
|
};
|
|
|
|
|
|
|
|
Vm & vm() { return _vm; }
|
|
|
|
|
|
|
|
template <typename FUNC>
|
|
|
|
void handle_vm(FUNC handler)
|
2015-02-19 14:50:27 +01:00
|
|
|
{
|
|
|
|
if (_vm.active()) {
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
_vm.pause();
|
|
|
|
|
2015-02-19 14:50:27 +01:00
|
|
|
enum { IRQ = 6, TRAP = 8 };
|
|
|
|
|
|
|
|
/* check exception reason */
|
2017-01-06 12:48:59 +01:00
|
|
|
switch (_vm.state().cpu_exception) {
|
2015-02-19 14:50:27 +01:00
|
|
|
case IRQ:
|
|
|
|
_gic.irq_occured();
|
|
|
|
break;
|
|
|
|
case TRAP:
|
|
|
|
_handle_trap();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw Vm::Exception("Curious exception occured");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
handler();
|
2015-02-19 14:50:27 +01:00
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
if (_vm.active()) _vm.run();
|
|
|
|
}
|
2015-02-19 14:50:27 +01:00
|
|
|
};
|
|
|
|
|
2017-01-06 12:48:59 +01:00
|
|
|
|
|
|
|
void Component::construct(Genode::Env & env) { static Vmm vmm(env); }
|