b1cb1ceaf9
Additionally use the block session interface asynchronously to let the VM continue execution. Fixes #806
323 lines
9.0 KiB
C++
323 lines
9.0 KiB
C++
/*
|
|
* \brief Block interface
|
|
* \author Markus Partheymueller
|
|
* \author Alexander Boettcher
|
|
* \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>
|
|
#include <base/heap.h>
|
|
#include <base/lock.h>
|
|
|
|
/* local includes */
|
|
#include <disk.h>
|
|
#include <utcb_guard.h>
|
|
|
|
/* Seoul includes */
|
|
#include <host/dma.h>
|
|
|
|
static Genode::Signal_receiver* disk_receiver()
|
|
{
|
|
static Genode::Signal_receiver receiver;
|
|
return &receiver;
|
|
}
|
|
|
|
|
|
static Genode::Heap * disk_heap() {
|
|
using namespace Genode;
|
|
static Heap heap(env()->ram_session(), env()->rm_session(), 4096);
|
|
return &heap;
|
|
}
|
|
|
|
|
|
Vancouver_disk::Vancouver_disk(Synced_motherboard &mb,
|
|
char * backing_store_base,
|
|
char * backing_store_fb_base)
|
|
:
|
|
_motherboard(mb), _backing_store_base(backing_store_base),
|
|
_backing_store_fb_base(backing_store_fb_base),
|
|
_tslab_msg(disk_heap()), _tslab_dma(disk_heap()), _tslab_avl(disk_heap())
|
|
{
|
|
/* initialize struct with 0 size */
|
|
for (int i=0; i < MAX_DISKS; i++) {
|
|
_diskcon[i].blk_size = 0;
|
|
}
|
|
|
|
start();
|
|
}
|
|
|
|
|
|
void Vancouver_disk::register_host_operations(Motherboard &motherboard)
|
|
{
|
|
motherboard.bus_disk.add(this, receive_static<MessageDisk>);
|
|
}
|
|
|
|
|
|
void Vancouver_disk::entry()
|
|
{
|
|
Logging::printf("Hello, this is Vancouver_disk.\n");
|
|
|
|
while (true) {
|
|
Genode::Signal signal = disk_receiver()->wait_for_signal();
|
|
Vancouver_disk_signal * disk_source =
|
|
reinterpret_cast<Vancouver_disk_signal *>(signal.context());
|
|
|
|
this->_signal_dispatch_entry(disk_source->disk_nr());
|
|
}
|
|
}
|
|
|
|
|
|
void Vancouver_disk::_signal_dispatch_entry(unsigned disknr)
|
|
{
|
|
Block::Session::Tx::Source *source = _diskcon[disknr].blk_con->tx();
|
|
|
|
while (source->ack_avail())
|
|
{
|
|
Block::Packet_descriptor packet = source->get_acked_packet();
|
|
char * source_addr = source->packet_content(packet);
|
|
|
|
/* find the corresponding MessageDisk object */
|
|
Avl_entry * obj = 0;
|
|
{
|
|
Genode::Lock::Guard lock_guard(_lookup_msg_lock);
|
|
obj = _lookup_msg.first();
|
|
if (obj)
|
|
obj = obj->find(reinterpret_cast<Genode::addr_t>(source_addr));
|
|
|
|
if (!obj) {
|
|
PWRN("Unknown MessageDisk object - drop ack of block session");
|
|
continue;
|
|
}
|
|
|
|
/* remove helper object */
|
|
_lookup_msg.remove(obj);
|
|
}
|
|
/* got the MessageDisk object */
|
|
MessageDisk * msg = obj->msg();
|
|
/* delete helper object */
|
|
destroy(&_tslab_avl, obj);
|
|
|
|
/* go ahead and tell VMM about new block event */
|
|
if (!packet.succeeded() ||
|
|
!(packet.operation() == Block::Packet_descriptor::Opcode::READ ||
|
|
packet.operation() == Block::Packet_descriptor::Opcode::WRITE)) {
|
|
|
|
PDBG("Getting block failed !");
|
|
|
|
MessageDiskCommit mdc(disknr, msg->usertag,
|
|
MessageDisk::DISK_STATUS_DEVICE);
|
|
_motherboard()->bus_diskcommit.send(mdc);
|
|
|
|
} else {
|
|
|
|
if (packet.operation() == Block::Packet_descriptor::Opcode::READ) {
|
|
|
|
unsigned long long sector = msg->sector;
|
|
sector = (sector-packet.block_number()) * _diskcon[disknr].blk_size;
|
|
|
|
for (unsigned i = 0; i < msg->dmacount; i++) {
|
|
char * dma_addr = _backing_store_base +
|
|
msg->dma[i].byteoffset + msg->physoffset;
|
|
|
|
// check for bounds
|
|
if (dma_addr >= _backing_store_fb_base ||
|
|
dma_addr < _backing_store_base) {
|
|
PERR("dma bounds violation");
|
|
} else
|
|
memcpy(dma_addr, source_addr + sector,
|
|
msg->dma[i].bytecount);
|
|
|
|
sector += msg->dma[i].bytecount;
|
|
}
|
|
|
|
destroy(&_tslab_dma, msg->dma);
|
|
msg->dma = 0;
|
|
}
|
|
|
|
MessageDiskCommit mdc (disknr, msg->usertag, MessageDisk::DISK_OK);
|
|
_motherboard()->bus_diskcommit.send(mdc);
|
|
}
|
|
|
|
source->release_packet(packet);
|
|
destroy(&_tslab_msg, msg);
|
|
}
|
|
}
|
|
|
|
|
|
bool Vancouver_disk::receive(MessageDisk &msg)
|
|
{
|
|
static Genode::Native_utcb utcb_backup;
|
|
Utcb_guard guard(utcb_backup);
|
|
|
|
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) {
|
|
|
|
try {
|
|
Genode::Allocator_avl * block_alloc =
|
|
new Genode::Allocator_avl(disk_heap());
|
|
|
|
_diskcon[msg.disknr].blk_con =
|
|
new Block::Connection(block_alloc, 4*512*1024, label);
|
|
_diskcon[msg.disknr].dispatcher =
|
|
new Vancouver_disk_signal(*disk_receiver(), *this,
|
|
&Vancouver_disk::_signal_dispatch_entry,
|
|
msg.disknr);
|
|
|
|
_diskcon[msg.disknr].blk_con->tx_channel()->sigh_ack_avail(
|
|
*_diskcon[msg.disknr].dispatcher);
|
|
} 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();
|
|
|
|
msg.error = MessageDisk::DISK_OK;
|
|
|
|
switch (msg.type) {
|
|
case MessageDisk::DISK_GET_PARAMS:
|
|
{
|
|
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 write = (msg.type == MessageDisk::DISK_WRITE);
|
|
|
|
if (write && !_diskcon[msg.disknr].ops.supported(Block::Packet_descriptor::WRITE)) {
|
|
MessageDiskCommit ro(msg.disknr, msg.usertag,
|
|
MessageDisk::DISK_STATUS_DEVICE);
|
|
_motherboard()->bus_diskcommit.send(ro);
|
|
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;
|
|
unsigned const blk_size = _diskcon[msg.disknr].blk_size;
|
|
|
|
if (blocks * blk_size < total) blocks++;
|
|
|
|
Block::Session::Tx::Source *source = _diskcon[msg.disknr].blk_con->tx();
|
|
Block::Packet_descriptor packet;
|
|
|
|
/* save original msg, required when we get acknowledgements */
|
|
MessageDisk * msg_cpy = 0;
|
|
try {
|
|
msg_cpy = new (&_tslab_msg) MessageDisk(msg);
|
|
|
|
packet = Block::Packet_descriptor(
|
|
source->alloc_packet(blocks * blk_size),
|
|
(write) ? Block::Packet_descriptor::WRITE
|
|
: Block::Packet_descriptor::READ,
|
|
sector, blocks);
|
|
} catch (...) {
|
|
if (msg_cpy)
|
|
destroy(&_tslab_msg, msg_cpy);
|
|
Logging::printf("could not allocate disk block elements\n");
|
|
return false;
|
|
}
|
|
|
|
char * source_addr = source->packet_content(packet);
|
|
{
|
|
Genode::Lock::Guard lock_guard(_lookup_msg_lock);
|
|
_lookup_msg.insert(new (&_tslab_avl) Avl_entry(source_addr,
|
|
msg_cpy));
|
|
}
|
|
|
|
/* copy DMA descriptors for read requests - they may change */
|
|
if (!write) {
|
|
msg_cpy->dma = new (&_tslab_dma) DmaDescriptor[msg_cpy->dmacount];
|
|
for (unsigned i = 0; i < msg_cpy->dmacount; i++)
|
|
memcpy(msg_cpy->dma + i, msg.dma + i, sizeof(DmaDescriptor));
|
|
}
|
|
|
|
|
|
/* for write operation */
|
|
source_addr += (sector - packet.block_number()) * blk_size;
|
|
|
|
/* check bounds for read and write operations */
|
|
for (unsigned i = 0; i < msg_cpy->dmacount; i++) {
|
|
char * dma_addr = _backing_store_base + msg_cpy->dma[i].byteoffset
|
|
+ msg_cpy->physoffset;
|
|
|
|
/* check for bounds */
|
|
if (dma_addr >= _backing_store_fb_base ||
|
|
dma_addr < _backing_store_base) {
|
|
/* drop allocated objects not needed in error case */
|
|
if (write)
|
|
destroy(&_tslab_dma, msg_cpy->dma);
|
|
destroy(&_tslab_msg, msg_cpy);
|
|
source->release_packet(packet);
|
|
return false;
|
|
}
|
|
|
|
if (write) {
|
|
memcpy(source_addr, dma_addr, msg.dma[i].bytecount);
|
|
source_addr += msg.dma[i].bytecount;
|
|
}
|
|
}
|
|
|
|
source->submit_packet(packet);
|
|
}
|
|
break;
|
|
default:
|
|
Logging::printf("Got MessageDisk type %x\n", msg.type);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
Vancouver_disk::~Vancouver_disk()
|
|
{
|
|
/* XXX: Close all connections */
|
|
}
|