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_ */