mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-30 08:03:59 +00:00
tz_vmm: update to new API and clean up
* get rid of printf * use exceptions instead of error codes * use Id_space instead of the individual block device registry * use Cstring instead of char const* * move method definitions > 1 line to .cc files * rename Block Block_driver and Serial Serial_driver to avoid name clashes with the Genode namespace and thereby simplify the code * use lambdas for Block device lookup and apply * switch to the Component framework * don't use env(), config(), ... and hand over env to each connection * use Attached_mmio and Attached_rom/ram_dataspace instead of manual solutions Fixes #2223
This commit is contained in:
parent
0590e00e66
commit
c9f7e9dbb2
@ -1,5 +1,5 @@
|
||||
LIBS += base config
|
||||
SRC_CC += serial.cc block.cc spec/imx53/main.cc
|
||||
SRC_CC += serial_driver.cc block_driver.cc vm_base.cc spec/imx53/main.cc
|
||||
INC_DIR += $(REP_DIR)/src/server/tz_vmm/spec/imx53
|
||||
INC_DIR += $(REP_DIR)/src/server/tz_vmm/include
|
||||
|
||||
|
@ -178,7 +178,7 @@ append config {
|
||||
if { $mmc_rootfs } {
|
||||
append config "
|
||||
<config>
|
||||
<block label=\"sda1\" irq=\"$tz_vmm_block_irq\"/>
|
||||
<block name=\"sda1\" irq=\"$tz_vmm_block_irq\"/>
|
||||
</config>
|
||||
<route>
|
||||
<service name=\"Block\"><child name=\"part_blk\" /></service>
|
||||
|
@ -1,602 +0,0 @@
|
||||
/*
|
||||
* \brief Paravirtualized serial device for a Trustzone VM
|
||||
* \author Martin Stein
|
||||
* \date 2015-10-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <block.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <block_session/connection.h>
|
||||
#include <base/thread.h>
|
||||
#include <util/construct_at.h>
|
||||
#include <base/allocator_avl.h>
|
||||
#include <os/config.h>
|
||||
|
||||
using namespace Vmm;
|
||||
using namespace Genode;
|
||||
|
||||
/**
|
||||
* Reply message from VMM to VM regarding a finished block request
|
||||
*/
|
||||
class Reply
|
||||
{
|
||||
private:
|
||||
|
||||
unsigned long _req;
|
||||
unsigned long _write;
|
||||
unsigned long _data_size;
|
||||
unsigned long _data[0];
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a reply for a given block request
|
||||
*
|
||||
* \param req request base
|
||||
* \param write wether the request wrote
|
||||
* \param data_size size of read data
|
||||
* \param data_src base of read data if any
|
||||
*/
|
||||
Reply(void * const req, bool const write,
|
||||
size_t const data_size, void * const data_src)
|
||||
:
|
||||
_req((unsigned long)req), _write(write), _data_size(data_size)
|
||||
{
|
||||
memcpy(_data, data_src, data_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the message size assuming a payload size of 'data_size'
|
||||
*/
|
||||
static size_t size(size_t const data_size) {
|
||||
return data_size + sizeof(Reply); }
|
||||
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/**
|
||||
* Cache for pending block requests
|
||||
*/
|
||||
class Request_cache
|
||||
{
|
||||
public:
|
||||
|
||||
class Full : public Exception { };
|
||||
|
||||
private:
|
||||
|
||||
class Entry_not_found : public Exception { };
|
||||
|
||||
enum { MAX = Block::Session::TX_QUEUE_SIZE };
|
||||
|
||||
struct Entry
|
||||
{
|
||||
void * pkt;
|
||||
void * req;
|
||||
|
||||
} _cache[MAX];
|
||||
|
||||
unsigned _find(void * const packet)
|
||||
{
|
||||
for (unsigned i = 0; i < MAX; i++) {
|
||||
if (_cache[i].pkt == packet) { return i; }
|
||||
}
|
||||
throw Entry_not_found();
|
||||
}
|
||||
|
||||
void _free(unsigned const id) { _cache[id].pkt = 0; }
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct an empty cache
|
||||
*/
|
||||
Request_cache() {
|
||||
for (unsigned i = 0; i < MAX; i++) { _free(i); } }
|
||||
|
||||
/**
|
||||
* Fill a free entry with packet base 'pkt' and request base 'req'
|
||||
*
|
||||
* \throw Full
|
||||
*/
|
||||
void insert(void * const pkt, void * const req)
|
||||
{
|
||||
try {
|
||||
unsigned const id = _find(0);
|
||||
_cache[id] = { pkt, req };
|
||||
|
||||
} catch (Entry_not_found) { throw Full(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Free entry of packet 'pkt' and return corresponding request in 'req'
|
||||
*/
|
||||
void remove(void * const pkt, void ** const req)
|
||||
{
|
||||
try {
|
||||
unsigned const id = _find(pkt);
|
||||
*req = _cache[id].req;
|
||||
_free(id);
|
||||
|
||||
} catch (Entry_not_found) { }
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A block device that is addressable by the VM
|
||||
*/
|
||||
class Device
|
||||
{
|
||||
public:
|
||||
|
||||
enum {
|
||||
TX_BUF_SIZE = 5 * 1024 * 1024,
|
||||
MAX_NAME_LEN = 64,
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Request_cache _cache;
|
||||
Allocator_avl _alloc;
|
||||
Block::Connection _session;
|
||||
size_t _blk_size;
|
||||
Block::sector_t _blk_cnt;
|
||||
Block::Session::Operations _blk_ops;
|
||||
Native_capability _irq_cap;
|
||||
Signal_context _tx;
|
||||
char _name[MAX_NAME_LEN];
|
||||
unsigned const _irq;
|
||||
bool _writeable;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a device with name 'name' and interrupt 'irq'
|
||||
*/
|
||||
Device(const char * const name, unsigned const irq)
|
||||
:
|
||||
_alloc(env()->heap()),
|
||||
_session(&_alloc, TX_BUF_SIZE, name), _irq(irq)
|
||||
{
|
||||
_session.info(&_blk_cnt, &_blk_size, &_blk_ops);
|
||||
_writeable = _blk_ops.supported(Block::Packet_descriptor::WRITE);
|
||||
strncpy(_name, name, sizeof(_name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Accessors
|
||||
*/
|
||||
|
||||
Request_cache * cache() { return &_cache; }
|
||||
Block::Connection * session() { return &_session; }
|
||||
Signal_context * context() { return &_tx; }
|
||||
size_t block_size() { return _blk_size; }
|
||||
size_t block_count() { return _blk_cnt; }
|
||||
bool writeable() { return _writeable; }
|
||||
const char * name() { return _name; }
|
||||
unsigned irq() { return _irq; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Registry of all block devices that are addressable by the VM
|
||||
*/
|
||||
class Device_registry
|
||||
{
|
||||
public:
|
||||
|
||||
class Bad_device_id : public Exception { };
|
||||
|
||||
private:
|
||||
|
||||
Device ** _devs;
|
||||
unsigned _count;
|
||||
|
||||
void _init_devs(Xml_node config, unsigned const node_id)
|
||||
{
|
||||
if (!config.sub_node(node_id).has_type("block")) { return; }
|
||||
|
||||
char label[Device::MAX_NAME_LEN];
|
||||
config.sub_node(node_id).attribute("label").value(label, sizeof(label));
|
||||
|
||||
unsigned irq = ~0;
|
||||
config.sub_node(node_id).attribute("irq").value(&irq);
|
||||
|
||||
static unsigned dev_id = 0;
|
||||
_devs[dev_id] = new (env()->heap()) Device(label, irq);
|
||||
dev_id++;
|
||||
}
|
||||
|
||||
void _init_count(Xml_node config, unsigned const node_id)
|
||||
{
|
||||
if (!config.sub_node(node_id).has_type("block")) { return; }
|
||||
_count++;
|
||||
}
|
||||
|
||||
void _init()
|
||||
{
|
||||
Xml_node config = Genode::config()->xml_node();
|
||||
size_t node_cnt = config.num_sub_nodes();
|
||||
|
||||
for (unsigned i = 0; i < node_cnt; i++) { _init_count(config, i); }
|
||||
if (_count == 0) { return; }
|
||||
|
||||
size_t const size = _count * sizeof(Device *);
|
||||
_devs = (Device **)env()->heap()->alloc(size);
|
||||
|
||||
for (unsigned i = 0; i < node_cnt; i++) { _init_devs(config, i); }
|
||||
}
|
||||
|
||||
Device_registry()
|
||||
{
|
||||
try { _init(); }
|
||||
catch(...) { Genode::error("blk: config parsing error"); }
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static Device_registry * singleton()
|
||||
{
|
||||
static Device_registry s;
|
||||
return &s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return device with ID 'id' if existent
|
||||
*
|
||||
* \throw Bad_device_id
|
||||
*/
|
||||
Device * dev(unsigned const id) const
|
||||
{
|
||||
if (id >= _count) { throw Bad_device_id(); }
|
||||
return _devs[id];
|
||||
}
|
||||
|
||||
/*
|
||||
* Accessors
|
||||
*/
|
||||
|
||||
unsigned count() const { return _count; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Thread that listens to device interrupts and propagates them to a VM
|
||||
*/
|
||||
class Callback : public Thread_deprecated<8192>
|
||||
{
|
||||
private:
|
||||
|
||||
Lock _ready_lock;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
* If we want to support multiple VMs at a time, this should be part of
|
||||
* the requests that are saved in the device request-cache.
|
||||
*/
|
||||
Vm_base * const _vm;
|
||||
|
||||
/*
|
||||
* Thread interface
|
||||
*/
|
||||
|
||||
void entry()
|
||||
{
|
||||
Signal_receiver receiver;
|
||||
unsigned const count = Device_registry::singleton()->count();
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
Device * const dev = Device_registry::singleton()->dev(i);
|
||||
Signal_context_capability cap(receiver.manage(dev->context()));
|
||||
dev->session()->tx_channel()->sigh_ready_to_submit(cap);
|
||||
dev->session()->tx_channel()->sigh_ack_avail(cap);
|
||||
}
|
||||
|
||||
_ready_lock.unlock();
|
||||
|
||||
while (true) {
|
||||
Signal s = receiver.wait_for_signal();
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
Device * const dev = Device_registry::singleton()->dev(i);
|
||||
if (dev->context() == s.context()) {
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
* If we want to support multiple VMs, this should
|
||||
* be read from the corresponding request.
|
||||
*/
|
||||
_vm->inject_irq(dev->irq());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a callback thread for VM 'vm'
|
||||
*/
|
||||
Callback(Vm_base * const vm)
|
||||
:
|
||||
Thread_deprecated<8192>("blk-signal-thread"),
|
||||
_ready_lock(Lock::LOCKED),
|
||||
_vm(vm)
|
||||
{
|
||||
Thread::start();
|
||||
_ready_lock.lock();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Vmm::Block implementation
|
||||
*/
|
||||
|
||||
void Vmm::Block::_buf_to_pkt(void * const dst, size_t const sz)
|
||||
{
|
||||
if (sz > _buf_size) { throw Oversized_request(); }
|
||||
memcpy(dst, _buf, sz);
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_name(Vm_base * const vm)
|
||||
{
|
||||
try {
|
||||
unsigned const id = vm->smc_arg_2();
|
||||
Device * const dev = Device_registry::singleton()->dev(id);
|
||||
strncpy((char *)_buf, dev->name(), _buf_size);
|
||||
|
||||
} catch (Device_registry::Bad_device_id) {
|
||||
Genode::error("bad block device ID");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_block_count(Vm_base * const vm)
|
||||
{
|
||||
try {
|
||||
unsigned const id = vm->smc_arg_2();
|
||||
Device * const dev = Device_registry::singleton()->dev(id);
|
||||
vm->smc_ret(dev->block_count());
|
||||
|
||||
} catch (Device_registry::Bad_device_id) {
|
||||
Genode::error("bad block device ID");
|
||||
vm->smc_ret(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_block_size(Vm_base * const vm)
|
||||
{
|
||||
try {
|
||||
unsigned const id = vm->smc_arg_2();
|
||||
Device * const dev = Device_registry::singleton()->dev(id);
|
||||
vm->smc_ret(dev->block_size());
|
||||
|
||||
} catch (Device_registry::Bad_device_id) {
|
||||
Genode::error("bad block device ID");
|
||||
vm->smc_ret(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_queue_size(Vm_base * const vm)
|
||||
{
|
||||
try {
|
||||
unsigned const id = vm->smc_arg_2();
|
||||
Device * const dev = Device_registry::singleton()->dev(id);
|
||||
vm->smc_ret(dev->session()->tx()->bulk_buffer_size());
|
||||
return;
|
||||
|
||||
} catch (Device_registry::Bad_device_id) { Genode::error("bad block device ID"); }
|
||||
vm->smc_ret(0);
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_writeable(Vm_base * const vm)
|
||||
{
|
||||
try {
|
||||
unsigned const id = vm->smc_arg_2();
|
||||
Device * const dev = Device_registry::singleton()->dev(id);
|
||||
vm->smc_ret(dev->writeable());
|
||||
|
||||
} catch (Device_registry::Bad_device_id) {
|
||||
Genode::error("bad block device ID");
|
||||
vm->smc_ret(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_irq(Vm_base * const vm)
|
||||
{
|
||||
try {
|
||||
unsigned const id = vm->smc_arg_2();
|
||||
Device * const dev = Device_registry::singleton()->dev(id);
|
||||
vm->smc_ret(dev->irq());
|
||||
|
||||
} catch (Device_registry::Bad_device_id) {
|
||||
Genode::error("bad block device ID");
|
||||
vm->smc_ret(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_buffer(Vm_base * const vm)
|
||||
{
|
||||
addr_t const buf_base = vm->smc_arg_2();
|
||||
_buf_size = vm->smc_arg_3();
|
||||
addr_t const buf_top = buf_base + _buf_size;
|
||||
Ram * const ram = vm->ram();
|
||||
addr_t const ram_top = ram->base() + ram->size();
|
||||
|
||||
bool buf_err;
|
||||
buf_err = buf_top <= buf_base;
|
||||
buf_err |= buf_base < ram->base();
|
||||
buf_err |= buf_top >= ram_top;
|
||||
if (buf_err) {
|
||||
Genode::error("illegal block buffer constraints");
|
||||
return;
|
||||
}
|
||||
addr_t const buf_off = buf_base - ram->base();
|
||||
_buf = (void *)(ram->local() + buf_off);
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_start_callback(Vm_base * const vm) {
|
||||
static Callback c(vm); }
|
||||
|
||||
|
||||
void Vmm::Block::_device_count(Vm_base * const vm)
|
||||
{
|
||||
vm->smc_ret(Device_registry::singleton()->count());
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_new_request(Vm_base * const vm)
|
||||
{
|
||||
unsigned const id = vm->smc_arg_2();
|
||||
unsigned long const sz = vm->smc_arg_3();
|
||||
void * const req = (void*)vm->smc_arg_4();
|
||||
try {
|
||||
Device * const dev = Device_registry::singleton()->dev(id);
|
||||
::Block::Connection * session = dev->session();
|
||||
::Block::Packet_descriptor p = session->tx()->alloc_packet(sz);
|
||||
void *addr = session->tx()->packet_content(p);
|
||||
dev->cache()->insert(addr, req);
|
||||
vm->smc_ret((long)addr, p.offset());
|
||||
|
||||
} catch (Request_cache::Full) {
|
||||
Genode::error("block request cache full");
|
||||
vm->smc_ret(0, 0);
|
||||
|
||||
} catch (::Block::Session::Tx::Source::Packet_alloc_failed) {
|
||||
Genode::error("failed to allocate packet for block request");
|
||||
vm->smc_ret(0, 0);
|
||||
|
||||
} catch (Device_registry::Bad_device_id) {
|
||||
Genode::error("bad block device ID");
|
||||
vm->smc_ret(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_submit_request(Vm_base * const vm)
|
||||
{
|
||||
unsigned const id = vm->smc_arg_2();
|
||||
unsigned long const queue_offset = vm->smc_arg_3();
|
||||
unsigned long const size = vm->smc_arg_4();
|
||||
int const write = vm->smc_arg_7();
|
||||
void * const dst = (void *)vm->smc_arg_8();
|
||||
|
||||
unsigned long long const disc_offset =
|
||||
(unsigned long long)vm->smc_arg_5() << 32 | vm->smc_arg_6();
|
||||
|
||||
try {
|
||||
Device * const dev = Device_registry::singleton()->dev(id);
|
||||
if (write) { _buf_to_pkt(dst, size); }
|
||||
|
||||
size_t sector = disc_offset / dev->block_size();
|
||||
size_t sector_cnt = size / dev->block_size();
|
||||
|
||||
using ::Block::Packet_descriptor;
|
||||
Packet_descriptor p(
|
||||
Packet_descriptor(queue_offset, size),
|
||||
write ? Packet_descriptor::WRITE : Packet_descriptor::READ,
|
||||
sector, sector_cnt
|
||||
);
|
||||
dev->session()->tx()->submit_packet(p);
|
||||
|
||||
} catch (Oversized_request) {
|
||||
Genode::error("oversized block request");
|
||||
|
||||
} catch (Device_registry::Bad_device_id) {
|
||||
Genode::error("bad block device ID");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::_collect_reply(Vm_base * const vm)
|
||||
{
|
||||
try {
|
||||
/* lookup device */
|
||||
unsigned const id = vm->smc_arg_2();
|
||||
Device * const dev = Device_registry::singleton()->dev(id);
|
||||
::Block::Connection * const con = dev->session();
|
||||
|
||||
/* get next packet/request pair and release invalid packets */
|
||||
typedef ::Block::Packet_descriptor Packet;
|
||||
void * rq = 0;
|
||||
Packet pkt;
|
||||
for (; !rq; con->tx()->release_packet(pkt)) {
|
||||
|
||||
/* check for packets and tell VM to stop if none available */
|
||||
if (!con->tx()->ack_avail()) {
|
||||
vm->smc_ret(0);
|
||||
return;
|
||||
}
|
||||
/* lookup request of next packet and free cache slot */
|
||||
pkt = con->tx()->get_acked_packet();
|
||||
dev->cache()->remove(con->tx()->packet_content(pkt), &rq);
|
||||
}
|
||||
/* get packet values */
|
||||
void * const dat = con->tx()->packet_content(pkt);
|
||||
bool const w = pkt.operation() == Packet::WRITE;
|
||||
size_t const dat_sz = pkt.size();
|
||||
|
||||
/* communicate response, release packet and tell VM to continue */
|
||||
if (Reply::size(dat_sz) > _buf_size) { throw Oversized_request(); }
|
||||
construct_at<Reply>(_buf, rq, w, dat_sz, dat);
|
||||
con->tx()->release_packet(pkt);
|
||||
vm->smc_ret(1);
|
||||
|
||||
} catch (Oversized_request) {
|
||||
Genode::error("oversized block request");
|
||||
vm->smc_ret(-1);
|
||||
|
||||
} catch (Device_registry::Bad_device_id) {
|
||||
Genode::error("bad block device ID");
|
||||
vm->smc_ret(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Vmm::Block::handle(Vm_base * const vm)
|
||||
{
|
||||
enum {
|
||||
DEVICE_COUNT = 0,
|
||||
BLOCK_COUNT = 1,
|
||||
BLOCK_SIZE = 2,
|
||||
WRITEABLE = 3,
|
||||
QUEUE_SIZE = 4,
|
||||
IRQ = 5,
|
||||
START_CALLBACK = 6,
|
||||
NEW_REQUEST = 7,
|
||||
SUBMIT_REQUEST = 8,
|
||||
COLLECT_REPLY = 9,
|
||||
BUFFER = 10,
|
||||
NAME = 11,
|
||||
};
|
||||
switch (vm->smc_arg_1()) {
|
||||
case DEVICE_COUNT: _device_count(vm); break;
|
||||
case BLOCK_COUNT: _block_count(vm); break;
|
||||
case BLOCK_SIZE: _block_size(vm); break;
|
||||
case WRITEABLE: _writeable(vm); break;
|
||||
case QUEUE_SIZE: _queue_size(vm); break;
|
||||
case IRQ: _irq(vm); break;
|
||||
case START_CALLBACK: _start_callback(vm); break;
|
||||
case NEW_REQUEST: _new_request(vm); break;
|
||||
case SUBMIT_REQUEST: _submit_request(vm); break;
|
||||
case COLLECT_REPLY: _collect_reply(vm); break;
|
||||
case BUFFER: _buffer(vm); break;
|
||||
case NAME: _name(vm); break;
|
||||
default:
|
||||
Genode::error("Unknown function ", vm->smc_arg_1(), " requested on VMM block");
|
||||
break;
|
||||
}
|
||||
}
|
315
repos/os/src/server/tz_vmm/block_driver.cc
Normal file
315
repos/os/src/server/tz_vmm/block_driver.cc
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* \brief Paravirtualized access to block devices for VMs
|
||||
* \author Martin Stein
|
||||
* \date 2015-10-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <block_driver.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/construct_at.h>
|
||||
#include <util/xml_node.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/*********************************
|
||||
** Block_driver::Request_cache **
|
||||
*********************************/
|
||||
|
||||
unsigned Block_driver::Request_cache::_find(void *pkt)
|
||||
{
|
||||
for (unsigned i = 0; i < NR_OF_ENTRIES; i++) {
|
||||
if (_entries[i].pkt == pkt) {
|
||||
return i; }
|
||||
}
|
||||
throw No_matching_entry();
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::Request_cache::insert(void *pkt, void *req)
|
||||
{
|
||||
try { _entries[_find(nullptr)] = { pkt, req }; }
|
||||
catch (No_matching_entry) { throw Full(); }
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::Request_cache::remove(void *pkt, void **req)
|
||||
{
|
||||
try {
|
||||
unsigned const id = _find(pkt);
|
||||
*req = _entries[id].req;
|
||||
_free(id);
|
||||
}
|
||||
catch (No_matching_entry) { }
|
||||
}
|
||||
|
||||
|
||||
/**************************
|
||||
** Block_driver::Device **
|
||||
**************************/
|
||||
|
||||
Block_driver::Device::Device(Entrypoint &ep,
|
||||
Xml_node node,
|
||||
Range_allocator &alloc,
|
||||
Id_space<Device> &id_space,
|
||||
Id id,
|
||||
Vm_base &vm)
|
||||
:
|
||||
_vm(vm), _name(node.attribute_value("name", Name())),
|
||||
_irq(node.attribute_value("irq", ~(unsigned)0)),
|
||||
_irq_handler(ep, *this, &Device::_handle_irq),
|
||||
_session(&alloc, TX_BUF_SIZE, _name.string()),
|
||||
_id_space_elem(*this, id_space, id)
|
||||
{
|
||||
if (_name == Name() || _irq == ~(unsigned)0) {
|
||||
throw Invalid(); }
|
||||
|
||||
_session.info(&_blk_cnt, &_blk_size, &_blk_ops);
|
||||
_writeable = _blk_ops.supported(Packet_descriptor::WRITE);
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::Device::start_irq_handling()
|
||||
{
|
||||
_session.tx_channel()->sigh_ready_to_submit(_irq_handler);
|
||||
_session.tx_channel()->sigh_ack_avail(_irq_handler);
|
||||
}
|
||||
|
||||
|
||||
/******************
|
||||
** Block_driver **
|
||||
******************/
|
||||
|
||||
Block_driver::Block_driver(Entrypoint &ep,
|
||||
Xml_node config,
|
||||
Allocator &alloc,
|
||||
Vm_base &vm) : _dev_alloc(&alloc)
|
||||
{
|
||||
config.for_each_sub_node("block", [&] (Xml_node node) {
|
||||
try { new (alloc) Device(ep, node, _dev_alloc, _devs,
|
||||
Device::Id { _dev_count++ }, vm); }
|
||||
catch (Device::Invalid) { error("invalid block device"); }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::_name(Vm_base &vm)
|
||||
{
|
||||
_dev_apply(Device::Id { vm.smc_arg_2() },
|
||||
[&] (Device &dev) { strncpy((char *)_buf, dev.name().string(), _buf_size); },
|
||||
[&] () { ((char *)_buf)[0] = 0; });
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::_block_count(Vm_base &vm)
|
||||
{
|
||||
_dev_apply(Device::Id { vm.smc_arg_2() },
|
||||
[&] (Device &dev) { vm.smc_ret(dev.block_count()); },
|
||||
[&] () { vm.smc_ret(0); });
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::_block_size(Vm_base &vm)
|
||||
{
|
||||
_dev_apply(Device::Id { vm.smc_arg_2() },
|
||||
[&] (Device &dev) { vm.smc_ret(dev.block_size()); },
|
||||
[&] () { vm.smc_ret(0); });
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::_queue_size(Vm_base &vm)
|
||||
{
|
||||
_dev_apply(Device::Id { vm.smc_arg_2() },
|
||||
[&] (Device &dev) { vm.smc_ret(dev.session().tx()->bulk_buffer_size()); },
|
||||
[&] () { vm.smc_ret(0); });
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::_writeable(Vm_base &vm)
|
||||
{
|
||||
_dev_apply(Device::Id { vm.smc_arg_2() },
|
||||
[&] (Device &dev) { vm.smc_ret(dev.writeable()); },
|
||||
[&] () { vm.smc_ret(false); });
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::_irq(Vm_base &vm)
|
||||
{
|
||||
_dev_apply(Device::Id { vm.smc_arg_2() },
|
||||
[&] (Device &dev) { vm.smc_ret(dev.irq()); },
|
||||
[&] () { vm.smc_ret(~(unsigned)0); });
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::_buffer(Vm_base &vm)
|
||||
{
|
||||
addr_t const buf_base = vm.smc_arg_2();
|
||||
_buf_size = vm.smc_arg_3();
|
||||
addr_t const buf_top = buf_base + _buf_size;
|
||||
Ram const &ram = vm.ram();
|
||||
addr_t const ram_top = ram.base() + ram.size();
|
||||
|
||||
bool buf_err;
|
||||
buf_err = buf_top <= buf_base;
|
||||
buf_err |= buf_base < ram.base();
|
||||
buf_err |= buf_top >= ram_top;
|
||||
if (buf_err) {
|
||||
error("illegal block buffer constraints");
|
||||
return;
|
||||
}
|
||||
addr_t const buf_off = buf_base - ram.base();
|
||||
_buf = (void *)(ram.local() + buf_off);
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::_new_request(Vm_base &vm)
|
||||
{
|
||||
auto dev_func = [&] (Device &dev) {
|
||||
try {
|
||||
size_t const size = vm.smc_arg_3();
|
||||
void *const req = (void*)vm.smc_arg_4();
|
||||
|
||||
Packet_descriptor pkt = dev.session().tx()->alloc_packet(size);
|
||||
void *addr = dev.session().tx()->packet_content(pkt);
|
||||
dev.cache().insert(addr, req);
|
||||
vm.smc_ret((long)addr, pkt.offset());
|
||||
}
|
||||
catch (Request_cache::Full) {
|
||||
error("block request cache full");
|
||||
throw Device_function_failed();
|
||||
}
|
||||
catch (Block::Session::Tx::Source::Packet_alloc_failed) {
|
||||
error("failed to allocate packet for block request");
|
||||
throw Device_function_failed();
|
||||
}
|
||||
};
|
||||
_dev_apply(Device::Id { vm.smc_arg_2() }, dev_func, [&] () { vm.smc_ret(0, 0); });
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::_submit_request(Vm_base &vm)
|
||||
{
|
||||
auto dev_func = [&] (Device &dev) {
|
||||
|
||||
off_t const queue_offset = vm.smc_arg_3();
|
||||
size_t const size = vm.smc_arg_4();
|
||||
bool const write = vm.smc_arg_7();
|
||||
void *const dst = (void *)vm.smc_arg_8();
|
||||
unsigned long long const disc_offset =
|
||||
(unsigned long long)vm.smc_arg_5() << 32 | vm.smc_arg_6();
|
||||
|
||||
if (write) {
|
||||
if (size > _buf_size) {
|
||||
error("oversized block request");
|
||||
throw Device_function_failed();
|
||||
}
|
||||
memcpy(dst, _buf, size);
|
||||
}
|
||||
size_t const sector = disc_offset / dev.block_size();
|
||||
size_t const sector_cnt = size / dev.block_size();
|
||||
Packet_descriptor pkt(Packet_descriptor(queue_offset, size),
|
||||
write ? Packet_descriptor::WRITE :
|
||||
Packet_descriptor::READ,
|
||||
sector, sector_cnt);
|
||||
|
||||
dev.session().tx()->submit_packet(pkt);
|
||||
};
|
||||
_dev_apply(Device::Id { vm.smc_arg_2() }, dev_func, [] () { });
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::_collect_reply(Vm_base &vm)
|
||||
{
|
||||
auto dev_func = [&] (Device &dev) {
|
||||
|
||||
struct Reply
|
||||
{
|
||||
unsigned long _req;
|
||||
unsigned long _write;
|
||||
unsigned long _dat_size;
|
||||
unsigned long _dat[0];
|
||||
|
||||
Reply(void *req, bool write, size_t dat_size, void *dat_src)
|
||||
:
|
||||
_req((unsigned long)req), _write(write), _dat_size(dat_size)
|
||||
{
|
||||
memcpy(_dat, dat_src, dat_size);
|
||||
}
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* get next packet/request pair and release invalid packets */
|
||||
void * req = 0;
|
||||
Packet_descriptor pkt;
|
||||
for (; !req; dev.session().tx()->release_packet(pkt)) {
|
||||
|
||||
/* check for packets and tell VM to stop if none available */
|
||||
if (!dev.session().tx()->ack_avail()) {
|
||||
vm.smc_ret(0);
|
||||
return;
|
||||
}
|
||||
/* lookup request of next packet and free cache slot */
|
||||
pkt = dev.session().tx()->get_acked_packet();
|
||||
dev.cache().remove(dev.session().tx()->packet_content(pkt), &req);
|
||||
}
|
||||
/* get packet values */
|
||||
void *const dat = dev.session().tx()->packet_content(pkt);
|
||||
bool const write = pkt.operation() == Packet_descriptor::WRITE;
|
||||
size_t const dat_size = pkt.size();
|
||||
|
||||
/* communicate response, release packet and tell VM to continue */
|
||||
if (dat_size + sizeof(Reply) > _buf_size) {
|
||||
error("oversized block reply");
|
||||
throw Device_function_failed();
|
||||
}
|
||||
construct_at<Reply>(_buf, req, write, dat_size, dat);
|
||||
dev.session().tx()->release_packet(pkt);
|
||||
vm.smc_ret(1);
|
||||
};
|
||||
_dev_apply(Device::Id { vm.smc_arg_2() }, dev_func, [&] () { vm.smc_ret(-1); });
|
||||
}
|
||||
|
||||
|
||||
void Block_driver::handle_smc(Vm_base &vm)
|
||||
{
|
||||
enum {
|
||||
DEVICE_COUNT = 0,
|
||||
BLOCK_COUNT = 1,
|
||||
BLOCK_SIZE = 2,
|
||||
WRITEABLE = 3,
|
||||
QUEUE_SIZE = 4,
|
||||
IRQ = 5,
|
||||
START_CALLBACK = 6,
|
||||
NEW_REQUEST = 7,
|
||||
SUBMIT_REQUEST = 8,
|
||||
COLLECT_REPLY = 9,
|
||||
BUFFER = 10,
|
||||
NAME = 11,
|
||||
};
|
||||
switch (vm.smc_arg_1()) {
|
||||
case DEVICE_COUNT: vm.smc_ret(_dev_count); break;
|
||||
case BLOCK_COUNT: _block_count(vm); break;
|
||||
case BLOCK_SIZE: _block_size(vm); break;
|
||||
case WRITEABLE: _writeable(vm); break;
|
||||
case QUEUE_SIZE: _queue_size(vm); break;
|
||||
case IRQ: _irq(vm); break;
|
||||
case START_CALLBACK: _devs.for_each<Device>([&] (Device &dev)
|
||||
{ dev.start_irq_handling(); }); break;
|
||||
case NEW_REQUEST: _new_request(vm); break;
|
||||
case SUBMIT_REQUEST: _submit_request(vm); break;
|
||||
case COLLECT_REPLY: _collect_reply(vm); break;
|
||||
case BUFFER: _buffer(vm); break;
|
||||
case NAME: _name(vm); break;
|
||||
default:
|
||||
error("unknown block-driver function ", vm.smc_arg_1());
|
||||
throw Vm_base::Exception_handling_failed();
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* \brief Paravirtualized access to block devices for a Trustzone VM
|
||||
* \author Martin Stein
|
||||
* \date 2015-10-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _TZ_VMM__INCLUDE__BLOCK_H_
|
||||
#define _TZ_VMM__INCLUDE__BLOCK_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <vm_state.h>
|
||||
|
||||
/* local includes */
|
||||
#include <vm_base.h>
|
||||
|
||||
namespace Vmm { class Block; }
|
||||
|
||||
/**
|
||||
* Paravirtualized access to block devices for a Trustzone VM
|
||||
*/
|
||||
class Vmm::Block
|
||||
{
|
||||
private:
|
||||
|
||||
class Oversized_request : public Genode::Exception { };
|
||||
|
||||
void * _buf;
|
||||
Genode::size_t _buf_size;
|
||||
|
||||
void _buf_to_pkt(void * const dst, Genode::size_t const sz);
|
||||
|
||||
void _name(Vm_base * const vm);
|
||||
|
||||
void _block_count(Vm_base * const vm);
|
||||
|
||||
void _block_size(Vm_base * const vm);
|
||||
|
||||
void _queue_size(Vm_base * const vm);
|
||||
|
||||
void _writeable(Vm_base * const vm);
|
||||
|
||||
void _irq(Vm_base * const vm);
|
||||
|
||||
void _buffer(Vm_base * const vm);
|
||||
|
||||
void _start_callback(Vm_base * const vm);
|
||||
|
||||
void _device_count(Vm_base * const vm);
|
||||
|
||||
void _new_request(Vm_base * const vm);
|
||||
|
||||
void _submit_request(Vm_base * const vm);
|
||||
|
||||
void _collect_reply(Vm_base * const vm);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Handle Secure Monitor Call of VM 'vm' on VMM block
|
||||
*/
|
||||
void handle(Vm_base * const vm);
|
||||
|
||||
Block() : _buf_size(0) { }
|
||||
};
|
||||
|
||||
#endif /* _TZ_VMM__INCLUDE__BLOCK_H_ */
|
154
repos/os/src/server/tz_vmm/include/block_driver.h
Normal file
154
repos/os/src/server/tz_vmm/include/block_driver.h
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* \brief Paravirtualized access to block devices for VMs
|
||||
* \author Martin Stein
|
||||
* \date 2015-10-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _BLOCK_DRIVER_H_
|
||||
#define _BLOCK_DRIVER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <block_session/connection.h>
|
||||
#include <base/allocator_avl.h>
|
||||
|
||||
/* local includes */
|
||||
#include <vm_base.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
class Xml_node;
|
||||
class Block_driver;
|
||||
}
|
||||
|
||||
|
||||
class Genode::Block_driver
|
||||
{
|
||||
private:
|
||||
|
||||
using Packet_descriptor = Block::Packet_descriptor;
|
||||
|
||||
struct Device_function_failed : Exception { };
|
||||
|
||||
class Request_cache
|
||||
{
|
||||
private:
|
||||
|
||||
enum { NR_OF_ENTRIES = Block::Session::TX_QUEUE_SIZE };
|
||||
|
||||
struct No_matching_entry : Exception { };
|
||||
|
||||
struct Entry { void *pkt; void *req; } _entries[NR_OF_ENTRIES];
|
||||
|
||||
unsigned _find(void *packet);
|
||||
void _free(unsigned id) { _entries[id].pkt = 0; }
|
||||
|
||||
public:
|
||||
|
||||
struct Full : Exception { };
|
||||
|
||||
Request_cache() {
|
||||
for (unsigned i = 0; i < NR_OF_ENTRIES; i++) { _free(i); } }
|
||||
|
||||
void insert(void *pkt, void *req);
|
||||
void remove(void *pkt, void **req);
|
||||
};
|
||||
|
||||
class Device
|
||||
{
|
||||
public:
|
||||
|
||||
enum { TX_BUF_SIZE = 5 * 1024 * 1024 };
|
||||
|
||||
using Id = Id_space<Device>::Id;
|
||||
using Name = String<64>;
|
||||
|
||||
private:
|
||||
|
||||
Request_cache _cache;
|
||||
Vm_base &_vm;
|
||||
Name const _name;
|
||||
unsigned const _irq;
|
||||
Signal_handler<Device> _irq_handler;
|
||||
Block::Connection _session;
|
||||
Id_space<Device>::Element _id_space_elem;
|
||||
size_t _blk_size;
|
||||
Block::sector_t _blk_cnt;
|
||||
Block::Session::Operations _blk_ops;
|
||||
bool _writeable;
|
||||
|
||||
public:
|
||||
|
||||
void _handle_irq() { _vm.inject_irq(_irq); }
|
||||
|
||||
public:
|
||||
|
||||
struct Invalid : Exception { };
|
||||
|
||||
Device(Entrypoint &ep,
|
||||
Xml_node node,
|
||||
Range_allocator &alloc,
|
||||
Id_space<Device> &id_space,
|
||||
Id id,
|
||||
Vm_base &vm);
|
||||
|
||||
void start_irq_handling();
|
||||
|
||||
Request_cache &cache() { return _cache; }
|
||||
Block::Connection &session() { return _session; }
|
||||
size_t block_size() const { return _blk_size; }
|
||||
size_t block_count() const { return _blk_cnt; }
|
||||
bool writeable() const { return _writeable; }
|
||||
Name const &name() const { return _name; }
|
||||
unsigned irq() const { return _irq; }
|
||||
};
|
||||
|
||||
void *_buf = nullptr;
|
||||
size_t _buf_size = 0;
|
||||
Id_space<Device> _devs;
|
||||
unsigned _dev_count = 0;
|
||||
Allocator_avl _dev_alloc;
|
||||
|
||||
void _buf_to_pkt(void *dst, size_t sz);
|
||||
void _name(Vm_base &vm);
|
||||
void _block_count(Vm_base &vm);
|
||||
void _block_size(Vm_base &vm);
|
||||
void _queue_size(Vm_base &vm);
|
||||
void _writeable(Vm_base &vm);
|
||||
void _irq(Vm_base &vm);
|
||||
void _buffer(Vm_base &vm);
|
||||
void _device_count(Vm_base &vm);
|
||||
void _new_request(Vm_base &vm);
|
||||
void _submit_request(Vm_base &vm);
|
||||
void _collect_reply(Vm_base &vm);
|
||||
|
||||
template <typename DEV_FUNC, typename ERR_FUNC>
|
||||
void _dev_apply(Device::Id id,
|
||||
DEV_FUNC const &dev_func,
|
||||
ERR_FUNC const &err_func)
|
||||
{
|
||||
try { _devs.apply<Device>(id, [&] (Device &dev) { dev_func(dev); }); }
|
||||
catch (Id_space<Device>::Unknown_id) {
|
||||
error("unknown block device ", id);
|
||||
err_func();
|
||||
}
|
||||
catch (Device_function_failed) { err_func(); }
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void handle_smc(Vm_base &vm);
|
||||
|
||||
Block_driver(Entrypoint &ep,
|
||||
Xml_node config,
|
||||
Allocator &alloc,
|
||||
Vm_base &vm);
|
||||
};
|
||||
|
||||
#endif /* _BLOCK_DRIVER_H_ */
|
@ -22,10 +22,10 @@ class Mmu
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Vm_state *_state;
|
||||
Ram *_ram;
|
||||
Genode::Vm_state &_state;
|
||||
Ram const &_ram;
|
||||
|
||||
unsigned _n_bits() { return _state->ttbrc & 0x7; }
|
||||
unsigned _n_bits() { return _state.ttbrc & 0x7; }
|
||||
|
||||
bool _ttbr0(Genode::addr_t mva) {
|
||||
return (!_n_bits() || !(mva >> (32 - _n_bits()))); }
|
||||
@ -33,10 +33,10 @@ class Mmu
|
||||
Genode::addr_t _first_level(Genode::addr_t va)
|
||||
{
|
||||
if (!_ttbr0(va))
|
||||
return ((_state->ttbr[1] & 0xffffc000) |
|
||||
return ((_state.ttbr[1] & 0xffffc000) |
|
||||
((va >> 18) & 0xffffc));
|
||||
unsigned shift = 14 - _n_bits();
|
||||
return (((_state->ttbr[0] >> shift) << shift) |
|
||||
return (((_state.ttbr[0] >> shift) << shift) |
|
||||
(((va << _n_bits()) >> (18 + _n_bits())) & 0x3ffc));
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ class Mmu
|
||||
{
|
||||
enum Type { FAULT, LARGE, SMALL };
|
||||
|
||||
Genode::addr_t se = *((Genode::addr_t*)_ram->va(((fe & (~0UL << 10)) |
|
||||
Genode::addr_t se = *((Genode::addr_t*)_ram.va(((fe & (~0UL << 10)) |
|
||||
((va >> 10) & 0x3fc))));
|
||||
switch (se & 0x3) {
|
||||
case FAULT:
|
||||
@ -68,7 +68,7 @@ class Mmu
|
||||
|
||||
public:
|
||||
|
||||
Mmu(Genode::Vm_state *state, Ram *ram)
|
||||
Mmu(Genode::Vm_state &state, Ram const &ram)
|
||||
: _state(state), _ram(ram) {}
|
||||
|
||||
|
||||
@ -76,7 +76,7 @@ class Mmu
|
||||
{
|
||||
enum Type { FAULT, PAGETABLE, SECTION };
|
||||
|
||||
Genode::addr_t fe = *((Genode::addr_t*)_ram->va(_first_level(va)));
|
||||
Genode::addr_t fe = *((Genode::addr_t*)_ram.va(_first_level(va)));
|
||||
switch (fe & 0x3) {
|
||||
case PAGETABLE:
|
||||
return _page(fe, va);
|
||||
|
@ -15,6 +15,7 @@
|
||||
#define _SRC__SERVER__VMM__INCLUDE__RAM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/attached_io_mem_dataspace.h>
|
||||
#include <base/stdint.h>
|
||||
#include <base/exception.h>
|
||||
|
||||
@ -22,22 +23,25 @@ class Ram {
|
||||
|
||||
private:
|
||||
|
||||
Genode::addr_t _base;
|
||||
Genode::size_t _size;
|
||||
Genode::addr_t _local;
|
||||
Genode::Attached_io_mem_dataspace _ds;
|
||||
Genode::addr_t const _base;
|
||||
Genode::size_t const _size;
|
||||
Genode::addr_t const _local;
|
||||
|
||||
public:
|
||||
|
||||
class Invalid_addr : Genode::Exception {};
|
||||
|
||||
Ram(Genode::addr_t addr, Genode::size_t sz, Genode::addr_t local)
|
||||
: _base(addr), _size(sz), _local(local) { }
|
||||
Ram(Genode::Env &env, Genode::addr_t base, Genode::size_t size)
|
||||
:
|
||||
_ds(env, base, size), _base(base), _size(size),
|
||||
_local((Genode::addr_t)_ds.local_addr<void>()) { }
|
||||
|
||||
Genode::addr_t base() { return _base; }
|
||||
Genode::size_t size() { return _size; }
|
||||
Genode::addr_t local() { return _local; }
|
||||
Genode::addr_t base() const { return _base; }
|
||||
Genode::size_t size() const { return _size; }
|
||||
Genode::addr_t local() const { return _local; }
|
||||
|
||||
Genode::addr_t va(Genode::addr_t phys)
|
||||
Genode::addr_t va(Genode::addr_t phys) const
|
||||
{
|
||||
if ((phys < _base) || (phys > (_base + _size)))
|
||||
throw Invalid_addr();
|
||||
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* \brief Paravirtualized access to serial devices for a Trustzone VM
|
||||
* \author Martin Stein
|
||||
* \date 2015-10-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _TZ_VMM__INCLUDE__SERIAL_H_
|
||||
#define _TZ_VMM__INCLUDE__SERIAL_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <vm_base.h>
|
||||
#include <os/attached_ram_dataspace.h>
|
||||
|
||||
namespace Vmm { class Serial; }
|
||||
|
||||
/**
|
||||
* Paravirtualized access to serial devices for a Trustzone VM
|
||||
*/
|
||||
class Vmm::Serial : private Genode::Attached_ram_dataspace
|
||||
{
|
||||
private:
|
||||
|
||||
enum {
|
||||
BUF_SIZE = 4096,
|
||||
WRAP = BUF_SIZE - sizeof(char),
|
||||
};
|
||||
|
||||
Genode::addr_t _off;
|
||||
|
||||
void _push(char const c);
|
||||
|
||||
void _flush();
|
||||
|
||||
void _send(Vm_base * const vm);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Handle Secure Monitor Call of VM 'vm' on VMM serial
|
||||
*/
|
||||
void handle(Vm_base * const vm);
|
||||
|
||||
Serial();
|
||||
};
|
||||
|
||||
#endif /* _TZ_VMM__INCLUDE__SERIAL_H_ */
|
46
repos/os/src/server/tz_vmm/include/serial_driver.h
Normal file
46
repos/os/src/server/tz_vmm/include/serial_driver.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* \brief Paravirtualized access to serial device for a Trustzone VM
|
||||
* \author Martin Stein
|
||||
* \date 2015-10-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _SERIAL_DRIVER_H_
|
||||
#define _SERIAL_DRIVER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/attached_ram_dataspace.h>
|
||||
|
||||
/* local includes */
|
||||
#include <vm_base.h>
|
||||
|
||||
namespace Genode { class Serial_driver; }
|
||||
|
||||
|
||||
class Genode::Serial_driver
|
||||
{
|
||||
private:
|
||||
|
||||
enum { BUF_SIZE = 4096 };
|
||||
|
||||
Attached_ram_dataspace _buf;
|
||||
addr_t _off = 0;
|
||||
|
||||
void _push(char c) { _buf.local_addr<char>()[_off++] = c; }
|
||||
void _flush();
|
||||
void _send(Vm_base &vm);
|
||||
|
||||
public:
|
||||
|
||||
void handle_smc(Vm_base &vm);
|
||||
|
||||
Serial_driver(Ram_session &ram) : _buf(&ram, BUF_SIZE) { }
|
||||
};
|
||||
|
||||
#endif /* _SERIAL_DRIVER_H_ */
|
@ -1,206 +1,119 @@
|
||||
/*
|
||||
* \brief Virtual Machine Monitor VM definition
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin Stein
|
||||
* \date 2012-06-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2013 Genode Labs GmbH
|
||||
* Copyright (C) 2012-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__SERVER__VMM__INCLUDE__VM_H_
|
||||
#define _SRC__SERVER__VMM__INCLUDE__VM_H_
|
||||
#ifndef _VM_BASE_H_
|
||||
#define _VM_BASE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <dataspace/client.h>
|
||||
#include <io_mem_session/connection.h>
|
||||
#include <rom_session/connection.h>
|
||||
#include <vm_session/connection.h>
|
||||
#include <util/noncopyable.h>
|
||||
|
||||
/* local includes */
|
||||
#include <mmu.h>
|
||||
#include <vm_state.h>
|
||||
#include <ram.h>
|
||||
|
||||
class Vm_base {
|
||||
namespace Genode
|
||||
{
|
||||
class Board_revision;
|
||||
class Vm_base;
|
||||
class Machine_type;
|
||||
}
|
||||
|
||||
struct Genode::Board_revision
|
||||
{
|
||||
unsigned long value;
|
||||
|
||||
explicit Board_revision(unsigned long value) : value(value) { }
|
||||
};
|
||||
|
||||
|
||||
struct Genode::Machine_type
|
||||
{
|
||||
unsigned long value;
|
||||
|
||||
explicit Machine_type(unsigned long value) : value(value) { }
|
||||
};
|
||||
|
||||
|
||||
class Genode::Vm_base : Noncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
using Kernel_name = String<32>;
|
||||
using Command_line = String<64>;
|
||||
|
||||
protected:
|
||||
|
||||
Genode::Vm_connection _vm_con;
|
||||
Genode::Rom_connection _kernel_rom;
|
||||
Genode::Dataspace_client _kernel_cap;
|
||||
const char* _cmdline;
|
||||
Genode::Vm_state *_state;
|
||||
Genode::Io_mem_connection _ram_iomem;
|
||||
Ram _ram;
|
||||
Genode::addr_t _kernel_offset;
|
||||
unsigned long _mach_type;
|
||||
unsigned long _board_rev;
|
||||
Env &_env;
|
||||
Kernel_name const &_kernel;
|
||||
Command_line const &_cmdline;
|
||||
off_t const _kernel_off;
|
||||
Machine_type const _machine;
|
||||
Board_revision const _board;
|
||||
Ram const _ram;
|
||||
Vm_connection _vm { _env };
|
||||
Vm_state &_state { *(Vm_state*)_env.rm()
|
||||
.attach(_vm.cpu_state()) };
|
||||
|
||||
void _load_kernel()
|
||||
{
|
||||
using namespace Genode;
|
||||
void _load_kernel();
|
||||
|
||||
addr_t addr = env()->rm_session()->attach(_kernel_cap);
|
||||
memcpy((void*)(_ram.local() + _kernel_offset),
|
||||
(void*)addr, _kernel_cap.size());
|
||||
_state->ip = _ram.base() + _kernel_offset;
|
||||
env()->rm_session()->detach((void*)addr);
|
||||
}
|
||||
|
||||
virtual void _load_kernel_surroundings() = 0;
|
||||
virtual Genode::addr_t _board_info_offset() const = 0;
|
||||
virtual void _load_kernel_surroundings() = 0;
|
||||
virtual addr_t _board_info_offset() const = 0;
|
||||
|
||||
public:
|
||||
|
||||
class Inject_irq_failed : public Genode::Exception { };
|
||||
struct Inject_irq_failed : Exception { };
|
||||
struct Exception_handling_failed : Exception { };
|
||||
|
||||
Vm_base(const char *kernel, const char *cmdline,
|
||||
Genode::addr_t ram_base, Genode::size_t ram_size,
|
||||
Genode::addr_t kernel_offset, unsigned long mach_type,
|
||||
unsigned long board_rev = 0)
|
||||
: _kernel_rom(kernel),
|
||||
_kernel_cap(_kernel_rom.dataspace()),
|
||||
_cmdline(cmdline),
|
||||
_state((Genode::Vm_state*)Genode::env()->rm_session()->attach(_vm_con.cpu_state())),
|
||||
_ram_iomem(ram_base, ram_size),
|
||||
_ram(ram_base, ram_size, (Genode::addr_t)Genode::env()->rm_session()->attach(_ram_iomem.dataspace())),
|
||||
_kernel_offset(kernel_offset),
|
||||
_mach_type(mach_type),
|
||||
_board_rev(board_rev)
|
||||
{
|
||||
_state->irq_injection = 0;
|
||||
}
|
||||
Vm_base(Env &env,
|
||||
Kernel_name const &kernel,
|
||||
Command_line const &cmdline,
|
||||
addr_t ram_base,
|
||||
size_t ram_size,
|
||||
off_t kernel_off,
|
||||
Machine_type machine,
|
||||
Board_revision board);
|
||||
|
||||
void start()
|
||||
{
|
||||
Genode::memset((void*)_state, 0, sizeof(Genode::Vm_state));
|
||||
_load_kernel();
|
||||
_load_kernel_surroundings();
|
||||
_state->cpsr = 0x93; /* SVC mode and IRQs disabled */
|
||||
_state->r0 = 0;
|
||||
_state->r1 = _mach_type;
|
||||
_state->r2 = _ram.base() + _board_info_offset();
|
||||
}
|
||||
void exception_handler(Signal_context_capability handler) {
|
||||
_vm.exception_handler(handler); }
|
||||
|
||||
void sig_handler(Genode::Signal_context_capability sig_cap) {
|
||||
_vm_con.exception_handler(sig_cap); }
|
||||
void run() { _vm.run(); }
|
||||
void pause() { _vm.pause(); }
|
||||
|
||||
void run() { _vm_con.run(); }
|
||||
void pause() { _vm_con.pause(); }
|
||||
void start();
|
||||
void dump();
|
||||
void inject_irq(unsigned irq);
|
||||
addr_t va_to_pa(addr_t va);
|
||||
|
||||
void inject_irq(unsigned const irq)
|
||||
{
|
||||
if (_state->irq_injection) { throw Inject_irq_failed(); }
|
||||
_state->irq_injection = irq;
|
||||
}
|
||||
Vm_state const &state() const { return _state; }
|
||||
Ram const &ram() const { return _ram; }
|
||||
|
||||
void dump()
|
||||
{
|
||||
using namespace Genode;
|
||||
addr_t smc_arg_0() { return _state.r0; }
|
||||
addr_t smc_arg_1() { return _state.r1; }
|
||||
addr_t smc_arg_2() { return _state.r2; }
|
||||
addr_t smc_arg_3() { return _state.r3; }
|
||||
addr_t smc_arg_4() { return _state.r4; }
|
||||
addr_t smc_arg_5() { return _state.r5; }
|
||||
addr_t smc_arg_6() { return _state.r6; }
|
||||
addr_t smc_arg_7() { return _state.r7; }
|
||||
addr_t smc_arg_8() { return _state.r8; }
|
||||
addr_t smc_arg_9() { return _state.r9; }
|
||||
|
||||
const char * const modes[] =
|
||||
{ "und", "svc", "abt", "irq", "fiq" };
|
||||
const char * const exc[] =
|
||||
{ "invalid", "reset", "undefined", "smc", "pf_abort",
|
||||
"data_abort", "irq", "fiq" };
|
||||
|
||||
printf("Cpu state:\n");
|
||||
printf(" Register Virt Phys\n");
|
||||
printf("---------------------------------\n");
|
||||
printf(" r0 = %08lx [%08lx]\n",
|
||||
_state->r0, va_to_pa(_state->r0));
|
||||
printf(" r1 = %08lx [%08lx]\n",
|
||||
_state->r1, va_to_pa(_state->r1));
|
||||
printf(" r2 = %08lx [%08lx]\n",
|
||||
_state->r2, va_to_pa(_state->r2));
|
||||
printf(" r3 = %08lx [%08lx]\n",
|
||||
_state->r3, va_to_pa(_state->r3));
|
||||
printf(" r4 = %08lx [%08lx]\n",
|
||||
_state->r4, va_to_pa(_state->r4));
|
||||
printf(" r5 = %08lx [%08lx]\n",
|
||||
_state->r5, va_to_pa(_state->r5));
|
||||
printf(" r6 = %08lx [%08lx]\n",
|
||||
_state->r6, va_to_pa(_state->r6));
|
||||
printf(" r7 = %08lx [%08lx]\n",
|
||||
_state->r7, va_to_pa(_state->r7));
|
||||
printf(" r8 = %08lx [%08lx]\n",
|
||||
_state->r8, va_to_pa(_state->r8));
|
||||
printf(" r9 = %08lx [%08lx]\n",
|
||||
_state->r9, va_to_pa(_state->r9));
|
||||
printf(" r10 = %08lx [%08lx]\n",
|
||||
_state->r10, va_to_pa(_state->r10));
|
||||
printf(" r11 = %08lx [%08lx]\n",
|
||||
_state->r11, va_to_pa(_state->r11));
|
||||
printf(" r12 = %08lx [%08lx]\n",
|
||||
_state->r12, va_to_pa(_state->r12));
|
||||
printf(" sp = %08lx [%08lx]\n",
|
||||
_state->sp, va_to_pa(_state->sp));
|
||||
printf(" lr = %08lx [%08lx]\n",
|
||||
_state->lr, va_to_pa(_state->lr));
|
||||
printf(" ip = %08lx [%08lx]\n",
|
||||
_state->ip, va_to_pa(_state->ip));
|
||||
printf(" cpsr = %08lx\n", _state->cpsr);
|
||||
for (unsigned i = 0;
|
||||
i < Genode::Vm_state::Mode_state::MAX; i++) {
|
||||
printf(" sp_%s = %08lx [%08lx]\n", modes[i],
|
||||
_state->mode[i].sp, va_to_pa(_state->mode[i].sp));
|
||||
printf(" lr_%s = %08lx [%08lx]\n", modes[i],
|
||||
_state->mode[i].lr, va_to_pa(_state->mode[i].lr));
|
||||
printf(" spsr_%s = %08lx [%08lx]\n", modes[i],
|
||||
_state->mode[i].spsr, va_to_pa(_state->mode[i].spsr));
|
||||
}
|
||||
printf(" ttbr0 = %08lx\n", _state->ttbr[0]);
|
||||
printf(" ttbr1 = %08lx\n", _state->ttbr[1]);
|
||||
printf(" ttbrc = %08lx\n", _state->ttbrc);
|
||||
printf(" dfar = %08lx [%08lx]\n",
|
||||
_state->dfar, va_to_pa(_state->dfar));
|
||||
printf(" exception = %s\n", exc[_state->cpu_exception]);
|
||||
}
|
||||
|
||||
Genode::addr_t va_to_pa(Genode::addr_t va)
|
||||
{
|
||||
try {
|
||||
Mmu mmu(_state, &_ram);
|
||||
return mmu.phys_addr(va);
|
||||
} catch(Ram::Invalid_addr) {}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Genode::Vm_state *state() const { return _state; }
|
||||
Ram *ram() { return &_ram; }
|
||||
|
||||
/*
|
||||
* Read accessors for argument values of a Secure Monitor Call
|
||||
*/
|
||||
|
||||
Genode::addr_t smc_arg_0() { return _state->r0; }
|
||||
Genode::addr_t smc_arg_1() { return _state->r1; }
|
||||
Genode::addr_t smc_arg_2() { return _state->r2; }
|
||||
Genode::addr_t smc_arg_3() { return _state->r3; }
|
||||
Genode::addr_t smc_arg_4() { return _state->r4; }
|
||||
Genode::addr_t smc_arg_5() { return _state->r5; }
|
||||
Genode::addr_t smc_arg_6() { return _state->r6; }
|
||||
Genode::addr_t smc_arg_7() { return _state->r7; }
|
||||
Genode::addr_t smc_arg_8() { return _state->r8; }
|
||||
Genode::addr_t smc_arg_9() { return _state->r9; }
|
||||
|
||||
/*
|
||||
* Write accessors for return values of a Secure Monitor Call
|
||||
*/
|
||||
|
||||
void smc_ret(Genode::addr_t const ret_0)
|
||||
{
|
||||
_state->r0 = ret_0;
|
||||
}
|
||||
|
||||
void smc_ret(Genode::addr_t const ret_0, Genode::addr_t const ret_1)
|
||||
{
|
||||
_state->r0 = ret_0;
|
||||
_state->r1 = ret_1;
|
||||
}
|
||||
void smc_ret(addr_t const ret_0) { _state.r0 = ret_0; }
|
||||
void smc_ret(addr_t const ret_0, addr_t const ret_1);
|
||||
};
|
||||
|
||||
#endif /* _SRC__SERVER__VMM__INCLUDE__VM_H_ */
|
||||
#endif /* _VM_BASE_H_ */
|
||||
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* \brief Paravirtualized access to serial devices for a Trustzone VM
|
||||
* \author Martin Stein
|
||||
* \date 2015-10-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <serial.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Vmm;
|
||||
|
||||
|
||||
void Serial::_push(char const c)
|
||||
{
|
||||
local_addr<char>()[_off] = c;
|
||||
_off += sizeof(char);
|
||||
}
|
||||
|
||||
|
||||
void Serial::_flush()
|
||||
{
|
||||
_push(0);
|
||||
log("[vm] ", local_addr<char const>());
|
||||
_off = 0;
|
||||
}
|
||||
|
||||
|
||||
void Serial::_send(Vm_base * const vm)
|
||||
{
|
||||
char const c = vm->smc_arg_2();
|
||||
if (c == '\n') { _flush(); }
|
||||
else { _push(c); }
|
||||
if (_off == WRAP) { _flush(); }
|
||||
}
|
||||
|
||||
|
||||
void Serial::handle(Vm_base * const vm)
|
||||
{
|
||||
enum { SEND = 0 };
|
||||
switch (vm->smc_arg_1()) {
|
||||
case SEND: _send(vm); break;
|
||||
default:
|
||||
Genode::error("Unknown function ", vm->smc_arg_1(), " requested on VMM serial");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Serial::Serial()
|
||||
:
|
||||
Attached_ram_dataspace(env()->ram_session(), BUF_SIZE), _off(0)
|
||||
{ }
|
47
repos/os/src/server/tz_vmm/serial_driver.cc
Normal file
47
repos/os/src/server/tz_vmm/serial_driver.cc
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* \brief Paravirtualized access to serial device for a Trustzone VM
|
||||
* \author Martin Stein
|
||||
* \date 2015-10-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* local includes */
|
||||
#include <serial_driver.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Serial_driver::_flush()
|
||||
{
|
||||
_push(0);
|
||||
log("[vm] ", _buf.local_addr<char const>());
|
||||
_off = 0;
|
||||
}
|
||||
|
||||
|
||||
void Serial_driver::_send(Vm_base &vm)
|
||||
{
|
||||
char const c = vm.smc_arg_2();
|
||||
if (c == '\n') {
|
||||
_flush();
|
||||
} else {
|
||||
_push(c); }
|
||||
|
||||
if (_off == BUF_SIZE - 1) {
|
||||
_flush(); }
|
||||
}
|
||||
|
||||
|
||||
void Serial_driver::handle_smc(Vm_base &vm)
|
||||
{
|
||||
enum { SEND = 0 };
|
||||
switch (vm.smc_arg_1()) {
|
||||
case SEND: _send(vm); break;
|
||||
default: error("unknown serial-driver function ", vm.smc_arg_1()); }
|
||||
}
|
@ -5,19 +5,19 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012 Genode Labs GmbH
|
||||
* Copyright (C) 2012-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _SERVER__TZ_VMM__SPEC__IMX53__M4IF_H_
|
||||
#define _SERVER__TZ_VMM__SPEC__IMX53__M4IF_H_
|
||||
#ifndef _M4IF_H_
|
||||
#define _M4IF_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
#include <os/attached_mmio.h>
|
||||
|
||||
class M4if : Genode::Mmio
|
||||
class M4if : Genode::Attached_mmio
|
||||
{
|
||||
private:
|
||||
|
||||
@ -58,7 +58,8 @@ class M4if : Genode::Mmio
|
||||
|
||||
public:
|
||||
|
||||
M4if(Genode::addr_t const base) : Genode::Mmio(base) {}
|
||||
M4if(Genode::Env &env, Genode::addr_t base, Genode::size_t size)
|
||||
: Genode::Attached_mmio(env, base, size) { }
|
||||
|
||||
void set_region0(Genode::addr_t addr, Genode::size_t size)
|
||||
{
|
||||
@ -87,4 +88,4 @@ class M4if : Genode::Mmio
|
||||
Genode::addr_t violation_addr() { return read<Wm_reg0_addr>(); }
|
||||
};
|
||||
|
||||
#endif /* _SERVER__TZ_VMM__SPEC__IMX53__M4IF_H_ */
|
||||
#endif /* _M4IF_H_ */
|
||||
|
@ -1,63 +1,63 @@
|
||||
/*
|
||||
* \brief Virtual Machine Monitor
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin Stein
|
||||
* \date 2012-06-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2012 Genode Labs GmbH
|
||||
* Copyright (C) 2008-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/env.h>
|
||||
#include <base/sleep.h>
|
||||
#include <base/thread.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/component.h>
|
||||
#include <drivers/board_base.h>
|
||||
#include <drivers/trustzone.h>
|
||||
#include <vm_state.h>
|
||||
|
||||
/* local includes */
|
||||
#include <vm.h>
|
||||
#include <m4if.h>
|
||||
#include <serial.h>
|
||||
#include <block.h>
|
||||
#include <serial_driver.h>
|
||||
#include <block_driver.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
enum {
|
||||
KERNEL_OFFSET = 0x8000,
|
||||
MACH_TYPE_TABLET = 3011,
|
||||
MACH_TYPE_QSB = 3273,
|
||||
BOARD_REV_TABLET = 0x53321,
|
||||
};
|
||||
|
||||
|
||||
static const char* cmdline_tablet = "console=ttymxc0,115200";
|
||||
|
||||
void on_vmm_entry();
|
||||
void on_vmm_exit();
|
||||
|
||||
namespace Vmm {
|
||||
class Vmm;
|
||||
}
|
||||
|
||||
|
||||
class Vmm::Vmm : public Thread_deprecated<8192>
|
||||
class Main
|
||||
{
|
||||
private:
|
||||
|
||||
Signal_receiver _sig_rcv;
|
||||
Signal_context _vm_context;
|
||||
Vm *_vm;
|
||||
Io_mem_connection _m4if_io_mem;
|
||||
M4if _m4if;
|
||||
Serial _serial;
|
||||
Block _block;
|
||||
enum {
|
||||
KERNEL_OFFSET = 0x8000,
|
||||
MACHINE_TABLET = 3011,
|
||||
MACHINE_QSB = 3273,
|
||||
BOARD_TABLET = 0x53321,
|
||||
BOARD_QSB = 0,
|
||||
};
|
||||
|
||||
void _handle_hypervisor_call()
|
||||
Env &_env;
|
||||
Vm::Kernel_name const _kernel_name { "linux" };
|
||||
Vm::Command_line const _cmd_line { "console=ttymxc0,115200" };
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
Signal_handler<Main> _exception_handler { _env.ep(), *this,
|
||||
&Main::_handle_exception };
|
||||
|
||||
Heap _heap { &_env.ram(), &_env.rm() };
|
||||
Vm _vm { _env, _kernel_name, _cmd_line,
|
||||
Trustzone::NONSECURE_RAM_BASE,
|
||||
Trustzone::NONSECURE_RAM_SIZE,
|
||||
KERNEL_OFFSET, Machine_type(MACHINE_QSB),
|
||||
Board_revision(BOARD_QSB) };
|
||||
M4if _m4if { _env, Board_base::M4IF_BASE, Board_base::M4IF_SIZE };
|
||||
Serial_driver _serial { _env.ram() };
|
||||
Block_driver _block { _env.ep(), _config.xml(), _heap, _vm };
|
||||
|
||||
void _handle_smc()
|
||||
{
|
||||
enum {
|
||||
FRAMEBUFFER = 0,
|
||||
@ -65,90 +65,52 @@ class Vmm::Vmm : public Thread_deprecated<8192>
|
||||
SERIAL = 2,
|
||||
BLOCK = 3,
|
||||
};
|
||||
switch (_vm->smc_arg_0()) {
|
||||
case FRAMEBUFFER: break;
|
||||
case INPUT: break;
|
||||
case SERIAL: _serial.handle(_vm); break;
|
||||
case BLOCK: _block.handle(_vm); break;
|
||||
switch (_vm.smc_arg_0()) {
|
||||
case FRAMEBUFFER: break;
|
||||
case INPUT: break;
|
||||
case SERIAL: _serial.handle_smc(_vm); break;
|
||||
case BLOCK: _block.handle_smc(_vm); break;
|
||||
default:
|
||||
Genode::error("unknown hypervisor call!");
|
||||
_vm->dump();
|
||||
error("unknown hypervisor call ", _vm.smc_arg_0());
|
||||
throw Vm::Exception_handling_failed();
|
||||
};
|
||||
}
|
||||
|
||||
bool _handle_data_abort()
|
||||
void _handle_data_abort()
|
||||
{
|
||||
_vm->dump();
|
||||
return false;
|
||||
error("failed to handle data abort");
|
||||
throw Vm::Exception_handling_failed();
|
||||
}
|
||||
|
||||
bool _handle_vm()
|
||||
void _handle_exception()
|
||||
{
|
||||
/* check exception reason */
|
||||
switch (_vm->state()->cpu_exception) {
|
||||
case Cpu_state::DATA_ABORT:
|
||||
if (!_handle_data_abort()) {
|
||||
Genode::error("could not handle data-abort will exit!");
|
||||
return false;
|
||||
_vm.on_vmm_entry();
|
||||
try {
|
||||
switch (_vm.state().cpu_exception) {
|
||||
case Cpu_state::DATA_ABORT: _handle_data_abort(); break;
|
||||
case Cpu_state::SUPERVISOR_CALL: _handle_smc(); break;
|
||||
default:
|
||||
error("unknown exception ", _vm.state().cpu_exception);
|
||||
throw Vm::Exception_handling_failed();
|
||||
}
|
||||
break;
|
||||
case Cpu_state::SUPERVISOR_CALL:
|
||||
_handle_hypervisor_call();
|
||||
break;
|
||||
default:
|
||||
Genode::error("curious exception occured");
|
||||
_vm->dump();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void entry()
|
||||
{
|
||||
_vm->sig_handler(_sig_rcv.manage(&_vm_context));
|
||||
_vm->start();
|
||||
_vm->run();
|
||||
|
||||
while (true) {
|
||||
Signal s = _sig_rcv.wait_for_signal();
|
||||
on_vmm_entry();
|
||||
if (s.context() == &_vm_context) {
|
||||
if (_handle_vm())
|
||||
_vm->run();
|
||||
} else {
|
||||
Genode::warning("invalid context");
|
||||
continue;
|
||||
}
|
||||
on_vmm_exit();
|
||||
_vm.run();
|
||||
}
|
||||
catch (Vm::Exception_handling_failed) { _vm.dump(); }
|
||||
_vm.on_vmm_exit();
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
Vmm(Vm *vm)
|
||||
: Thread_deprecated<8192>("vmm"),
|
||||
_vm(vm),
|
||||
_m4if_io_mem(Board_base::M4IF_BASE, Board_base::M4IF_SIZE),
|
||||
_m4if((addr_t)env()->rm_session()->attach(_m4if_io_mem.dataspace()))
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
log("Start virtual machine ...");
|
||||
_m4if.set_region0(Trustzone::SECURE_RAM_BASE,
|
||||
Trustzone::SECURE_RAM_SIZE);
|
||||
_vm.exception_handler(_exception_handler);
|
||||
_vm.start();
|
||||
_vm.run();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
static Vm vm("linux", cmdline_tablet,
|
||||
Trustzone::NONSECURE_RAM_BASE, Trustzone::NONSECURE_RAM_SIZE,
|
||||
KERNEL_OFFSET, MACH_TYPE_QSB);
|
||||
static Vmm::Vmm vmm(&vm);
|
||||
|
||||
Genode::log("Start virtual machine ...");
|
||||
vmm.start();
|
||||
|
||||
sleep_forever();
|
||||
return 0;
|
||||
}
|
||||
void Component::construct(Env &env) { static Main main(env); }
|
||||
|
@ -1,19 +1,40 @@
|
||||
/*
|
||||
* \brief Virtual machine implementation
|
||||
* \brief Virtual Machine implementation
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin Stein
|
||||
* \date 2015-06-10
|
||||
* \date 2015-02-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
/* local includes */
|
||||
#include <vm.h>
|
||||
|
||||
void on_vmm_entry() { }
|
||||
using namespace Genode;
|
||||
|
||||
void on_vmm_exit() { }
|
||||
|
||||
void Vm::_load_kernel_surroundings()
|
||||
{
|
||||
/* load initrd */
|
||||
enum { INITRD_OFFSET = 0x1000000 };
|
||||
Attached_rom_dataspace initrd(_env, "initrd.gz");
|
||||
memcpy((void*)(_ram.local() + INITRD_OFFSET),
|
||||
initrd.local_addr<void>(), initrd.size());
|
||||
|
||||
/* load ATAGs */
|
||||
Atag tag((void*)(_ram.local() + ATAG_OFFSET));
|
||||
tag.setup_mem_tag(_ram.base(), _ram.size());
|
||||
tag.setup_cmdline_tag(_cmdline.string());
|
||||
tag.setup_initrd2_tag(_ram.base() + INITRD_OFFSET, initrd.size());
|
||||
if (_board.value) {
|
||||
tag.setup_rev_tag(_board.value); }
|
||||
tag.setup_end_tag();
|
||||
}
|
||||
|
@ -1,78 +1,56 @@
|
||||
/*
|
||||
* \brief Virtual Machine implementation using device trees
|
||||
* \brief Virtual Machine implementation
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin Stein
|
||||
* \date 2015-02-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _SERVER__TZ_VMM__SPEC__IMX53_QSB__VM_H_
|
||||
#define _SERVER__TZ_VMM__SPEC__IMX53_QSB__VM_H_
|
||||
#ifndef _VM_H_
|
||||
#define _VM_H_
|
||||
|
||||
/* local includes */
|
||||
#include <vm_base.h>
|
||||
#include <atag.h>
|
||||
|
||||
class Vm : public Vm_base
|
||||
namespace Genode { class Vm; }
|
||||
|
||||
|
||||
class Genode::Vm : public Vm_base
|
||||
{
|
||||
private:
|
||||
|
||||
enum {
|
||||
ATAG_OFFSET = 0x100,
|
||||
INITRD_OFFSET = 0x1000000,
|
||||
};
|
||||
enum { ATAG_OFFSET = 0x100 };
|
||||
|
||||
Genode::Rom_connection _initrd_rom;
|
||||
Genode::Dataspace_client _initrd_cap;
|
||||
|
||||
void _load_initrd()
|
||||
{
|
||||
using namespace Genode;
|
||||
addr_t addr = env()->rm_session()->attach(_initrd_cap);
|
||||
memcpy((void*)(_ram.local() + INITRD_OFFSET),
|
||||
(void*)addr, _initrd_cap.size());
|
||||
env()->rm_session()->detach((void*)addr);
|
||||
}
|
||||
/*************
|
||||
** Vm_base **
|
||||
*************/
|
||||
|
||||
void _load_atag()
|
||||
{
|
||||
Atag tag((void*)(_ram.local() + ATAG_OFFSET));
|
||||
tag.setup_mem_tag(_ram.base(), _ram.size());
|
||||
tag.setup_cmdline_tag(_cmdline);
|
||||
tag.setup_initrd2_tag(_ram.base() + INITRD_OFFSET, _initrd_cap.size());
|
||||
if (_board_rev)
|
||||
tag.setup_rev_tag(_board_rev);
|
||||
tag.setup_end_tag();
|
||||
}
|
||||
void _load_kernel_surroundings();
|
||||
|
||||
/*
|
||||
* Vm_base interface
|
||||
*/
|
||||
|
||||
void _load_kernel_surroundings()
|
||||
{
|
||||
_load_initrd();
|
||||
_load_atag();
|
||||
}
|
||||
|
||||
Genode::addr_t _board_info_offset() const { return ATAG_OFFSET; }
|
||||
addr_t _board_info_offset() const { return ATAG_OFFSET; }
|
||||
|
||||
public:
|
||||
|
||||
Vm(const char *kernel, const char *cmdline,
|
||||
Genode::addr_t ram_base, Genode::size_t ram_size,
|
||||
Genode::addr_t kernel_offset, unsigned long mach_type,
|
||||
unsigned long board_rev = 0)
|
||||
:
|
||||
Vm_base(kernel, cmdline, ram_base, ram_size,
|
||||
kernel_offset, mach_type, board_rev),
|
||||
_initrd_rom("initrd.gz"),
|
||||
_initrd_cap(_initrd_rom.dataspace())
|
||||
{ }
|
||||
Vm(Env &env,
|
||||
Kernel_name const &kernel,
|
||||
Command_line const &cmdl,
|
||||
addr_t ram,
|
||||
size_t ram_sz,
|
||||
off_t kernel_off,
|
||||
Machine_type mach,
|
||||
Board_revision board)
|
||||
: Vm_base(env, kernel, cmdl, ram, ram_sz, kernel_off, mach, board) { }
|
||||
|
||||
void on_vmm_exit() { }
|
||||
void on_vmm_entry() { };
|
||||
};
|
||||
|
||||
#endif /* _SERVER__TZ_VMM__SPEC__IMX53_QSB__VM_H_ */
|
||||
#endif /* _VM_H_ */
|
||||
|
@ -1,34 +1,36 @@
|
||||
/*
|
||||
* \brief Virtual machine implementation
|
||||
* \brief Virtual Machine implementation
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin Stein
|
||||
* \date 2015-06-10
|
||||
* \date 2015-02-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
/* local includes */
|
||||
#include <vm.h>
|
||||
#include <gpio_session/connection.h>
|
||||
|
||||
Gpio::Connection * led()
|
||||
{
|
||||
static Gpio::Connection led(123);
|
||||
return &led;
|
||||
}
|
||||
using namespace Genode;
|
||||
|
||||
void on_vmm_entry()
|
||||
|
||||
void Vm::on_vmm_entry()
|
||||
{
|
||||
led()->direction(Gpio::Session::OUT);
|
||||
led()->write(false);
|
||||
_led.direction(Gpio::Session::OUT);
|
||||
_led.write(false);
|
||||
}
|
||||
|
||||
|
||||
void on_vmm_exit()
|
||||
void Vm::_load_kernel_surroundings()
|
||||
{
|
||||
led()->write(true);
|
||||
Attached_rom_dataspace dtb(_env, "dtb");
|
||||
memcpy((void*)(_ram.local() + DTB_OFFSET), dtb.local_addr<void>(),
|
||||
dtb.size());
|
||||
}
|
||||
|
@ -1,57 +1,60 @@
|
||||
/*
|
||||
* \brief Virtual Machine implementation using device trees
|
||||
* \brief Virtual Machine implementation
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin Stein
|
||||
* \date 2015-02-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _SERVER__TZ_VMM__SPEC__USB_ARMORY__VM_H_
|
||||
#define _SERVER__TZ_VMM__SPEC__USB_ARMORY__VM_H_
|
||||
#ifndef _VM_H_
|
||||
#define _VM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <gpio_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
#include <vm_base.h>
|
||||
|
||||
class Vm : public Genode::Rom_connection,
|
||||
public Genode::Dataspace_client,
|
||||
public Vm_base
|
||||
namespace Genode { class Vm; }
|
||||
|
||||
|
||||
class Genode::Vm : public Vm_base
|
||||
{
|
||||
private:
|
||||
|
||||
enum { DTB_OFFSET = 0x1000000 };
|
||||
|
||||
void _load_dtb()
|
||||
{
|
||||
using namespace Genode;
|
||||
addr_t addr = env()->rm_session()->attach(*this);
|
||||
memcpy((void*)(_ram.local() + DTB_OFFSET), (void*)addr, size());
|
||||
env()->rm_session()->detach((void*)addr);
|
||||
}
|
||||
Gpio::Connection _led { _env, 123 };
|
||||
|
||||
/*
|
||||
* Vm_base interface
|
||||
*/
|
||||
|
||||
void _load_kernel_surroundings() { _load_dtb(); }
|
||||
/*************
|
||||
** Vm_base **
|
||||
*************/
|
||||
|
||||
Genode::addr_t _board_info_offset() const { return DTB_OFFSET; }
|
||||
void _load_kernel_surroundings();
|
||||
|
||||
addr_t _board_info_offset() const { return DTB_OFFSET; }
|
||||
|
||||
public:
|
||||
|
||||
Vm(const char *kernel, const char *cmdline,
|
||||
Genode::addr_t ram_base, Genode::size_t ram_size,
|
||||
Genode::addr_t kernel_offset, unsigned long mach_type,
|
||||
unsigned long board_rev = 0)
|
||||
: Genode::Rom_connection("dtb"),
|
||||
Genode::Dataspace_client(dataspace()),
|
||||
Vm_base(kernel, cmdline, ram_base, ram_size,
|
||||
kernel_offset, mach_type, board_rev)
|
||||
{ }
|
||||
Vm(Env &env,
|
||||
Kernel_name const &kernel,
|
||||
Command_line const &cmdl,
|
||||
addr_t ram,
|
||||
size_t ram_sz,
|
||||
off_t kernel_off,
|
||||
Machine_type mach,
|
||||
Board_revision board)
|
||||
: Vm_base(env, kernel, cmdl, ram, ram_sz, kernel_off, mach, board) { }
|
||||
|
||||
void on_vmm_exit() { _led.write(true); }
|
||||
void on_vmm_entry();
|
||||
};
|
||||
|
||||
#endif /* _SERVER__TZ_VMM__SPEC__USB_ARMORY__VM_H_ */
|
||||
#endif /* _VM_H_ */
|
||||
|
131
repos/os/src/server/tz_vmm/vm_base.cc
Normal file
131
repos/os/src/server/tz_vmm/vm_base.cc
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* \brief Virtual Machine Monitor VM definition
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin Stein
|
||||
* \date 2012-06-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
|
||||
/* local includes */
|
||||
#include <vm_base.h>
|
||||
#include <mmu.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Vm_base::_load_kernel()
|
||||
{
|
||||
Attached_rom_dataspace kernel(_env, _kernel.string());
|
||||
memcpy((void*)(_ram.local() + _kernel_off),
|
||||
kernel.local_addr<void>(), kernel.size());
|
||||
_state.ip = _ram.base() + _kernel_off;
|
||||
}
|
||||
|
||||
|
||||
Vm_base::Vm_base(Env &env,
|
||||
Kernel_name const &kernel,
|
||||
Command_line const &cmdline,
|
||||
addr_t ram_base,
|
||||
size_t ram_size,
|
||||
off_t kernel_off,
|
||||
Machine_type machine,
|
||||
Board_revision board)
|
||||
:
|
||||
_env(env), _kernel(kernel), _cmdline(cmdline), _kernel_off(kernel_off),
|
||||
_machine(machine), _board(board), _ram(env, ram_base, ram_size)
|
||||
{
|
||||
_state.irq_injection = 0;
|
||||
}
|
||||
|
||||
void Vm_base::start()
|
||||
{
|
||||
memset((void*)&_state, 0, sizeof(Vm_state));
|
||||
_load_kernel();
|
||||
_load_kernel_surroundings();
|
||||
_state.cpsr = 0x93; /* SVC mode and IRQs disabled */
|
||||
_state.r0 = 0;
|
||||
_state.r1 = _machine.value;
|
||||
_state.r2 = _ram.base() + _board_info_offset();
|
||||
}
|
||||
|
||||
|
||||
void Vm_base::inject_irq(unsigned irq)
|
||||
{
|
||||
if (_state.irq_injection) { throw Inject_irq_failed(); }
|
||||
_state.irq_injection = irq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Vm_base::dump()
|
||||
{
|
||||
char const *mod[] = { "und", "svc", "abt", "irq", "fiq" };
|
||||
char const *exc[] = { "invalid", "reset", "undefined", "smc",
|
||||
"pf_abort", "data_abort", "irq", "fiq" };
|
||||
|
||||
auto log_adr_reg = [&] (char const *reg, addr_t val) {
|
||||
log(" ", reg, " = ", Hex(val, Hex::PREFIX, Hex::PAD), " ",
|
||||
Hex(va_to_pa(val), Hex::PREFIX, Hex::PAD)); };
|
||||
|
||||
auto log_mod_reg = [&] (char const *reg, addr_t val, char const *mod) {
|
||||
log(" ", reg, "_", mod, " = ", Hex(val, Hex::PREFIX, Hex::PAD), " ",
|
||||
Hex(va_to_pa(val), Hex::PREFIX, Hex::PAD)); };
|
||||
|
||||
log("Cpu state:");
|
||||
log(" Register Virt Phys");
|
||||
log("------------------------------------");
|
||||
log_adr_reg("r0 ", _state.r0);
|
||||
log_adr_reg("r1 ", _state.r1);
|
||||
log_adr_reg("r2 ", _state.r2);
|
||||
log_adr_reg("r3 ", _state.r3);
|
||||
log_adr_reg("r4 ", _state.r4);
|
||||
log_adr_reg("r5 ", _state.r5);
|
||||
log_adr_reg("r6 ", _state.r6);
|
||||
log_adr_reg("r7 ", _state.r7);
|
||||
log_adr_reg("r8 ", _state.r8);
|
||||
log_adr_reg("r9 ", _state.r9);
|
||||
log_adr_reg("r10 ", _state.r10);
|
||||
log_adr_reg("r11 ", _state.r11);
|
||||
log_adr_reg("r12 ", _state.r12);
|
||||
log_adr_reg("sp ", _state.sp);
|
||||
log_adr_reg("lr ", _state.lr);
|
||||
log_adr_reg("ip ", _state.ip);
|
||||
log_adr_reg("cpsr ", _state.cpsr);
|
||||
for (unsigned i = 0; i < Vm_state::Mode_state::MAX; i++) {
|
||||
log_mod_reg("sp ", _state.mode[i].sp, mod[i]);
|
||||
log_mod_reg("lr ", _state.mode[i].lr, mod[i]);
|
||||
log_mod_reg("spsr ", _state.mode[i].spsr, mod[i]);
|
||||
}
|
||||
log(" ttbr0 = ", Hex(_state.ttbr[0], Hex::PREFIX, Hex::PAD));
|
||||
log(" ttbr1 = ", Hex(_state.ttbr[1], Hex::PREFIX, Hex::PAD));
|
||||
log(" ttbrc = ", Hex(_state.ttbrc , Hex::PREFIX, Hex::PAD));
|
||||
log_adr_reg("dfar ", _state.dfar);
|
||||
log(" exception = ", exc[_state.cpu_exception]);
|
||||
}
|
||||
|
||||
|
||||
void Vm_base::smc_ret(addr_t const ret_0, addr_t const ret_1)
|
||||
{
|
||||
_state.r0 = ret_0;
|
||||
_state.r1 = ret_1;
|
||||
}
|
||||
|
||||
|
||||
addr_t Vm_base::va_to_pa(addr_t va)
|
||||
{
|
||||
try {
|
||||
Mmu mmu(_state, _ram);
|
||||
return mmu.phys_addr(va);
|
||||
}
|
||||
catch(Ram::Invalid_addr) { }
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user