diff options
-rw-r--r-- | Makefile.target | 1 | ||||
-rw-r--r-- | fsdev/qemu-fsdev.c | 2 | ||||
-rw-r--r-- | fsdev/qemu-fsdev.h | 1 | ||||
-rw-r--r-- | hw/9p.h | 26 | ||||
-rw-r--r-- | hw/virtio-9p-debug.c | 484 | ||||
-rw-r--r-- | hw/virtio-9p-local.c | 17 | ||||
-rw-r--r-- | hw/virtio-9p.c | 307 | ||||
-rw-r--r-- | hw/virtio-9p.h | 166 | ||||
-rw-r--r-- | hw/virtio-pci.c | 32 | ||||
-rw-r--r-- | hw/virtio.h | 7 |
10 files changed, 1042 insertions, 1 deletions
diff --git a/Makefile.target b/Makefile.target index 65beed5f3c..7ea753b8f9 100644 --- a/Makefile.target +++ b/Makefile.target @@ -168,6 +168,7 @@ obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o obj-y += vhost_net.o obj-$(CONFIG_VHOST_NET) += vhost.o +obj-$(CONFIG_LINUX) += virtio-9p.o virtio-9p-debug.o virtio-9p-local.o obj-y += rwhandler.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o obj-$(CONFIG_NO_KVM) += kvm-stub.o diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c index 48bbfd95e7..813e1f77a4 100644 --- a/fsdev/qemu-fsdev.c +++ b/fsdev/qemu-fsdev.c @@ -21,7 +21,7 @@ static QTAILQ_HEAD(FsTypeEntry_head, FsTypeListEntry) fstype_entries = QTAILQ_HEAD_INITIALIZER(fstype_entries); static FsTypeTable FsTypes[] = { - { .name = "local", .ops = NULL}, + { .name = "local", .ops = &local_ops}, }; int qemu_fsdev_add(QemuOpts *opts) diff --git a/fsdev/qemu-fsdev.h b/fsdev/qemu-fsdev.h index 7fa4f60376..b50fbe057b 100644 --- a/fsdev/qemu-fsdev.h +++ b/fsdev/qemu-fsdev.h @@ -50,4 +50,5 @@ typedef struct FsTypeListEntry { extern int qemu_fsdev_add(QemuOpts *opts); extern FsTypeEntry *get_fsdev_fsentry(char *id); +extern FileOperations local_ops; #endif diff --git a/hw/9p.h b/hw/9p.h new file mode 100644 index 0000000000..5fdd770ffc --- /dev/null +++ b/hw/9p.h @@ -0,0 +1,26 @@ +/* + * Virtio 9p + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_9P_H +#define QEMU_9P_H + +#include <stdbool.h> + +typedef struct V9fsConf +{ + /* tag name for the device */ + char *tag; + char *fsdev_id; +} V9fsConf; + +#endif diff --git a/hw/virtio-9p-debug.c b/hw/virtio-9p-debug.c new file mode 100644 index 0000000000..2fb2673d93 --- /dev/null +++ b/hw/virtio-9p-debug.c @@ -0,0 +1,484 @@ +/* + * Virtio 9p PDU debug + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#include "virtio.h" +#include "pc.h" +#include "virtio-9p.h" +#include "virtio-9p-debug.h" + +#define BUG_ON(cond) assert(!(cond)) + +static FILE *llogfile; + +static struct iovec *get_sg(V9fsPDU *pdu, int rx) +{ + if (rx) { + return pdu->elem.in_sg; + } + return pdu->elem.out_sg; +} + +static int get_sg_count(V9fsPDU *pdu, int rx) +{ + if (rx) { + return pdu->elem.in_num; + } + return pdu->elem.out_num; + +} + +static void pprint_int8(V9fsPDU *pdu, int rx, size_t *offsetp, + const char *name) +{ + size_t copied; + int count = get_sg_count(pdu, rx); + size_t offset = *offsetp; + struct iovec *sg = get_sg(pdu, rx); + int8_t value; + + copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); + + BUG_ON(copied != sizeof(value)); + offset += sizeof(value); + fprintf(llogfile, "%s=0x%x", name, value); + *offsetp = offset; +} + +static void pprint_int16(V9fsPDU *pdu, int rx, size_t *offsetp, + const char *name) +{ + size_t copied; + int count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + int16_t value; + + + copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); + + BUG_ON(copied != sizeof(value)); + offset += sizeof(value); + fprintf(llogfile, "%s=0x%x", name, value); + *offsetp = offset; +} + +static void pprint_int32(V9fsPDU *pdu, int rx, size_t *offsetp, + const char *name) +{ + size_t copied; + int count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + int32_t value; + + + copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); + + BUG_ON(copied != sizeof(value)); + offset += sizeof(value); + fprintf(llogfile, "%s=0x%x", name, value); + *offsetp = offset; +} + +static void pprint_int64(V9fsPDU *pdu, int rx, size_t *offsetp, + const char *name) +{ + size_t copied; + int count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + int64_t value; + + + copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); + + BUG_ON(copied != sizeof(value)); + offset += sizeof(value); + fprintf(llogfile, "%s=0x%" PRIx64, name, value); + *offsetp = offset; +} + +static void pprint_str(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + int sg_count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + uint16_t tmp_size, size; + size_t result; + size_t copied = 0; + int i = 0; + + /* get the size */ + copied = do_pdu_unpack(&tmp_size, sg, sg_count, offset, sizeof(tmp_size)); + BUG_ON(copied != sizeof(tmp_size)); + size = le16_to_cpupu(&tmp_size); + offset += copied; + + fprintf(llogfile, "%s=", name); + for (i = 0; size && i < sg_count; i++) { + size_t len; + if (offset >= sg[i].iov_len) { + /* skip this sg */ + offset -= sg[i].iov_len; + continue; + } else { + len = MIN(sg[i].iov_len - offset, size); + result = fwrite(sg[i].iov_base + offset, 1, len, llogfile); + BUG_ON(result != len); + size -= len; + copied += len; + if (size) { + offset = 0; + continue; + } + } + } + *offsetp += copied; +} + +static void pprint_qid(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + fprintf(llogfile, "%s={", name); + pprint_int8(pdu, rx, offsetp, "type"); + pprint_int32(pdu, rx, offsetp, ", version"); + pprint_int64(pdu, rx, offsetp, ", path"); + fprintf(llogfile, "}"); +} + +static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + fprintf(llogfile, "%s={", name); + pprint_int16(pdu, rx, offsetp, "size"); + pprint_int16(pdu, rx, offsetp, ", type"); + pprint_int32(pdu, rx, offsetp, ", dev"); + pprint_qid(pdu, rx, offsetp, ", qid"); + pprint_int32(pdu, rx, offsetp, ", mode"); + pprint_int32(pdu, rx, offsetp, ", atime"); + pprint_int32(pdu, rx, offsetp, ", mtime"); + pprint_int64(pdu, rx, offsetp, ", length"); + pprint_str(pdu, rx, offsetp, ", name"); + pprint_str(pdu, rx, offsetp, ", uid"); + pprint_str(pdu, rx, offsetp, ", gid"); + pprint_str(pdu, rx, offsetp, ", muid"); + if (dotu) { + pprint_str(pdu, rx, offsetp, ", extension"); + pprint_int32(pdu, rx, offsetp, ", uid"); + pprint_int32(pdu, rx, offsetp, ", gid"); + pprint_int32(pdu, rx, offsetp, ", muid"); + } + fprintf(llogfile, "}"); +} + +static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + int sg_count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + uint16_t tmp_count, count, i; + size_t copied = 0; + + fprintf(llogfile, "%s={", name); + + /* Get the count */ + copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count)); + BUG_ON(copied != sizeof(tmp_count)); + count = le16_to_cpupu(&tmp_count); + offset += copied; + + for (i = 0; i < count; i++) { + char str[512]; + if (i) { + fprintf(llogfile, ", "); + } + snprintf(str, sizeof(str), "[%d]", i); + pprint_str(pdu, rx, &offset, str); + } + + fprintf(llogfile, "}"); + + *offsetp = offset; +} + +static void pprint_qids(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + int sg_count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + uint16_t tmp_count, count, i; + size_t copied = 0; + + fprintf(llogfile, "%s={", name); + + copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count)); + BUG_ON(copied != sizeof(tmp_count)); + count = le16_to_cpupu(&tmp_count); + offset += copied; + + for (i = 0; i < count; i++) { + char str[512]; + if (i) { + fprintf(llogfile, ", "); + } + snprintf(str, sizeof(str), "[%d]", i); + pprint_qid(pdu, rx, &offset, str); + } + + fprintf(llogfile, "}"); + + *offsetp = offset; +} + +static void pprint_sg(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + struct iovec *sg = get_sg(pdu, rx); + unsigned int count; + int i; + + if (rx) { + count = pdu->elem.in_num; + } else { + count = pdu->elem.out_num; + } + + fprintf(llogfile, "%s={", name); + for (i = 0; i < count; i++) { + if (i) { + fprintf(llogfile, ", "); + } + fprintf(llogfile, "(%p, 0x%zx)", sg[i].iov_base, sg[i].iov_len); + } + fprintf(llogfile, "}"); +} + +/* FIXME: read from a directory fid returns serialized stat_t's */ +#ifdef DEBUG_DATA +static void pprint_data(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + unsigned int count; + int32_t size; + int total, i, j; + ssize_t len; + + if (rx) { + count = pdu->elem.in_num; + } else + count = pdu->elem.out_num; + } + + BUG_ON((offset + sizeof(size)) > sg[0].iov_len); + + memcpy(&size, sg[0].iov_base + offset, sizeof(size)); + offset += sizeof(size); + + fprintf(llogfile, "size: %x\n", size); + + sg[0].iov_base += 11; /* skip header */ + sg[0].iov_len -= 11; + + total = 0; + for (i = 0; i < count; i++) { + total += sg[i].iov_len; + if (total >= size) { + /* trim sg list so writev does the right thing */ + sg[i].iov_len -= (total - size); + i++; + break; + } + } + + fprintf(llogfile, "%s={\"", name); + fflush(llogfile); + for (j = 0; j < i; j++) { + if (j) { + fprintf(llogfile, "\", \""); + fflush(llogfile); + } + + do { + len = writev(fileno(llogfile), &sg[j], 1); + } while (len == -1 && errno == EINTR); + fprintf(llogfile, "len == %ld: %m\n", len); + BUG_ON(len != sg[j].iov_len); + } + fprintf(llogfile, "\"}"); + + sg[0].iov_base -= 11; + sg[0].iov_len += 11; + +} +#endif + +void pprint_pdu(V9fsPDU *pdu) +{ + size_t offset = 7; + + if (llogfile == NULL) { + llogfile = fopen("/tmp/pdu.log", "w"); + } + + switch (pdu->id) { + case P9_TVERSION: + fprintf(llogfile, "TVERSION: ("); + pprint_int32(pdu, 0, &offset, "msize"); + pprint_str(pdu, 0, &offset, ", version"); + break; + case P9_RVERSION: + fprintf(llogfile, "RVERSION: ("); + pprint_int32(pdu, 1, &offset, "msize"); + pprint_str(pdu, 1, &offset, ", version"); + break; + case P9_TAUTH: + fprintf(llogfile, "TAUTH: ("); + pprint_int32(pdu, 0, &offset, "afid"); + pprint_str(pdu, 0, &offset, ", uname"); + pprint_str(pdu, 0, &offset, ", aname"); + if (dotu) { + pprint_int32(pdu, 0, &offset, ", n_uname"); + } + break; + case P9_RAUTH: + fprintf(llogfile, "RAUTH: ("); + pprint_qid(pdu, 1, &offset, "qid"); + break; + case P9_TATTACH: + fprintf(llogfile, "TATTACH: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int32(pdu, 0, &offset, ", afid"); + pprint_str(pdu, 0, &offset, ", uname"); + pprint_str(pdu, 0, &offset, ", aname"); + if (dotu) { + pprint_int32(pdu, 0, &offset, ", n_uname"); + } + break; + case P9_RATTACH: + fprintf(llogfile, "RATTACH: ("); + pprint_qid(pdu, 1, &offset, "qid"); + break; + case P9_TERROR: + fprintf(llogfile, "TERROR: ("); + break; + case P9_RERROR: + fprintf(llogfile, "RERROR: ("); + pprint_str(pdu, 1, &offset, "ename"); + if (dotu) { + pprint_int32(pdu, 1, &offset, ", ecode"); + } + break; + case P9_TFLUSH: + fprintf(llogfile, "TFLUSH: ("); + pprint_int16(pdu, 0, &offset, "oldtag"); + break; + case P9_RFLUSH: + fprintf(llogfile, "RFLUSH: ("); + break; + case P9_TWALK: + fprintf(llogfile, "TWALK: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int32(pdu, 0, &offset, ", newfid"); + pprint_strs(pdu, 0, &offset, ", wnames"); + break; + case P9_RWALK: + fprintf(llogfile, "RWALK: ("); + pprint_qids(pdu, 1, &offset, "wqids"); + break; + case P9_TOPEN: + fprintf(llogfile, "TOPEN: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int8(pdu, 0, &offset, ", mode"); + break; + case P9_ROPEN: + fprintf(llogfile, "ROPEN: ("); + pprint_qid(pdu, 1, &offset, "qid"); + pprint_int32(pdu, 1, &offset, ", iounit"); + break; + case P9_TCREATE: + fprintf(llogfile, "TCREATE: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_str(pdu, 0, &offset, ", name"); + pprint_int32(pdu, 0, &offset, ", perm"); + pprint_int8(pdu, 0, &offset, ", mode"); + if (dotu) { + pprint_str(pdu, 0, &offset, ", extension"); + } + break; + case P9_RCREATE: + fprintf(llogfile, "RCREATE: ("); + pprint_qid(pdu, 1, &offset, "qid"); + pprint_int32(pdu, 1, &offset, ", iounit"); + break; + case P9_TREAD: + fprintf(llogfile, "TREAD: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int64(pdu, 0, &offset, ", offset"); + pprint_int32(pdu, 0, &offset, ", count"); + pprint_sg(pdu, 0, &offset, ", sg"); + break; + case P9_RREAD: + fprintf(llogfile, "RREAD: ("); + pprint_int32(pdu, 1, &offset, "count"); + pprint_sg(pdu, 1, &offset, ", sg"); + offset = 7; +#ifdef DEBUG_DATA + pprint_data(pdu, 1, &offset, ", data"); +#endif + break; + case P9_TWRITE: + fprintf(llogfile, "TWRITE: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int64(pdu, 0, &offset, ", offset"); + pprint_int32(pdu, 0, &offset, ", count"); + break; + case P9_RWRITE: + fprintf(llogfile, "RWRITE: ("); + pprint_int32(pdu, 1, &offset, "count"); + break; + case P9_TCLUNK: + fprintf(llogfile, "TCLUNK: ("); + pprint_int32(pdu, 0, &offset, "fid"); + break; + case P9_RCLUNK: + fprintf(llogfile, "RCLUNK: ("); + break; + case P9_TREMOVE: + fprintf(llogfile, "TREMOVE: ("); + pprint_int32(pdu, 0, &offset, "fid"); + break; + case P9_RREMOVE: + fprintf(llogfile, "RREMOVE: ("); + break; + case P9_TSTAT: + fprintf(llogfile, "TSTAT: ("); + pprint_int32(pdu, 0, &offset, "fid"); + break; + case P9_RSTAT: + fprintf(llogfile, "RSTAT: ("); + offset += 2; /* ignored */ + pprint_stat(pdu, 1, &offset, "stat"); + break; + case P9_TWSTAT: + fprintf(llogfile, "TWSTAT: ("); + pprint_int32(pdu, 0, &offset, "fid"); + offset += 2; /* ignored */ + pprint_stat(pdu, 0, &offset, ", stat"); + break; + case P9_RWSTAT: + fprintf(llogfile, "RWSTAT: ("); + break; + default: + fprintf(llogfile, "unknown(%d): (", pdu->id); + break; + } + + fprintf(llogfile, ")\n"); +} diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c new file mode 100644 index 0000000000..379af60eed --- /dev/null +++ b/hw/virtio-9p-local.c @@ -0,0 +1,17 @@ +/* + * Virtio 9p Posix callback + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#include "virtio.h" +#include "virtio-9p.h" + +FileOperations local_ops = { +}; diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c new file mode 100644 index 0000000000..abd01c16b6 --- /dev/null +++ b/hw/virtio-9p.c @@ -0,0 +1,307 @@ +/* + * Virtio 9p backend + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "virtio.h" +#include "pc.h" +#include "qemu_socket.h" +#include "virtio-9p.h" +#include "fsdev/qemu-fsdev.h" +#include "virtio-9p-debug.h" + +int dotu = 1; +int debug_9p_pdu; + +static V9fsPDU *alloc_pdu(V9fsState *s) +{ + V9fsPDU *pdu = NULL; + + if (!QLIST_EMPTY(&s->free_list)) { + pdu = QLIST_FIRST(&s->free_list); + QLIST_REMOVE(pdu, next); + } + return pdu; +} + +static void free_pdu(V9fsState *s, V9fsPDU *pdu) +{ + if (pdu) { + QLIST_INSERT_HEAD(&s->free_list, pdu, next); + } +} + +size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count, + size_t offset, size_t size, int pack) +{ + int i = 0; + size_t copied = 0; + + for (i = 0; size && i < sg_count; i++) { + size_t len; + if (offset >= sg[i].iov_len) { + /* skip this sg */ + offset -= sg[i].iov_len; + continue; + } else { + len = MIN(sg[i].iov_len - offset, size); + if (pack) { + memcpy(sg[i].iov_base + offset, addr, len); + } else { + memcpy(addr, sg[i].iov_base + offset, len); + } + size -= len; + copied += len; + addr += len; + if (size) { + offset = 0; + continue; + } + } + } + + return copied; +} + +static void v9fs_version(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_attach(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_stat(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_walk(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_open(V9fsState *s, V9fsPDU *pdu) +{ if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_read(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_write(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_create(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_flush(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_remove(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu) +{ + if (debug_9p_pdu) { + pprint_pdu(pdu); + } +} + +typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu); + +static pdu_handler_t *pdu_handlers[] = { + [P9_TVERSION] = v9fs_version, + [P9_TATTACH] = v9fs_attach, + [P9_TSTAT] = v9fs_stat, + [P9_TWALK] = v9fs_walk, + [P9_TCLUNK] = v9fs_clunk, + [P9_TOPEN] = v9fs_open, + [P9_TREAD] = v9fs_read, +#if 0 + [P9_TAUTH] = v9fs_auth, +#endif + [P9_TFLUSH] = v9fs_flush, + [P9_TCREATE] = v9fs_create, + [P9_TWRITE] = v9fs_write, + [P9_TWSTAT] = v9fs_wstat, + [P9_TREMOVE] = v9fs_remove, +}; + +static void submit_pdu(V9fsState *s, V9fsPDU *pdu) +{ + pdu_handler_t *handler; + + if (debug_9p_pdu) { + pprint_pdu(pdu); + } + + BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers)); + + handler = pdu_handlers[pdu->id]; + BUG_ON(handler == NULL); + + handler(s, pdu); +} + +static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) +{ + V9fsState *s = (V9fsState *)vdev; + V9fsPDU *pdu; + ssize_t len; + + while ((pdu = alloc_pdu(s)) && + (len = virtqueue_pop(vq, &pdu->elem)) != 0) { + uint8_t *ptr; + + BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0); + BUG_ON(pdu->elem.out_sg[0].iov_len < 7); + + ptr = pdu->elem.out_sg[0].iov_base; + + memcpy(&pdu->size, ptr, 4); + pdu->id = ptr[4]; + memcpy(&pdu->tag, ptr + 5, 2); + + submit_pdu(s, pdu); + } + + free_pdu(s, pdu); +} + +static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features) +{ + features |= 1 << VIRTIO_9P_MOUNT_TAG; + return features; +} + +static V9fsState *to_virtio_9p(VirtIODevice *vdev) +{ + return (V9fsState *)vdev; +} + +static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) +{ + struct virtio_9p_config *cfg; + V9fsState *s = to_virtio_9p(vdev); + + cfg = qemu_mallocz(sizeof(struct virtio_9p_config) + + s->tag_len); + stw_raw(&cfg->tag_len, s->tag_len); + memcpy(cfg->tag, s->tag, s->tag_len); + memcpy(config, cfg, s->config_size); + qemu_free(cfg); +} + +VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) + { + V9fsState *s; + int i, len; + struct stat stat; + FsTypeEntry *fse; + + + s = (V9fsState *)virtio_common_init("virtio-9p", + VIRTIO_ID_9P, + sizeof(struct virtio_9p_config)+ + MAX_TAG_LEN, + sizeof(V9fsState)); + + /* initialize pdu allocator */ + QLIST_INIT(&s->free_list); + for (i = 0; i < (MAX_REQ - 1); i++) { + QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next); + } + + s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output); + + fse = get_fsdev_fsentry(conf->fsdev_id); + + if (!fse) { + /* We don't have a fsdev identified by fsdev_id */ + fprintf(stderr, "Virtio-9p device couldn't find fsdev " + "with the id %s\n", conf->fsdev_id); + exit(1); + } + + if (!fse->path || !conf->tag) { + /* we haven't specified a mount_tag or the path */ + fprintf(stderr, "fsdev with id %s needs path " + "and Virtio-9p device needs mount_tag arguments\n", + conf->fsdev_id); + exit(1); + } + + if (lstat(fse->path, &stat)) { + fprintf(stderr, "share path %s does not exist\n", fse->path); + exit(1); + } else if (!S_ISDIR(stat.st_mode)) { + fprintf(stderr, "share path %s is not a directory \n", fse->path); + exit(1); + } + + s->ctx.fs_root = qemu_strdup(fse->path); + len = strlen(conf->tag); + if (len > MAX_TAG_LEN) { + len = MAX_TAG_LEN; + } + /* s->tag is non-NULL terminated string */ + s->tag = qemu_malloc(len); + memcpy(s->tag, conf->tag, len); + s->tag_len = len; + s->ctx.uid = -1; + + s->ops = fse->ops; + s->vdev.get_features = virtio_9p_get_features; + s->config_size = sizeof(struct virtio_9p_config) + + s->tag_len; + s->vdev.get_config = virtio_9p_get_config; + + return &s->vdev; +} diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h new file mode 100644 index 0000000000..b95dbe48af --- /dev/null +++ b/hw/virtio-9p.h @@ -0,0 +1,166 @@ +#ifndef _QEMU_VIRTIO_9P_H +#define _QEMU_VIRTIO_9P_H + +#include <sys/types.h> +#include <dirent.h> +#include <sys/time.h> +#include <utime.h> + +#include "file-op-9p.h" + +/* The feature bitmap for virtio 9P */ +/* The mount point is specified in a config variable */ +#define VIRTIO_9P_MOUNT_TAG 0 + +enum { + P9_TVERSION = 100, + P9_RVERSION, + P9_TAUTH = 102, + P9_RAUTH, + P9_TATTACH = 104, + P9_RATTACH, + P9_TERROR = 106, + P9_RERROR, + P9_TFLUSH = 108, + P9_RFLUSH, + P9_TWALK = 110, + P9_RWALK, + P9_TOPEN = 112, + P9_ROPEN, + P9_TCREATE = 114, + P9_RCREATE, + P9_TREAD = 116, + P9_RREAD, + P9_TWRITE = 118, + P9_RWRITE, + P9_TCLUNK = 120, + P9_RCLUNK, + P9_TREMOVE = 122, + P9_RREMOVE, + P9_TSTAT = 124, + P9_RSTAT, + P9_TWSTAT = 126, + P9_RWSTAT, +}; + + +/* qid.types */ +enum { + P9_QTDIR = 0x80, + P9_QTAPPEND = 0x40, + P9_QTEXCL = 0x20, + P9_QTMOUNT = 0x10, + P9_QTAUTH = 0x08, + P9_QTTMP = 0x04, + P9_QTSYMLINK = 0x02, + P9_QTLINK = 0x01, + P9_QTFILE = 0x00, +}; + +#define P9_NOTAG (u16)(~0) +#define P9_NOFID (u32)(~0) +#define P9_MAXWELEM 16 + +typedef struct V9fsPDU V9fsPDU; + +struct V9fsPDU +{ + uint32_t size; + uint16_t tag; + uint8_t id; + VirtQueueElement elem; + QLIST_ENTRY(V9fsPDU) next; +}; + + +/* FIXME + * 1) change user needs to set groups and stuff + */ + +/* from Linux's linux/virtio_9p.h */ + +/* The ID for virtio console */ +#define VIRTIO_ID_9P 9 +#define MAX_REQ 128 +#define MAX_TAG_LEN 32 + +#define BUG_ON(cond) assert(!(cond)) + +typedef struct V9fsFidState V9fsFidState; + +typedef struct V9fsString +{ + int16_t size; + char *data; +} V9fsString; + +typedef struct V9fsQID +{ + int8_t type; + int32_t version; + int64_t path; +} V9fsQID; + +typedef struct V9fsStat +{ + int16_t size; + int16_t type; + int32_t dev; + V9fsQID qid; + int32_t mode; + int32_t atime; + int32_t mtime; + int64_t length; + V9fsString name; + V9fsString uid; + V9fsString gid; + V9fsString muid; + /* 9p2000.u */ + V9fsString extension; + int32_t n_uid; + int32_t n_gid; + int32_t n_muid; +} V9fsStat; + +struct V9fsFidState +{ + int32_t fid; + V9fsString path; + int fd; + DIR *dir; + uid_t uid; + V9fsFidState *next; +}; + +typedef struct V9fsState +{ + VirtIODevice vdev; + VirtQueue *vq; + V9fsPDU pdus[MAX_REQ]; + QLIST_HEAD(, V9fsPDU) free_list; + V9fsFidState *fid_list; + FileOperations *ops; + FsContext ctx; + uint16_t tag_len; + uint8_t *tag; + size_t config_size; +} V9fsState; + +struct virtio_9p_config +{ + /* number of characters in tag */ + uint16_t tag_len; + /* Variable size tag name */ + uint8_t tag[0]; +} __attribute__((packed)); + +extern size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count, + size_t offset, size_t size, int pack); + +static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count, + size_t offset, size_t size) +{ + return pdu_packunpack(dst, sg, sg_count, offset, size, 0); +} + +#endif diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 7ce526f42c..7ddf612282 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -102,6 +102,9 @@ typedef struct { BlockConf block; NICConf nic; uint32_t host_features; +#ifdef CONFIG_LINUX + V9fsConf fsconf; +#endif /* Max. number of ports we can have for a the virtio-serial device */ uint32_t max_virtserial_ports; } VirtIOPCIProxy; @@ -639,6 +642,23 @@ static int virtio_balloon_init_pci(PCIDevice *pci_dev) return 0; } +#ifdef CONFIG_LINUX +static int virtio_9p_init_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + VirtIODevice *vdev; + + vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf); + virtio_init_pci(proxy, vdev, + PCI_VENDOR_ID_REDHAT_QUMRANET, + 0x1009, + 0x2, + 0x00); + + return 0; +} +#endif + static PCIDeviceInfo virtio_info[] = { { .qdev.name = "virtio-blk-pci", @@ -693,6 +713,18 @@ static PCIDeviceInfo virtio_info[] = { }, .qdev.reset = virtio_pci_reset, },{ +#ifdef CONFIG_LINUX + .qdev.name = "virtio-9p-pci", + .qdev.size = sizeof(VirtIOPCIProxy), + .init = virtio_9p_init_pci, + .qdev.props = (Property[]) { + DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), + DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag), + DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id), + DEFINE_PROP_END_OF_LIST(), + }, + }, { +#endif /* end of list */ } }; diff --git a/hw/virtio.h b/hw/virtio.h index f885f1b599..e4306cd750 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -20,6 +20,9 @@ #include "sysemu.h" #include "block_int.h" #include "event_notifier.h" +#ifdef CONFIG_LINUX +#include "9p.h" +#endif /* from Linux's linux/virtio_config.h */ @@ -185,6 +188,10 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf); VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf); VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports); VirtIODevice *virtio_balloon_init(DeviceState *dev); +#ifdef CONFIG_LINUX +VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); +#endif + void virtio_net_exit(VirtIODevice *vdev); |