From 3f7a9348dd365edcfe58b8ecdf293b17a7d779ce Mon Sep 17 00:00:00 2001 From: Andreas Sandberg Date: Sat, 20 Sep 2014 17:17:54 -0400 Subject: dev: Add support for 9p proxying over VirtIO This patch adds support for 9p filesystem proxying over VirtIO. It can currently operate by connecting to a 9p server over a socket (VirtIO9PSocket) or by starting the diod 9p server and connecting over pipe (VirtIO9PDiod). *WARNING*: Checkpoints are currently not supported for systems with 9p proxies! --- src/dev/virtio/SConscript | 4 + src/dev/virtio/VirtIO9P.py | 70 +++++++ src/dev/virtio/fs9p.cc | 481 +++++++++++++++++++++++++++++++++++++++++++++ src/dev/virtio/fs9p.hh | 371 ++++++++++++++++++++++++++++++++++ 4 files changed, 926 insertions(+) create mode 100644 src/dev/virtio/VirtIO9P.py create mode 100644 src/dev/virtio/fs9p.cc create mode 100644 src/dev/virtio/fs9p.hh (limited to 'src/dev/virtio') diff --git a/src/dev/virtio/SConscript b/src/dev/virtio/SConscript index 42dc480f0..796aa76f3 100644 --- a/src/dev/virtio/SConscript +++ b/src/dev/virtio/SConscript @@ -45,13 +45,17 @@ if env['TARGET_ISA'] == 'null': SimObject('VirtIO.py') SimObject('VirtIOConsole.py') SimObject('VirtIOBlock.py') +SimObject('VirtIO9P.py') Source('base.cc') Source('pci.cc') Source('console.cc') Source('block.cc') +Source('fs9p.cc') DebugFlag('VIO', 'VirtIO base functionality') DebugFlag('VIOPci', 'VirtIO PCI transport') DebugFlag('VIOConsole', 'VirtIO console device') DebugFlag('VIOBlock', 'VirtIO block device') +DebugFlag('VIO9P', 'General 9p over VirtIO debugging') +DebugFlag('VIO9PData', 'Dump data in VirtIO 9p connections') diff --git a/src/dev/virtio/VirtIO9P.py b/src/dev/virtio/VirtIO9P.py new file mode 100644 index 000000000..a13107db4 --- /dev/null +++ b/src/dev/virtio/VirtIO9P.py @@ -0,0 +1,70 @@ +# -*- mode:python -*- + +# Copyright (c) 2014 ARM Limited +# All rights reserved. +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Andreas Sandberg + +from m5.params import * +from m5.proxy import * +from VirtIO import VirtIODeviceBase + +class VirtIO9PBase(VirtIODeviceBase): + type = 'VirtIO9PBase' + abstract = True + cxx_header = 'dev/virtio/fs9p.hh' + + queueSize = Param.Unsigned(32, "Output queue size (pages)") + tag = Param.String("gem5", "Mount tag") + + +class VirtIO9PProxy(VirtIO9PBase): + type = 'VirtIO9PProxy' + abstract = True + cxx_header = 'dev/virtio/fs9p.hh' + +class VirtIO9PDiod(VirtIO9PProxy): + type = 'VirtIO9PDiod' + cxx_header = 'dev/virtio/fs9p.hh' + + diod = Param.String("/usr/sbin/diod", "Path to diod") + root = Param.String("/tmp", "Path to export through diod") + +class VirtIO9PSocket(VirtIO9PProxy): + type = 'VirtIO9PSocket' + cxx_header = 'dev/virtio/fs9p.hh' + + server = Param.String("127.0.0.1", "9P server address or host name") + port = Param.String("564", "9P server port") diff --git a/src/dev/virtio/fs9p.cc b/src/dev/virtio/fs9p.cc new file mode 100644 index 000000000..b09ab15aa --- /dev/null +++ b/src/dev/virtio/fs9p.cc @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2014 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andreas Sandberg + */ + +#include +#include +#include +#include +#include +#include + +#include "debug/VIO9P.hh" +#include "debug/VIO9PData.hh" +#include "dev/virtio/fs9p.hh" +#include "params/VirtIO9PBase.hh" +#include "params/VirtIO9PDiod.hh" +#include "params/VirtIO9PProxy.hh" +#include "params/VirtIO9PSocket.hh" +#include "sim/system.hh" + +struct P9MsgInfo { + P9MsgInfo(P9MsgType _type, std::string _name) + : type(_type), name(_name) {} + + P9MsgType type; + std::string name; +}; + +typedef std::map P9MsgInfoMap; + +#define P9MSG(type, name) \ + { (type), P9MsgInfo((type), "T" # name ) }, \ + { (type + 1), P9MsgInfo((type + 1), "R" # name ) } + +static const P9MsgInfoMap p9_msg_info { + P9MSG(6, LERROR), + P9MSG(8, STATFS), + P9MSG(12, LOPEN), + P9MSG(14, LCREATE), + P9MSG(16, SYMLINK), + P9MSG(18, MKNOD), + P9MSG(20, RENAME), + P9MSG(22, READLINK), + P9MSG(24, GETATTR), + P9MSG(26, SETATTR), + P9MSG(30, XATTRWALK), + P9MSG(32, XATTRCREATE), + P9MSG(40, READDIR), + P9MSG(50, FSYNC), + P9MSG(52, LOCK), + P9MSG(54, GETLOCK), + P9MSG(70, LINK), + P9MSG(72, MKDIR), + P9MSG(74, RENAMEAT), + P9MSG(76, UNLINKAT), + P9MSG(100, VERSION), + P9MSG(102, AUTH), + P9MSG(104, ATTACH), + P9MSG(106, ERROR), + P9MSG(108, FLUSH), + P9MSG(110, WALK), + P9MSG(112, OPEN), + P9MSG(114, CREATE), + P9MSG(116, READ), + P9MSG(118, WRITE), + P9MSG(120, CLUNK), + P9MSG(122, REMOVE), + P9MSG(124, STAT), + P9MSG(126, WSTAT), +}; + +#undef P9MSG + +VirtIO9PBase::VirtIO9PBase(Params *params) + : VirtIODeviceBase(params, ID_9P, + sizeof(Config) + params->tag.size(), + F_MOUNT_TAG), + queue(params->system->physProxy, params->queueSize, *this) +{ + config.reset((Config *) + operator new(configSize)); + config->len = htov_legacy(params->tag.size()); + memcpy(config->tag, params->tag.c_str(), params->tag.size()); + + registerQueue(queue); +} + + +VirtIO9PBase::~VirtIO9PBase() +{ +} + +void +VirtIO9PBase::readConfig(PacketPtr pkt, Addr cfgOffset) +{ + readConfigBlob(pkt, cfgOffset, (uint8_t *)config.get()); +} + +void +VirtIO9PBase::FSQueue::onNotifyDescriptor(VirtDescriptor *desc) +{ + DPRINTF(VIO9P, "Got input data descriptor (len: %i)\n", desc->size()); + DPRINTF(VIO9P, "\tPending transactions: %i\n", parent.pendingTransactions.size()); + + P9MsgHeader header; + desc->chainRead(0, (uint8_t *)&header, sizeof(header)); + header = p9toh(header); + + uint8_t data[header.len - sizeof(header)]; + desc->chainRead(sizeof(header), data, sizeof(data)); + + // Keep track of pending transactions + parent.pendingTransactions[header.tag] = desc; + + DPRINTF(VIO9P, "recvTMsg\n"); + parent.dumpMsg(header, data, sizeof(data)); + + // Notify device of message + parent.recvTMsg(header, data, sizeof(data)); +} + +void +VirtIO9PBase::sendRMsg(const P9MsgHeader &header, const uint8_t *data, size_t size) +{ + DPRINTF(VIO9P, "Sending RMsg\n"); + dumpMsg(header, data, size); + DPRINTF(VIO9P, "\tPending transactions: %i\n", pendingTransactions.size()); + assert(header.len >= sizeof(header)); + + VirtDescriptor *main_desc(pendingTransactions[header.tag]); + pendingTransactions.erase(header.tag); + + // Find the first output descriptor + VirtDescriptor *out_desc(main_desc); + while (out_desc && !out_desc->isOutgoing()) + out_desc = out_desc->next(); + if (!out_desc) + panic("sendRMsg: Framing error, no output descriptor.\n"); + + P9MsgHeader header_out(htop9(header)); + header_out.len = htop9(sizeof(P9MsgHeader) + size); + + out_desc->chainWrite(0, (uint8_t *)&header_out, sizeof(header_out)); + out_desc->chainWrite(sizeof(header_out), data, size); + + queue.produceDescriptor(main_desc, sizeof(P9MsgHeader) + size); + kick(); +} + +void +VirtIO9PBase::dumpMsg(const P9MsgHeader &header, const uint8_t *data, size_t size) +{ +#ifndef NDEBUG + if (!DTRACE(VIO9P)) + return; + + const P9MsgInfoMap::const_iterator it_msg(p9_msg_info.find(header.type)); + if (it_msg != p9_msg_info.cend()) { + const P9MsgInfo &info(it_msg->second); + DPRINTF(VIO9P, "P9Msg[len = %i, type = %s (%i), tag = %i]\n", + header.len, info.name, header.type, header.tag); + } else { + DPRINTF(VIO9P, "P9Msg[len = %i, type = Unknown (%i), tag = %i]\n", + header.len, header.type, header.tag); + } + DDUMP(VIO9PData, data, size); +#endif +} + + +VirtIO9PProxy::VirtIO9PProxy(Params *params) + : VirtIO9PBase(params) +{ +} + +VirtIO9PProxy::~VirtIO9PProxy() +{ +} + + +void +VirtIO9PProxy::VirtIO9PProxy::serialize(std::ostream &os) +{ + fatal("Can't checkpoint a system with a VirtIO 9p proxy!\n"); +} + +void +VirtIO9PProxy::unserialize(Checkpoint *cp, const std::string §ion) +{ + fatal("Can't checkpoint a system with a VirtIO 9p proxy!\n"); +} + + +void +VirtIO9PProxy::recvTMsg(const P9MsgHeader &header, + const uint8_t *data, size_t size) +{ + assert(header.len == sizeof(header) + size); + // While technically not needed, we send the packet as one + // contiguous segment to make some packet dissectors happy. + uint8_t out[header.len]; + P9MsgHeader header_out(htop9(header)); + memcpy(out, (uint8_t *)&header_out, sizeof(header_out)); + memcpy(out + sizeof(header_out), data, size); + writeAll(out, sizeof(header_out) + size); +} + +void +VirtIO9PProxy::serverDataReady() +{ + P9MsgHeader header; + readAll((uint8_t *)&header, sizeof(header)); + header = p9toh(header); + + const ssize_t payload_len(header.len - sizeof(header)); + if (payload_len < 0) + panic("Payload length is negative!\n"); + uint8_t data[payload_len]; + readAll(data, payload_len); + + sendRMsg(header, data, payload_len); +} + + +void +VirtIO9PProxy::readAll(uint8_t *data, size_t len) +{ + while (len) { + ssize_t ret; + while ((ret = read(data, len)) == -EAGAIN) + ; + if (ret < 0) + panic("readAll: Read failed: %i\n", -ret); + + len -= ret; + data += ret; + } +} + +void +VirtIO9PProxy::writeAll(const uint8_t *data, size_t len) +{ + while (len) { + ssize_t ret; + while ((ret = write(data, len)) == -EAGAIN) + ; + if (ret < 0) + panic("writeAll: write failed: %i\n", -ret); + + len -= ret; + data += ret; + } +} + + + +VirtIO9PDiod::VirtIO9PDiod(Params *params) + : VirtIO9PProxy(params), + fd_to_diod(-1), fd_from_diod(-1) +{ +} + +VirtIO9PDiod::~VirtIO9PDiod() +{ +} + +void +VirtIO9PDiod::startup() +{ + startDiod(); + dataEvent.reset(new DiodDataEvent(*this, fd_from_diod, POLLIN)); + pollQueue.schedule(dataEvent.get()); +} + +void +VirtIO9PDiod::startDiod() +{ + const Params *p(dynamic_cast(params())); + int pipe_rfd[2]; + int pipe_wfd[2]; + const int DIOD_RFD = 3; + const int DIOD_WFD = 4; + + const char *diod(p->diod.c_str()); + + if (pipe(pipe_rfd) == -1 || pipe(pipe_wfd) == -1) + panic("Failed to create DIOD pipes: %i\n", errno); + + fd_to_diod = pipe_rfd[1]; + fd_from_diod = pipe_wfd[0]; + + diod_pid = fork(); + if (diod_pid == -1) { + panic("Fork failed: %i\n", errno); + } else if (diod_pid == 0) { + close(STDIN_FILENO); + + if (dup2(pipe_rfd[0], DIOD_RFD) == -1 || + dup2(pipe_wfd[1], DIOD_WFD) == -1) { + + panic("Failed to setup read/write pipes: %i\n", + errno); + } + + execlp(diod, diod, + "-f", // start in foreground + "-r", "3", // setup read FD + "-w", "4", // setup write FD + "-e", p->root.c_str(), // path to export + "-n", // disable security + "-S", // squash all users + (char *)NULL); + panic("Failed to execute diod: %i\n", errno); + } else { + close(pipe_rfd[0]); + close(pipe_wfd[1]); + } + +#undef DIOD_RFD +#undef DIOD_WFD +} + +ssize_t +VirtIO9PDiod::read(uint8_t *data, size_t len) +{ + assert(fd_from_diod != -1); + const int ret(::read(fd_from_diod, (void *)data, len)); + return ret < 0 ? -errno : ret; +} + +ssize_t +VirtIO9PDiod::write(const uint8_t *data, size_t len) +{ + assert(fd_to_diod != -1); + const int ret(::write(fd_to_diod, (const void *)data, len)); + return ret < 0 ? -errno : ret; +} + +void +VirtIO9PDiod::DiodDataEvent::process(int revent) +{ + parent.serverDataReady(); +} + +VirtIO9PDiod * +VirtIO9PDiodParams::create() +{ + return new VirtIO9PDiod(this); +} + + + + +VirtIO9PSocket::VirtIO9PSocket(Params *params) + : VirtIO9PProxy(params), fdSocket(-1) +{ +} + +VirtIO9PSocket::~VirtIO9PSocket() +{ +} + +void +VirtIO9PSocket::startup() +{ + connectSocket(); + dataEvent.reset(new SocketDataEvent(*this, fdSocket, POLLIN)); + pollQueue.schedule(dataEvent.get()); +} + +void +VirtIO9PSocket::connectSocket() +{ + const Params &p(dynamic_cast(*params())); + + int ret; + struct addrinfo hints, *result; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + + if ((ret = getaddrinfo(p.server.c_str(), p.port.c_str(), + &hints, &result)) != 0) + panic("getaddrinfo: %s\n", gai_strerror(ret)); + + DPRINTF(VIO9P, "Connecting to 9p server '%s'.\n", p.server); + for (struct addrinfo *rp = result; rp; rp = rp->ai_next) { + fdSocket = socket(rp->ai_family, rp->ai_socktype, + rp->ai_protocol); + if (fdSocket == -1) { + continue; + } else if (connect(fdSocket, rp->ai_addr, rp->ai_addrlen) != -1) { + break; + } else { + close(fdSocket); + fdSocket = -1; + } + } + + freeaddrinfo(result); + + if (fdSocket == -1) + panic("Failed to connect to 9p server (%s:%s)", p.server, p.port); +} + +void +VirtIO9PSocket::socketDisconnect() +{ + panic("9P Socket disconnected!\n"); +} + +ssize_t +VirtIO9PSocket::read(uint8_t *data, size_t len) +{ + assert(fdSocket != -1); + int ret; + + ret = ::recv(fdSocket, (void *)data, len, 0); + if (ret == 0) + socketDisconnect(); + + return ret < 0 ? -errno : ret; +} + +ssize_t +VirtIO9PSocket::write(const uint8_t *data, size_t len) +{ + assert(fdSocket != -1); + int ret(::send(fdSocket, (const void *)data, len, 0)); + return ret < 0 ? -errno : ret; +} + +void +VirtIO9PSocket::SocketDataEvent::process(int revent) +{ + parent.serverDataReady(); +} + + +VirtIO9PSocket * +VirtIO9PSocketParams::create() +{ + return new VirtIO9PSocket(this); +} diff --git a/src/dev/virtio/fs9p.hh b/src/dev/virtio/fs9p.hh new file mode 100644 index 000000000..2cbdbc9eb --- /dev/null +++ b/src/dev/virtio/fs9p.hh @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2014 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andreas Sandberg + */ + +#ifndef __DEV_VIRTIO_FS9P_HH__ +#define __DEV_VIRTIO_FS9P_HH__ + +#include +#include +#include + +#include "base/pollevent.hh" +#include "dev/virtio/base.hh" + +struct VirtIO9PBaseParams; + +typedef uint8_t P9MsgType; +typedef uint16_t P9Tag; + +struct P9MsgHeader { + /** Length including header */ + uint32_t len; + /** Message type */ + P9MsgType type; + /** Message tag */ + P9Tag tag; +} M5_ATTR_PACKED; + +/** Convert p9 byte order (LE) to host byte order */ +template inline T +p9toh(T v) { return letoh(v); } + +/** Convert host byte order to p9 byte order (LE) */ +template inline T +htop9(T v) { return htole(v); } + +template <> inline P9MsgHeader +p9toh(P9MsgHeader v) +{ + v.len = p9toh(v.len); + v.type = p9toh(v.type); + v.tag = p9toh(v.tag); + return v; +} + +template <> inline P9MsgHeader +htop9(P9MsgHeader v) +{ + v.len = htop9(v.len); + v.type = htop9(v.type); + v.tag = htop9(v.tag); + return v; +} + +/** + * This class implements a VirtIO transport layer for the 9p network + * file system. + * + * The 9p VirtIO transport uses the following queues: + * -# 9p requests and replies + * + * Each 9p request and response is sent in its own descriptor + * chain. The guest initiates a transaction by packing a T message + * (see the 9p spec) into the first part of a descriptor chain. After + * the T message, the guest reserves space for the reply (R message) + * by including one or more writable descriptors. The server replies + * by writing an R message into the writable descriptors and putting + * the chain in the used ring (VirtQueue::produceDescriptor()). + * + * @see https://github.com/rustyrussell/virtio-spec + * @see https://github.com/ericvh/9p-rfc + * @see https://code.google.com/p/diod/wiki/protocol + */ +class VirtIO9PBase : public VirtIODeviceBase +{ + public: + typedef VirtIO9PBaseParams Params; + VirtIO9PBase(Params *params); + virtual ~VirtIO9PBase(); + + void readConfig(PacketPtr pkt, Addr cfgOffset); + + protected: + /** + * VirtIO 9p configuration structure + * + * @note The fields in this structure depend on the features + * exposed to the guest. + */ + struct Config { + uint16_t len; + char tag[]; + } M5_ATTR_PACKED; + + /** Currently active configuration (host byte order) */ + std::unique_ptr config; + + /** VirtIO device ID */ + static const DeviceId ID_9P = 0x09; + + /** @{ + * @name Feature bits + */ + /** Device provides a name of the resource in its configuration */ + static const FeatureBits F_MOUNT_TAG = 0x01; + /** @} */ + + protected: + /** + * Virtqueue for 9p requests + */ + class FSQueue : public VirtQueue + { + public: + FSQueue(PortProxy &proxy, uint16_t size, VirtIO9PBase &_parent) + : VirtQueue(proxy, size), parent(_parent) {} + virtual ~FSQueue() {} + + void onNotifyDescriptor(VirtDescriptor *desc); + + std::string name() const { return parent.name() + ".queue"; } + + protected: + VirtIO9PBase &parent; + }; + + FSQueue queue; + + protected: + /** + * Handle incoming 9p RPC message. + * + * @param header 9p message header. + * @param data Pointer to data in message. + * @param size Size of data (excluding header) + */ + virtual void recvTMsg(const P9MsgHeader &header, const uint8_t *data, size_t size) = 0; + /** + * Send a 9p RPC message reply. + * + * @param header 9p message header. + * @param data Pointer to data in message. + * @param size Size of data (excluding header) + */ + void sendRMsg(const P9MsgHeader &header, const uint8_t *data, size_t size); + + /** + * Dump a 9p RPC message on the debug output + * + * @param header 9p message header. + * @param data Pointer to data in message. + * @param size Size of data (excluding header) + */ + void dumpMsg(const P9MsgHeader &header, const uint8_t *data, size_t size); + + private: + /** + * Map between 9p transaction tags and descriptors where they + * appeared. + * + * When handling asynchronous requests, we need to ensure that + * replies are posted in the same descriptor as queries. The 9p + * RPC protocol uses the tag field in the header to match requests + * and replies, which we use here to find the relevant descriptor. + */ + std::map pendingTransactions; +}; + +struct VirtIO9PProxyParams; + +/** + * VirtIO 9p proxy base class. + * + * This base class provides basic functionality shared by different 9p + * proxy implementations. + */ +class VirtIO9PProxy : public VirtIO9PBase +{ + public: + typedef VirtIO9PProxyParams Params; + VirtIO9PProxy(Params *params); + virtual ~VirtIO9PProxy(); + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + protected: + void recvTMsg(const P9MsgHeader &header, const uint8_t *data, size_t size); + + /** Notification of pending data from server */ + void serverDataReady(); + + /** + * Read data from the server behind the proxy. + * + * @note This method may return read fewer than len bytes. + * + * @param data Memory location to store results in. + * @param len Maximum length to read. + * @return Number of bytes read, -errno on failure. + */ + virtual ssize_t read(uint8_t *data, size_t len) = 0; + /** + * Write data to the server behind the proxy. + * + * @note This method may return write fewer than len bytes. + * + * @param data Pointer to data to write. + * @param len Maximum length to write. + * @return Number of bytes written, -errno on failure. + */ + virtual ssize_t write(const uint8_t *data, size_t len) = 0; + + /** + * Convenience function that reads exactly len bytes. + * + * This method calls read until exactly len number of bytes has + * been read. A read() call is retried if the underlying syscall + * was interrupted. + * + * @param data Memory location to store results in. + * @param len Number of bytes to read. + */ + void readAll(uint8_t *data, size_t len); + /** + * Convenience function that writes exactly len bytes. + * + * This method calls write until exactly len number of bytes has + * been written. A write() call is retried if the underlying + * syscall was interrupted. + * + * @param data Data to write. + * @param len Number of bytes to write. + */ + void writeAll(const uint8_t *data, size_t len); +}; + +struct VirtIO9PDiodParams; + +/** + * VirtIO 9p proxy that communicates with the diod 9p server using + * pipes. + */ +class VirtIO9PDiod : public VirtIO9PProxy +{ + public: + typedef VirtIO9PDiodParams Params; + VirtIO9PDiod(Params *params); + virtual ~VirtIO9PDiod(); + + void startup(); + + protected: + /** + * Start diod and setup the communication pipes. + */ + void startDiod(); + + ssize_t read(uint8_t *data, size_t len); + ssize_t write(const uint8_t *data, size_t len); + + private: + class DiodDataEvent : public PollEvent + { + public: + DiodDataEvent(VirtIO9PDiod &_parent, int fd, int event) + : PollEvent(fd, event), parent(_parent) {} + + virtual ~DiodDataEvent() {}; + + void process(int revent); + + private: + VirtIO9PDiod &parent; + }; + + /** fd for data pipe going to diod (write end) */ + int fd_to_diod; + /** fd for data pipe coming from diod (read end) */ + int fd_from_diod; + + std::unique_ptr dataEvent; + + /** PID of diod process */ + int diod_pid; +}; + +struct VirtIO9PSocketParams; + +/** + * VirtIO 9p proxy that communicates with a 9p server over tcp + * sockets. + */ +class VirtIO9PSocket : public VirtIO9PProxy +{ + public: + typedef VirtIO9PSocketParams Params; + VirtIO9PSocket(Params *params); + virtual ~VirtIO9PSocket(); + + void startup(); + + protected: + /** + * Try to resolve the server name and connect to the 9p server. + */ + void connectSocket(); + + /** 9p server disconnect notification */ + void socketDisconnect(); + + ssize_t read(uint8_t *data, size_t len); + ssize_t write(const uint8_t *data, size_t len); + + private: + class SocketDataEvent : public PollEvent + { + public: + SocketDataEvent(VirtIO9PSocket &_parent, int fd, int event) + : PollEvent(fd, event), parent(_parent) {} + + virtual ~SocketDataEvent() {}; + + void process(int revent); + + private: + VirtIO9PSocket &parent; + }; + + /** Socket connected to the 9p server */ + int fdSocket; + + std::unique_ptr dataEvent; +}; + +#endif // __DEV_VIRTIO_FS9P_HH__ -- cgit v1.2.3