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.
This commit is contained in:
Markus Partheymueller 2012-10-11 16:41:02 +02:00 committed by Norman Feske
parent 1c447d98e9
commit cbf4a7b0c3
5 changed files with 302 additions and 2 deletions

View File

@ -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)

221
ports/src/vancouver/disk.cc Normal file
View File

@ -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 <base/allocator_avl.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <base/thread.h>
#include <block_session/connection.h>
#include <util/string.h>
/* local includes */
#include <disk.h>
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<MessageDisk>);
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 */
}

View File

@ -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 <base/allocator_avl.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <base/thread.h>
#include <block_session/connection.h>
#include <util/string.h>
/* NOVA userland includes */
#include <nul/motherboard.h>
#include <host/dma.h>
static const bool read_only = false;
class Vancouver_disk : public Genode::Thread<8192>, public StaticReceiver<Vancouver_disk>
{
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_ */

View File

@ -63,6 +63,7 @@
#include <boot_module_provider.h>
#include <console.h>
#include <network.h>
#include <disk.h>
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"));

View File

@ -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))