TZ VMM & USB Armory: Demo

* Provide paravirtualized block API for accessing the second partition
  of the block device that is provided by the ESDHC driver.

* Provide paravirtualized serial API for sending log-output over Genodes
  serial port.

* Use the latest Linux suggested in the USB Armory Wiki [1] when on USB Armory
  while still using the older vendor Linux when on i.MX53 QSB. I.e.,
  provide a device tree through RAM and a rootfs through the paravirtualized
  block device when on USB Armory while providing ATAGs and Initrd when on
  i.MX53 QSB.

* Switch on the LED on the USB Armory when the VMM catches a VM-exception
  and switch it off again when as soon as the exception is handled. This
  merely show-cases the ability to instrument the LED for such purposes. In an
  ideal world, the LED is switched on as long as we're on the "Secure Side"
  and switched off as long as we're not.

* For further information see repos/os/run/tz_vmm.run

[1] https://github.com/inversepath/usbarmory/wiki/Preparing-a-bootable-microSD-image

Fixes #1497
This commit is contained in:
Martin Stein 2015-11-02 14:34:49 +01:00 committed by Christian Helmuth
parent bed04f1f16
commit 59d2a83f30
12 changed files with 1207 additions and 112 deletions

View File

@ -5,20 +5,123 @@
# \date 2012-06-25
#
#
# On USB Armory
# #############
#
# Create bootable microSD card
# ============================
#
# :User settings:
#
# ! export TARGET_DEV=/dev/sdX # empty boot medium
# ! export TARGET_MNT=/mnt # where to mount the boot medium
# ! export GENODE_DIR=/path/genode # root of the Genode sources
# ! export BUILD_DIR=/path/build/hw_usb_armory # Genode build directory
#
# :Format microSD card:
#
# ! sudo parted $TARGET_DEV --script mklabel msdos
# ! sudo parted $TARGET_DEV --script mkpart primary ext4 10M 110M
# ! sudo parted $TARGET_DEV --script mkpart primary ext4 110M 2105M
# ! sudo mkfs.ext4 ${TARGET_DEV}1
# ! sudo mkfs.ext4 ${TARGET_DEV}2
#
# :Install bootloader:
#
# ! cd $GENODE_DIR
# ! ./tool/create_uboot hw_usb_armory
# ! sudo dd if=contrib/uboot/build/hw_usb_armory/mmc_img of=$TARGET_DEV bs=1K seek=1 conv=fsync
#
# :Install Genode:
#
# ! cd $BUILD_DIR
# ! echo "RUN_OPT += --include image/uboot" >> etc/build.conf
# ! make run/tz_vmm
# ! sudo mount ${TARGET_DEV}1 $TARGET_MNT
# ! sudo cp var/run/tz_vmm/uImage $TARGET_MNT
# ! sudo umount $TARGET_MNT
#
# :Install Linux rootfs:
#
# ! sudo partclone.extfs -r -d -s var/run/tz_vmm/rootfs -o ${TARGET_DEV}2
#
# :Insert microSD card into USB Armory:
#
# [https://github.com/inversepath/usbarmory/wiki/microSD-compatibility]
#
# :Connect USB Armory to host machine via USB-to-TTL serial cable:
#
# [https://github.com/inversepath/usbarmory/wiki/GPIOs#breakout-header]
#
# :Connect to USB Armory COM port:
#
# ! sudo picocom -b 115200 -r -l /dev/ttyUSB0
#
# :Insert USB Armory into host USB slot and interrupt auto boot on COM port:
#
# :Send bootloader commands on COM port:
#
# ! ext2load mmc 0:1 0x74000000 /uImage
# ! bootm 0x74000000
#
# :Communicate with Linux via CDC Ethernet:
#
# [https://github.com/inversepath/usbarmory/wiki/Host-communication#cdc-ethernet]
#
# Further information
# ===================
#
# :Tutorial on how to reproduce the Linux images:
#
# [https://github.com/m-stein/genode_binaries/blob/master/tz_vmm/usb_armory/README]
#
# :Wiki about the USB Armory and the Linux:
#
# [https://github.com/inversepath/usbarmory/wiki]
#
assert_spec hw
assert_spec imx53
assert_spec trustzone
build "core init server/tz_vmm"
# determine platform specific configuration
set trusted_led [have_spec hw_usb_armory]
set inversepath_linux [have_spec hw_usb_armory]
set freescale_linux [have_spec hw_imx53_qsb_tz]
set initrd_rootfs $freescale_linux
set mmc_rootfs $inversepath_linux
set dtb $inversepath_linux
set targets { core init server/tz_vmm }
if { $mmc_rootfs } {
# choose interrupt for paravirtualized block
if { [have_spec imx53] } { set tz_vmm_block_irq 92 }
# add targets that enable MMC access via paravirtualized block
lappend targets drivers/timer
lappend targets drivers/platform
lappend targets drivers/sd_card
lappend targets server/part_blk
}
lappend_if $trusted_led targets drivers/gpio
build $targets
create_boot_directory
install_config {
# compose config
set config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
@ -29,77 +132,150 @@ install_config {
</parent-provides>
<default-route>
<any-service><any-child/><parent/></any-service>
</default-route>
<start name="tz_vmm">
<resource name="RAM" quantum="4M"/>
</default-route> }
append_if $trusted_led config {
<start name="gpio_drv">
<resource name="RAM" quantum="3M"/>
<provides><service name="Gpio"/></provides>
<config/>
</start>}
append_if $mmc_rootfs config {
<start name="timer">
<resource name="RAM" quantum="3M"/>
<provides><service name="Timer"/></provides>
</start>
</config>
<start name="platform_drv">
<resource name="RAM" quantum="3M"/>
<provides>
<service name="Regulator"/>
<service name="Platform"/>
</provides>
</start>
<start name="sd_card_drv">
<resource name="RAM" quantum="3M"/>
<provides><service name="Block"/></provides>
</start>
<start name="part_blk">
<resource name="RAM" quantum="10M" />
<provides><service name="Block" /></provides>
<route>
<service name="Block"><child name="sd_card_drv" /></service>
<any-service><parent/><any-child/></any-service>
</route>
<config>
<policy label="tz_vmm -> sda1" partition="2"/>
</config>
</start> }
append config {
<start name="tz_vmm">
<resource name="RAM" quantum="10M"/> }
if { $mmc_rootfs } {
append config "
<config>
<block label=\"sda1\" irq=\"$tz_vmm_block_irq\"/>
</config>
<route>
<service name=\"Block\"><child name=\"part_blk\" /></service>
<any-service><any-child/><parent/></any-service>
</route> "
}
set init_uri "http://genode.org/files/images/imx53_qsb/initrd.gz"
append config {
</start>
</config> }
if {[have_spec hw_usb_armory]} {
#
# This Linux and DTB were build with this toolchain:
#
# ! http://releases.linaro.org/14.09/components/toolchain/binaries/gcc-linaro-arm-none-eabi-4.9-2014.09_linux.tar.xz
#
# From this sources:
#
# ! https://github.com/m-stein/linux/tree/usb_armory_genode_tz_vmm
#
# With the following commands:
#
# ! make ARCH=arm CROSS_COMPILE=$GCC/bin/arm-none-eabi- imx_v53_usb_armory_defconfig
# ! make ARCH=arm CROSS_COMPILE=$GCC/bin/arm-none-eabi- zImage LOADADDR=0x80008000
# ! make ARCH=arm CROSS_COMPILE=$GCC/bin/arm-none-eabi- dtbs
#
# Then 'arch/arm/boot/zImage' can be used as 'linux' and
# 'arch/arm/boot/dts/imx53-qsb.dtb' can be used as 'dtb'.
#
set linux_uri "https://github.com/m-stein/genode_binaries/raw/master/tz_vmm/imx53/linux"
set dtb_uri "https://github.com/m-stein/genode_binaries/raw/master/tz_vmm/imx53/dtb"
install_config $config
# download and add linux
cd bin
if { $inversepath_linux } {
set linux_uri "https://github.com/m-stein/genode_binaries/raw/master/tz_vmm/usb_armory/linux"
} elseif { $freescale_linux } {
if {![file exists bin/dtb]} {
puts "Download device tree ..."
exec >& /dev/null wget -c -O bin/dtb $dtb_uri
}
exec >& /dev/null wget -O bin/dtb.md5 $dtb_uri.md5
} elseif {[have_spec hw_imx53_qsb_tz]} {
set linux_uri "http://genode.org/files/images/imx53_qsb/linux_trustzone.bin"
}
if {![file exists bin/initrd.gz]} {
puts "Download initramfs ..."
exec >& /dev/null wget -c -O bin/initrd.gz $init_uri
}
if {![file exists bin/linux]} {
if {![file exists linux]} {
puts "Download linux binary ..."
exec >& /dev/null wget -c -O bin/linux $linux_uri
exec >& /dev/null wget -c -O linux $linux_uri
}
exec >& /dev/null wget -O bin/initrd.gz.md5 $init_uri.md5
exec >& /dev/null wget -O bin/linux.md5 $linux_uri.md5
cd bin
if {[have_spec hw_usb_armory]} { exec md5sum -c dtb.md5 }
exec md5sum -c initrd.gz.md5
exec >& /dev/null wget -O linux.md5 $linux_uri.md5
exec md5sum -c linux.md5
set boot_modules { core init tz_vmm linux }
if { $dtb } {
# download and add DTB
set dtb_uri "https://github.com/m-stein/genode_binaries/raw/master/tz_vmm/usb_armory/dtb"
if {![file exists dtb]} {
puts "Download device tree ..."
exec >& /dev/null wget -c -O dtb $dtb_uri
}
exec >& /dev/null wget -O dtb.md5 $dtb_uri.md5
exec md5sum -c dtb.md5
lappend boot_modules dtb
}
if { $mmc_rootfs } {
# add components that enable MMC access via parvirtualized block
lappend boot_modules timer
lappend boot_modules platform_drv
lappend boot_modules sd_card_drv
lappend boot_modules part_blk
# download and add rootfs
set rootfs_uri "https://github.com/m-stein/genode_binaries/raw/master/tz_vmm/usb_armory/rootfs"
if {![file exists rootfs]} {
puts "Download rootfs ..."
exec >& /dev/null wget -c -O rootfs.gz $rootfs_uri.gz
puts "Extract rootfs ..."
exec >& /dev/null gunzip rootfs.gz
}
exec >& /dev/null wget -O rootfs.md5 $rootfs_uri.md5
exec md5sum -c rootfs.md5
exec ln -sf ../../../bin/rootfs $env(PWD)/[run_dir]/rootfs
} elseif { $initrd_rootfs } {
# download and add initrd
set initrd_uri "http://genode.org/files/images/imx53_qsb/initrd.gz"
if {![file exists initrd.gz]} {
puts "Download initramfs ..."
exec >& /dev/null wget -c -O initrd.gz $initrd_uri
}
exec >& /dev/null wget -O initrd.gz.md5 $initrd_uri.md5
exec md5sum -c initrd.gz.md5
lappend boot_modules initrd.gz
}
cd ..
set boot_modules { core init tz_vmm linux initrd.gz }
lappend_if [have_spec hw_usb_armory] boot_modules dtb
lappend_if $trusted_led boot_modules gpio_drv
build_boot_image [join $boot_modules " "]
#
# Execute test case
#
run_genode_until {.*\/ #.*} 220
set serial_id [output_spawn_id]
if { $inversepath_linux } {
# Wait for network to settle down
send -i $serial_id "sleep 5\n"
send -i $serial_id "wget http://genode.org/\n"
run_genode_until "Connecting to genode.org" 10 $serial_id
run_genode_until {.*\/ #.*} 30 $serial_id
# execute and wait for console
run_genode_until "Debian GNU/Linux 7 usbarmory console\n" 220
exec rm bin/initrd.gz bin/initrd.gz.md5 bin/linux bin/linux.md5
if {[have_spec hw_usb_armory]} { exec rm bin/dtb bin/dtb.md5 }
} elseif { $freescale_linux } {
# execute and wait for console
run_genode_until {.*\/ #.*} 220
set serial_id [output_spawn_id]
# wait for network to settle down
send -i $serial_id "sleep 5\n"
# test network
send -i $serial_id "wget http://genode.org/\n"
run_genode_until "Connecting to genode.org" 10 $serial_id
run_genode_until {.*\/ #.*} 30 $serial_id
}

View File

@ -0,0 +1,602 @@
/*
* \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[];
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;
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(...) { PERR("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<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<8192>("blk-signal-thread"),
_ready_lock(Lock::LOCKED),
_vm(vm)
{
Thread_base::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) {
PERR("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) {
PERR("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) {
PERR("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) { PERR("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) {
PERR("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) {
PERR("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) {
PERR("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) {
PERR("Block request cache full");
vm->smc_ret(0, 0);
} catch (::Block::Session::Tx::Source::Packet_alloc_failed) {
PERR("Failed to allocate packet for block request");
vm->smc_ret(0, 0);
} catch (Device_registry::Bad_device_id) {
PERR("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) {
PERR("Oversized block request");
} catch (Device_registry::Bad_device_id) {
PERR("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) {
PERR("Oversized block request");
vm->smc_ret(-1);
} catch (Device_registry::Bad_device_id) {
PERR("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:
PERR("Unknown function %lu requested on VMM block", vm->smc_arg_1());
break;
}
}

View File

@ -0,0 +1,73 @@
/*
* \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_ */

View File

@ -0,0 +1,53 @@
/*
* \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_ */

View File

@ -28,13 +28,9 @@ class Vm_base {
protected:
enum { INITRD_OFFSET = 0x1000000, };
Genode::Vm_connection _vm_con;
Genode::Rom_connection _kernel_rom;
Genode::Rom_connection _initrd_rom;
Genode::Dataspace_client _kernel_cap;
Genode::Dataspace_client _initrd_cap;
const char* _cmdline;
Genode::Vm_state *_state;
Genode::Io_mem_connection _ram_iomem;
@ -54,45 +50,39 @@ class Vm_base {
env()->rm_session()->detach((void*)addr);
}
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);
}
virtual Genode::addr_t _load_board_info() = 0;
virtual void _load_kernel_surroundings() = 0;
virtual Genode::addr_t _board_info_offset() const = 0;
public:
Vm_base(const char *kernel, const char *initrd, const char *cmdline,
class Inject_irq_failed : public Genode::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),
_initrd_rom(initrd),
_kernel_cap(_kernel_rom.dataspace()),
_initrd_cap(_initrd_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) { }
_board_rev(board_rev)
{
_state->irq_injection = 0;
}
void start()
{
Genode::memset((void*)_state, 0, sizeof(Genode::Vm_state));
_load_kernel();
_load_initrd();
_load_kernel_surroundings();
_state->cpsr = 0x93; /* SVC mode and IRQs disabled */
_state->r0 = 0;
_state->r1 = _mach_type;
_state->r2 = _ram.base() + _load_board_info(); /* board info addr */
_state->r2 = _ram.base() + _board_info_offset();
}
void sig_handler(Genode::Signal_context_capability sig_cap) {
@ -101,6 +91,12 @@ class Vm_base {
void run() { _vm_con.run(); }
void pause() { _vm_con.pause(); }
void inject_irq(unsigned const irq)
{
if (_state->irq_injection) { throw Inject_irq_failed(); }
_state->irq_injection = irq;
}
void dump()
{
using namespace Genode;
@ -175,6 +171,36 @@ class Vm_base {
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;
}
};
#endif /* _SRC__SERVER__VMM__INCLUDE__VM_H_ */

View File

@ -0,0 +1,60 @@
/*
* \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);
printf("[vm] %s\n", local_addr<char>());
_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:
PERR("Unknown function %lu requested on VMM serial", vm->smc_arg_1());
break;
}
}
Serial::Serial()
:
Attached_ram_dataspace(env()->ram_session(), BUF_SIZE), _off(0)
{ }

View File

@ -22,6 +22,8 @@
/* local includes */
#include <vm.h>
#include <m4if.h>
#include <serial.h>
#include <block.h>
using namespace Genode;
@ -35,6 +37,8 @@ enum {
static const char* cmdline_tablet = "console=ttymxc0,115200";
void on_vmm_entry();
void on_vmm_exit();
namespace Vmm {
class Vmm;
@ -45,24 +49,27 @@ class Vmm::Vmm : public Thread<8192>
{
private:
enum Devices {
FRAMEBUFFER,
INPUT,
};
Signal_receiver _sig_rcv;
Signal_context _vm_context;
Vm *_vm;
Io_mem_connection _m4if_io_mem;
M4if _m4if;
Serial _serial;
Block _block;
void _handle_hypervisor_call()
{
/* check device number*/
switch (_vm->state()->r0) {
case FRAMEBUFFER:
case INPUT:
break;
enum {
FRAMEBUFFER = 0,
INPUT = 1,
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;
default:
PERR("Unknown hypervisor call!");
_vm->dump();
@ -106,6 +113,7 @@ class Vmm::Vmm : public Thread<8192>
while (true) {
Signal s = _sig_rcv.wait_for_signal();
on_vmm_entry();
if (s.context() == &_vm_context) {
if (_handle_vm())
_vm->run();
@ -113,6 +121,7 @@ class Vmm::Vmm : public Thread<8192>
PWRN("Invalid context");
continue;
}
on_vmm_exit();
}
};
@ -132,7 +141,7 @@ class Vmm::Vmm : public Thread<8192>
int main()
{
static Vm vm("linux", "initrd.gz", cmdline_tablet,
static Vm vm("linux", cmdline_tablet,
Trustzone::NONSECURE_RAM_BASE, Trustzone::NONSECURE_RAM_SIZE,
KERNEL_OFFSET, MACH_TYPE_QSB);
static Vmm::Vmm vmm(&vm);

View File

@ -1,9 +1,14 @@
TARGET = tz_vmm
REQUIRES += trustzone imx53
LIBS = base
SRC_CC = main.cc
INC_DIR += $(PRG_DIR) \
$(PRG_DIR)/../../include \
$(PRG_DIR)/../imx53
LIBS += base
LIBS += config
SRC_CC += vm.cc
SRC_CC += main.cc
SRC_CC += serial.cc
SRC_CC += block.cc
INC_DIR += $(PRG_DIR)
INC_DIR += $(PRG_DIR)/../../include
INC_DIR += $(PRG_DIR)/../imx53
vpath main.cc $(PRG_DIR)/../imx53
vpath % $(PRG_DIR)/../..

View File

@ -0,0 +1,19 @@
/*
* \brief Virtual machine implementation
* \author Martin Stein
* \date 2015-06-10
*/
/*
* 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 <vm.h>
void on_vmm_entry() { }
void on_vmm_exit() { }

View File

@ -21,9 +21,24 @@ class Vm : public Vm_base
{
private:
enum { ATAG_OFFSET = 0x100 };
enum {
ATAG_OFFSET = 0x100,
INITRD_OFFSET = 0x1000000,
};
Genode::addr_t _load_board_info()
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);
}
void _load_atag()
{
Atag tag((void*)(_ram.local() + ATAG_OFFSET));
tag.setup_mem_tag(_ram.base(), _ram.size());
@ -32,17 +47,32 @@ class Vm : public Vm_base
if (_board_rev)
tag.setup_rev_tag(_board_rev);
tag.setup_end_tag();
return ATAG_OFFSET;
}
/*
* Vm_base interface
*/
void _load_kernel_surroundings()
{
_load_initrd();
_load_atag();
}
Genode::addr_t _board_info_offset() const { return ATAG_OFFSET; }
public:
Vm(const char *kernel, const char *initrd, const char *cmdline,
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, initrd, cmdline, ram_base, ram_size,
kernel_offset, mach_type, board_rev) {}
:
Vm_base(kernel, cmdline, ram_base, ram_size,
kernel_offset, mach_type, board_rev),
_initrd_rom("initrd.gz"),
_initrd_cap(_initrd_rom.dataspace())
{ }
};
#endif /* _SERVER__TZ_VMM__SPEC__IMX53_QSB__VM_H_ */

View File

@ -0,0 +1,34 @@
/*
* \brief Virtual machine implementation
* \author Martin Stein
* \date 2015-06-10
*/
/*
* 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 <vm.h>
#include <gpio_session/connection.h>
Gpio::Connection * led()
{
static Gpio::Connection led(123);
return &led;
}
void on_vmm_entry()
{
led()->direction(Gpio::Session::OUT);
led()->write(false);
}
void on_vmm_exit()
{
led()->write(true);
}

View File

@ -14,36 +14,44 @@
#ifndef _SERVER__TZ_VMM__SPEC__USB_ARMORY__VM_H_
#define _SERVER__TZ_VMM__SPEC__USB_ARMORY__VM_H_
/* local includes */
#include <vm_base.h>
class Vm : public Genode::Rom_connection,
public Genode::Dataspace_client,
public Vm_base
public Genode::Dataspace_client,
public Vm_base
{
private:
enum { DTB_OFFSET = 0x2000000 };
enum { DTB_OFFSET = 0x1000000 };
Genode::addr_t _load_board_info()
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);
return DTB_OFFSET;
}
/*
* Vm_base interface
*/
void _load_kernel_surroundings() { _load_dtb(); }
Genode::addr_t _board_info_offset() const { return DTB_OFFSET; }
public:
Vm(const char *kernel, const char *initrd, const char *cmdline,
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, initrd, cmdline, ram_base, ram_size,
kernel_offset, mach_type, board_rev) {}
Vm_base(kernel, cmdline, ram_base, ram_size,
kernel_offset, mach_type, board_rev)
{ }
};
#endif /* _SERVER__TZ_VMM__SPEC__USB_ARMORY__VM_H_ */