virtio-9p: Add a virtio 9p device to qemu
This patch doesn't implement the 9p protocol handling code. It adds a simple device which dump the protocol data. [jvrao@linux.vnet.ibm.com: Little-Endian to host format conversion] [aneesh.kumar@linux.vnet.ibm.com: Multiple-mounts support] Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										26
									
								
								hw/9p.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								hw/9p.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										484
									
								
								hw/virtio-9p-debug.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										484
									
								
								hw/virtio-9p-debug.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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"); | ||||
| } | ||||
							
								
								
									
										17
									
								
								hw/virtio-9p-local.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								hw/virtio-9p-local.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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 = { | ||||
| }; | ||||
							
								
								
									
										307
									
								
								hw/virtio-9p.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								hw/virtio-9p.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
| } | ||||
							
								
								
									
										166
									
								
								hw/virtio-9p.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								hw/virtio-9p.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -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 */ | ||||
|     } | ||||
| }; | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user