From cbf4a7b0c3f7547d0369a2592dacb1028de4e5b8 Mon Sep 17 00:00:00 2001 From: Markus Partheymueller Date: Thu, 11 Oct 2012 16:41:02 +0200 Subject: [PATCH] vancouver: Disk support Vancouver can now assign block devices to guests using the Block interface. The machine has to be configured to use a specified drive, which could be theoretically routed to different partitions or services via policy definitions. Currently the USB driver only supports one device. Genode's AHCI driver is untested. If the session quota is too low, random pagefaults can occur on the stack. According to @Nils-TUD, it is necessary to protect the DiskCommit messages with a lock against deadlocking with the timer. Observations showed that this mitigates some problems with Gentoo on real hardware. --- ports/src/vancouver/device_model_registry.cc | 2 + ports/src/vancouver/disk.cc | 221 +++++++++++++++++++ ports/src/vancouver/disk.h | 71 ++++++ ports/src/vancouver/main.cc | 8 +- ports/src/vancouver/target.mk | 2 +- 5 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 ports/src/vancouver/disk.cc create mode 100644 ports/src/vancouver/disk.h diff --git a/ports/src/vancouver/device_model_registry.cc b/ports/src/vancouver/device_model_registry.cc index b5fd7b62c..4ce76f475 100644 --- a/ports/src/vancouver/device_model_registry.cc +++ b/ports/src/vancouver/device_model_registry.cc @@ -82,6 +82,8 @@ MODEL_INFO(pmtimer, "io_port") MODEL_INFO(pcihostbridge, "bus_num", "bus_count", "io_base", "mem_base") MODEL_INFO(i82576vf, "promisc", "mem_mmio", "mem_msix", "txpoll_us", "rx_map") +MODEL_INFO(ahci, "mem", "irq", "bdf") +MODEL_INFO(drive, "sigma0drive", "controller", "port") MODEL_INFO_NO_ARG(vbios_disk) MODEL_INFO_NO_ARG(vbios_keyboard) diff --git a/ports/src/vancouver/disk.cc b/ports/src/vancouver/disk.cc new file mode 100644 index 000000000..1b254bca8 --- /dev/null +++ b/ports/src/vancouver/disk.cc @@ -0,0 +1,221 @@ +/* + * \brief Block interface + * \author Markus Partheymueller + * \date 2012-09-15 + */ + +/* + * Copyright (C) 2012 Intel Corporation + * Copyright (C) 2013 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. + * + * The code is partially based on the Vancouver VMM, which is distributed + * under the terms of the GNU General Public License version 2. + * + * Modifications by Intel Corporation are contributed under the terms and + * conditions of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* local includes */ +#include + +static Genode::Native_utcb utcb_backup; + +extern Genode::Lock timeouts_lock; +extern bool disk_init; + + +Vancouver_disk::Vancouver_disk(Motherboard &mb, + char * backing_store_base, + char * backing_store_fb_base) +: + _mb(mb), _backing_store_base(backing_store_base), + _backing_store_fb_base(backing_store_fb_base) +{ + /* initialize struct with 0 size */ + for (int i=0; i < MAX_DISKS; i++) { + _diskcon[i].blk_size = 0; + } + start(); +} + + +void Vancouver_disk::entry() +{ + Logging::printf("Hello, this is Vancouver_disk.\n"); + + /* attach to disk bus */ + _mb.bus_disk.add(this, receive_static); + + disk_init = true; +} + + +bool Vancouver_disk::receive(MessageDisk &msg) +{ + utcb_backup = *Genode::Thread_base::myself()->utcb(); + + if (msg.disknr >= MAX_DISKS) + Logging::panic("You configured more disks than supported.\n"); + + /* + * If we receive a message for this disk the first time, create the + * structure for it. + */ + char label[14]; + Genode::snprintf(label, 14, "VirtualDisk %2u", msg.disknr); + + if (!_diskcon[msg.disknr].blk_size) { + + Genode::Allocator_avl * block_alloc = new Genode::Allocator_avl(Genode::env()->heap()); + try { + _diskcon[msg.disknr].blk_con = new Block::Connection(block_alloc, 4*512*1024, label); + } catch (...) { + + /* there is none. */ + return false; + } + + _diskcon[msg.disknr].blk_con->info( + &_diskcon[msg.disknr].blk_cnt, + &_diskcon[msg.disknr].blk_size, + &_diskcon[msg.disknr].ops); + + Logging::printf("Got info: %lu blocks (%u B), ops (R: %x, W:%x)\n ", + _diskcon[msg.disknr].blk_cnt, + _diskcon[msg.disknr].blk_size, + _diskcon[msg.disknr].ops.supported(Block::Packet_descriptor::READ), + _diskcon[msg.disknr].ops.supported(Block::Packet_descriptor::WRITE) + ); + } + + Block::Session::Tx::Source *source = _diskcon[msg.disknr].blk_con->tx(); + + switch (msg.type) { + case MessageDisk::DISK_GET_PARAMS: + { + msg.error = MessageDisk::DISK_OK; + msg.params->flags = DiskParameter::FLAG_HARDDISK; + msg.params->sectors = _diskcon[msg.disknr].blk_cnt; + msg.params->sectorsize = _diskcon[msg.disknr].blk_size; + msg.params->maxrequestcount = _diskcon[msg.disknr].blk_cnt; + memcpy(msg.params->name, label, strlen(label)); + msg.params->name[strlen(label)] = 0; + } + return true; + case MessageDisk::DISK_READ: + case MessageDisk::DISK_WRITE: + { + bool read = (msg.type == MessageDisk::DISK_READ); + + if (!read && !_diskcon[msg.disknr].ops.supported(Block::Packet_descriptor::WRITE)) { + MessageDiskCommit ro(msg.disknr, msg.usertag, + MessageDisk::DISK_STATUS_DEVICE); + _mb.bus_diskcommit.send(ro); + *Genode::Thread_base::myself()->utcb() = utcb_backup; + return true; + } + + unsigned long long sector = msg.sector; + unsigned long total = DmaDescriptor::sum_length(msg.dmacount, msg.dma); + unsigned long blocks = total/_diskcon[msg.disknr].blk_size; + + if (blocks*_diskcon[msg.disknr].blk_size < total) + blocks++; + + Block::Session::Tx::Source *source = _diskcon[msg.disknr].blk_con->tx(); + Block::Packet_descriptor + p(source->alloc_packet(blocks*_diskcon[msg.disknr].blk_size), + (read) ? Block::Packet_descriptor::READ + : Block::Packet_descriptor::WRITE, + sector, + blocks); + + if (read) { + source->submit_packet(p); + p = source->get_acked_packet(); + } + + if (!read || p.succeeded()) { + char * source_addr = source->packet_content(p); + + sector = (sector-p.block_number())*_diskcon[msg.disknr].blk_size; + + for (int i=0; i< msg.dmacount; i++) { + char * dma_addr = (char *) (msg.dma[i].byteoffset + msg.physoffset + + _backing_store_base); + + /* check for bounds */ + if (dma_addr >= _backing_store_fb_base + || dma_addr < _backing_store_base) + return false; + + if (read) + memcpy(dma_addr, source_addr+sector, msg.dma[i].bytecount); + else + memcpy(source_addr+sector, dma_addr, msg.dma[i].bytecount); + + sector += msg.dma[i].bytecount; + } + + if (!read) { + source->submit_packet(p); + p = source->get_acked_packet(); + if (!p.succeeded()) { + Logging::printf("Operation failed.\n"); + { + Genode::Lock::Guard guard(timeouts_lock); + MessageDiskCommit commit(msg.disknr, msg.usertag, + MessageDisk::DISK_STATUS_DEVICE); + _mb.bus_diskcommit.send(commit); + break; + } + } + } + + { + Genode::Lock::Guard guard(timeouts_lock); + MessageDiskCommit commit(msg.disknr, msg.usertag, + MessageDisk::DISK_OK); + _mb.bus_diskcommit.send(commit); + } + } else { + + Logging::printf("Operation failed.\n"); + { + Genode::Lock::Guard guard(timeouts_lock); + MessageDiskCommit commit(msg.disknr, msg.usertag, + MessageDisk::DISK_STATUS_DEVICE); + _mb.bus_diskcommit.send(commit); + } + } + + source->release_packet(p); + } + break; + + default: + + Logging::printf("Got MessageDisk type %x\n", msg.type); + *Genode::Thread_base::myself()->utcb() = utcb_backup; + return false; + } + *Genode::Thread_base::myself()->utcb() = utcb_backup; + return true; +} + + +Vancouver_disk::~Vancouver_disk() +{ + /* XXX: Close all connections */ +} diff --git a/ports/src/vancouver/disk.h b/ports/src/vancouver/disk.h new file mode 100644 index 000000000..348759936 --- /dev/null +++ b/ports/src/vancouver/disk.h @@ -0,0 +1,71 @@ +/* + * \brief Block interface + * \author Markus Partheymueller + * \date 2012-09-15 + */ + +/* + * Copyright (C) 2012 Intel Corporation + * Copyright (C) 2013 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. + * + * The code is partially based on the Vancouver VMM, which is distributed + * under the terms of the GNU General Public License version 2. + * + * Modifications by Intel Corporation are contributed under the terms and + * conditions of the GNU General Public License version 2. + */ + +#ifndef _DISK_H_ +#define _DISK_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* NOVA userland includes */ +#include +#include + +static const bool read_only = false; + + +class Vancouver_disk : public Genode::Thread<8192>, public StaticReceiver +{ + private: + + enum { MAX_DISKS = 16 }; + struct { + Block::Connection *blk_con; + Block::Session::Operations ops; + Genode::size_t blk_size; + Genode::size_t blk_cnt; + } _diskcon[MAX_DISKS]; + + Motherboard &_mb; + char *_backing_store_base; + char *_backing_store_fb_base; + + public: + + /** + * Constructor + */ + Vancouver_disk(Motherboard &mb, + char * backing_store_base, + char * backing_store_fb_base); + + ~Vancouver_disk(); + + void entry(); + + bool receive(MessageDisk &msg); +}; + +#endif /* _DISK_H_ */ diff --git a/ports/src/vancouver/main.cc b/ports/src/vancouver/main.cc index 629895085..4a30b5d74 100644 --- a/ports/src/vancouver/main.cc +++ b/ports/src/vancouver/main.cc @@ -63,6 +63,7 @@ #include #include #include +#include enum { PAGE_SIZE_LOG2 = 12UL, @@ -91,6 +92,7 @@ Genode::Lock global_lock(Genode::Lock::LOCKED); Genode::Lock timeouts_lock(Genode::Lock::UNLOCKED); volatile bool console_init = false; +volatile bool disk_init = false; /* Timer Service */ @@ -1558,8 +1560,12 @@ int main(int argc, char **argv) /* Create Console Thread */ Vancouver_console vcon(machine.get_mb(), fb_size, guest_memory.fb_ds()); + /* Create Disk Thread */ + Vancouver_disk vdisk(machine.get_mb(), guest_memory.backing_store_local_base(), + guest_memory.backing_store_fb_local_base()); + /* Wait for services */ - while (!console_init); + while (!console_init || !disk_init); machine.setup_devices(Genode::config()->xml_node().sub_node("machine")); diff --git a/ports/src/vancouver/target.mk b/ports/src/vancouver/target.mk index 2b9903e3e..c64019ee4 100644 --- a/ports/src/vancouver/target.mk +++ b/ports/src/vancouver/target.mk @@ -12,7 +12,7 @@ endif LIBS += cxx env blit thread alarm signal server SRC_CC = main.cc nova_user_env.cc device_model_registry.cc -SRC_CC += console.cc keyboard.cc network.cc +SRC_CC += console.cc keyboard.cc network.cc disk.cc SRC_BIN = mono.tff MODEL_SRC_CC += $(notdir $(wildcard $(VANCOUVER_DIR)/model/*.cc))