diff --git a/repos/os/run/tz_vmm.run b/repos/os/run/tz_vmm.run
index e1a449627..f229dc625 100644
--- a/repos/os/run/tz_vmm.run
+++ b/repos/os/run/tz_vmm.run
@@ -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 {
+
@@ -29,77 +132,150 @@ install_config {
-
-
-
+ }
+
+append_if $trusted_led config {
+
+
+
+
+ }
+
+append_if $mmc_rootfs config {
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append config {
+
+ }
+
+if { $mmc_rootfs } {
+ append config "
+
+
+
+
+
+
+ "
}
-set init_uri "http://genode.org/files/images/imx53_qsb/initrd.gz"
+append 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
+}
diff --git a/repos/os/src/server/tz_vmm/block.cc b/repos/os/src/server/tz_vmm/block.cc
new file mode 100644
index 000000000..8739b0f38
--- /dev/null
+++ b/repos/os/src/server/tz_vmm/block.cc
@@ -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
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+
+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(_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;
+ }
+}
diff --git a/repos/os/src/server/tz_vmm/include/block.h b/repos/os/src/server/tz_vmm/include/block.h
new file mode 100644
index 000000000..a1bbec09a
--- /dev/null
+++ b/repos/os/src/server/tz_vmm/include/block.h
@@ -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
+
+/* local includes */
+#include
+
+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_ */
diff --git a/repos/os/src/server/tz_vmm/include/serial.h b/repos/os/src/server/tz_vmm/include/serial.h
new file mode 100644
index 000000000..d09ac159a
--- /dev/null
+++ b/repos/os/src/server/tz_vmm/include/serial.h
@@ -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
+#include
+
+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_ */
diff --git a/repos/os/src/server/tz_vmm/include/vm_base.h b/repos/os/src/server/tz_vmm/include/vm_base.h
index 6bf41cc1e..26aad675b 100644
--- a/repos/os/src/server/tz_vmm/include/vm_base.h
+++ b/repos/os/src/server/tz_vmm/include/vm_base.h
@@ -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_ */
diff --git a/repos/os/src/server/tz_vmm/serial.cc b/repos/os/src/server/tz_vmm/serial.cc
new file mode 100644
index 000000000..e6e3a4ac5
--- /dev/null
+++ b/repos/os/src/server/tz_vmm/serial.cc
@@ -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
+
+using namespace Genode;
+using namespace Vmm;
+
+
+void Serial::_push(char const c)
+{
+ local_addr()[_off] = c;
+ _off += sizeof(char);
+}
+
+
+void Serial::_flush()
+{
+ _push(0);
+ printf("[vm] %s\n", local_addr());
+ _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)
+{ }
diff --git a/repos/os/src/server/tz_vmm/spec/imx53/main.cc b/repos/os/src/server/tz_vmm/spec/imx53/main.cc
index 2ee5a3c18..6c3ff240d 100644
--- a/repos/os/src/server/tz_vmm/spec/imx53/main.cc
+++ b/repos/os/src/server/tz_vmm/spec/imx53/main.cc
@@ -22,6 +22,8 @@
/* local includes */
#include
#include
+#include
+#include
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);
diff --git a/repos/os/src/server/tz_vmm/spec/imx53/target.inc b/repos/os/src/server/tz_vmm/spec/imx53/target.inc
index e7145a9c4..8db672a32 100644
--- a/repos/os/src/server/tz_vmm/spec/imx53/target.inc
+++ b/repos/os/src/server/tz_vmm/spec/imx53/target.inc
@@ -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)/../..
diff --git a/repos/os/src/server/tz_vmm/spec/imx53_qsb/vm.cc b/repos/os/src/server/tz_vmm/spec/imx53_qsb/vm.cc
new file mode 100644
index 000000000..7b366da59
--- /dev/null
+++ b/repos/os/src/server/tz_vmm/spec/imx53_qsb/vm.cc
@@ -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
+
+void on_vmm_entry() { }
+
+void on_vmm_exit() { }
diff --git a/repos/os/src/server/tz_vmm/spec/imx53_qsb/vm.h b/repos/os/src/server/tz_vmm/spec/imx53_qsb/vm.h
index 07f634ed7..58f753270 100644
--- a/repos/os/src/server/tz_vmm/spec/imx53_qsb/vm.h
+++ b/repos/os/src/server/tz_vmm/spec/imx53_qsb/vm.h
@@ -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_ */
diff --git a/repos/os/src/server/tz_vmm/spec/usb_armory/vm.cc b/repos/os/src/server/tz_vmm/spec/usb_armory/vm.cc
new file mode 100644
index 000000000..5eda791ca
--- /dev/null
+++ b/repos/os/src/server/tz_vmm/spec/usb_armory/vm.cc
@@ -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
+#include
+
+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);
+}
diff --git a/repos/os/src/server/tz_vmm/spec/usb_armory/vm.h b/repos/os/src/server/tz_vmm/spec/usb_armory/vm.h
index 74acab19d..071f55ef7 100644
--- a/repos/os/src/server/tz_vmm/spec/usb_armory/vm.h
+++ b/repos/os/src/server/tz_vmm/spec/usb_armory/vm.h
@@ -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
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_ */