diff options
Diffstat (limited to 'qemu/roms/SLOF/lib/libvirtio')
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/p9.c | 2 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/p9.h | 2 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio-9p.c | 35 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio-9p.h | 2 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio-blk.c | 100 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio-blk.h | 2 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio-internal.h | 48 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio-net.c | 200 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio-net.h | 15 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c | 174 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio.c | 457 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio.code | 23 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio.h | 58 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libvirtio/virtio.in | 2 |
14 files changed, 790 insertions, 330 deletions
diff --git a/qemu/roms/SLOF/lib/libvirtio/p9.c b/qemu/roms/SLOF/lib/libvirtio/p9.c index a55662994..0e5953031 100644 --- a/qemu/roms/SLOF/lib/libvirtio/p9.c +++ b/qemu/roms/SLOF/lib/libvirtio/p9.c @@ -143,7 +143,7 @@ int p9_transaction(p9_connection_t *connection) { int rc; int tx_size = GET_SIZE; - int rx_size = connection->message_size; + uint32_t rx_size = connection->message_size; if (transact == NULL) { return P9_NO_TRANSPORT; diff --git a/qemu/roms/SLOF/lib/libvirtio/p9.h b/qemu/roms/SLOF/lib/libvirtio/p9.h index 7df9ef441..3a35e80ed 100644 --- a/qemu/roms/SLOF/lib/libvirtio/p9.h +++ b/qemu/roms/SLOF/lib/libvirtio/p9.h @@ -33,7 +33,7 @@ #define P9_PARTIAL_WALK 1 typedef int (*p9_transact_t)(void *opaque, uint8_t *tx, int tx_size, - uint8_t *rx, int *rx_size); + uint8_t *rx, uint32_t *rx_size); typedef struct { uint32_t message_size; diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c index 5a5fd01da..fc5db9154 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c @@ -19,6 +19,7 @@ #include "virtio-9p.h" #include "p9.h" +static struct vqs vq; /** * Notes for 9P Server config: @@ -86,7 +87,7 @@ static void dprint_buffer(const char *name, uint8_t *buffer, int length) * @return 0 = success, -ve = error. */ static int virtio_9p_transact(void *opaque, uint8_t *tx, int tx_size, uint8_t *rx, - int *rx_size) + uint32_t *rx_size) { struct virtio_device *dev = opaque; struct vring_desc *desc; @@ -165,6 +166,7 @@ int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf, int buf_size) { struct vring_avail *vq_avail; + int status = VIRTIO_STAT_ACKNOWLEDGE; /* Check for double open */ if (__buf_size) @@ -174,28 +176,31 @@ int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf, dprintf("%s : device at %p\n", __func__, dev->base); dprintf("%s : type is %04x\n", __func__, dev->type); - /* Reset device */ - // XXX That will clear the virtq base. We need to move - // initializing it to here anyway - // - // virtio_reset_device(dev); + /* Keep it disabled until the driver is 1.0 capable */ + dev->is_modern = false; + + virtio_reset_device(dev); /* Acknowledge device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE); + virtio_set_status(dev, status); /* Tell HV that we know how to drive the device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER); + status |= VIRTIO_STAT_DRIVER; + virtio_set_status(dev, status); /* Device specific setup - we do not support special features */ virtio_set_guest_features(dev, 0); + if (virtio_queue_init_vq(dev, &vq, 0)) + goto dev_error; + vq_avail = virtio_get_vring_avail(dev, 0); vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT; vq_avail->idx = 0; /* Tell HV that setup succeeded */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER - |VIRTIO_STAT_DRIVER_OK); + status |= VIRTIO_STAT_DRIVER_OK; + virtio_set_status(dev, status); /* Setup 9P library. */ p9_reg_transport(virtio_9p_transact, dev,(uint8_t *)tx_buf, @@ -203,6 +208,12 @@ int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf, dprintf("%s : complete\n", __func__); return 0; + +dev_error: + printf("%s: failed\n", __func__); + status |= VIRTIO_STAT_FAILED; + virtio_set_status(dev, status); + return -1; } /** @@ -228,7 +239,7 @@ void virtio_9p_shutdown(struct virtio_device *dev) * @param buffer[out] Where to read the file to. * @return +ve = amount of data read, -ve = error. */ -int virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer) +long virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer) { int rc; uint16_t tag_len; @@ -332,5 +343,5 @@ cleanup_connection: dprintf("%s : complete, read %llu bytes\n", __func__, offset); - return rc == 0 ? offset : rc; + return rc == 0 ? (long)offset : rc; } diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h index 4bf47d078..db2cf6f11 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h @@ -26,7 +26,7 @@ typedef struct { int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf, int buf_size); void virtio_9p_shutdown(struct virtio_device *dev); -int virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer); +long virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer); #endif /* VIRTIO_9P_H_ */ diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c index 826f2ea0e..07ec1048f 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c @@ -13,10 +13,15 @@ #include <stdio.h> #include <cpu.h> #include <helpers.h> +#include <byteorder.h> #include "virtio.h" #include "virtio-blk.h" +#include "virtio-internal.h" #define DEFAULT_SECTOR_SIZE 512 +#define DRIVER_FEATURE_SUPPORT (VIRTIO_BLK_F_BLK_SIZE | VIRTIO_F_VERSION_1) + +static struct vqs vq; /** * Initialize virtio-block device. @@ -27,39 +32,54 @@ virtioblk_init(struct virtio_device *dev) { struct vring_avail *vq_avail; int blk_size = DEFAULT_SECTOR_SIZE; - int features; + uint64_t features; + int status = VIRTIO_STAT_ACKNOWLEDGE; /* Reset device */ - // XXX That will clear the virtq base. We need to move - // initializing it to here anyway - // - // virtio_reset_device(dev); + virtio_reset_device(dev); /* Acknowledge device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE); + virtio_set_status(dev, status); /* Tell HV that we know how to drive the device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER); + status |= VIRTIO_STAT_DRIVER; + virtio_set_status(dev, status); + + if (dev->is_modern) { + /* Negotiate features and sets FEATURES_OK if successful */ + if (virtio_negotiate_guest_features(dev, DRIVER_FEATURE_SUPPORT)) + goto dev_error; + + virtio_get_status(dev, &status); + } else { + /* Device specific setup - we support F_BLK_SIZE */ + virtio_set_guest_features(dev, VIRTIO_BLK_F_BLK_SIZE); + } - /* Device specific setup - we support F_BLK_SIZE */ - virtio_set_guest_features(dev, VIRTIO_BLK_F_BLK_SIZE); + if (virtio_queue_init_vq(dev, &vq, 0)) + goto dev_error; vq_avail = virtio_get_vring_avail(dev, 0); - vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT; + vq_avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT); vq_avail->idx = 0; /* Tell HV that setup succeeded */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER - |VIRTIO_STAT_DRIVER_OK); + status |= VIRTIO_STAT_DRIVER_OK; + virtio_set_status(dev, status); - virtio_get_host_features(dev, &features); + features = virtio_get_host_features(dev); if (features & VIRTIO_BLK_F_BLK_SIZE) { blk_size = virtio_get_config(dev, - offset_of(struct virtio_blk_cfg, blk_size), - sizeof(blk_size)); + offset_of(struct virtio_blk_cfg, blk_size), + sizeof(blk_size)); } return blk_size; +dev_error: + printf("%s: failed\n", __func__); + status |= VIRTIO_STAT_FAILED; + virtio_set_status(dev, status); + return 0; } @@ -77,6 +97,19 @@ virtioblk_shutdown(struct virtio_device *dev) virtio_reset_device(dev); } +static void fill_blk_hdr(struct virtio_blk_req *blkhdr, bool is_modern, + uint32_t type, uint32_t ioprio, uint32_t sector) +{ + if (is_modern) { + blkhdr->type = cpu_to_le32(type); + blkhdr->ioprio = cpu_to_le32(ioprio); + blkhdr->sector = cpu_to_le64(sector); + } else { + blkhdr->type = type; + blkhdr->ioprio = ioprio; + blkhdr->sector = sector; + } +} /** * Read blocks @@ -87,7 +120,7 @@ virtioblk_shutdown(struct virtio_device *dev) * @return number of blocks that have been read successfully */ int -virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt) +virtioblk_read(struct virtio_device *dev, char *buf, uint64_t blocknum, long cnt) { struct vring_desc *desc; int id; @@ -100,7 +133,7 @@ virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt) struct vring_used *vq_used; /* "Used" vring */ volatile uint8_t status = -1; volatile uint16_t *current_used_idx; - uint16_t last_used_idx; + uint16_t last_used_idx, avail_idx; int blk_size = DEFAULT_SECTOR_SIZE; //printf("virtioblk_read: dev=%p buf=%p blocknum=%li count=%li\n", @@ -128,41 +161,38 @@ virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt) vq_avail = virtio_get_vring_avail(dev, 0); vq_used = virtio_get_vring_used(dev, 0); + avail_idx = virtio_modern16_to_cpu(dev, vq_avail->idx); + last_used_idx = vq_used->idx; current_used_idx = &vq_used->idx; /* Set up header */ - blkhdr.type = VIRTIO_BLK_T_IN | VIRTIO_BLK_T_BARRIER; - blkhdr.ioprio = 1; - blkhdr.sector = blocknum * blk_size / DEFAULT_SECTOR_SIZE; + fill_blk_hdr(&blkhdr, dev->is_modern, VIRTIO_BLK_T_IN | VIRTIO_BLK_T_BARRIER, + 1, blocknum * blk_size / DEFAULT_SECTOR_SIZE); /* Determine descriptor index */ - id = (vq_avail->idx * 3) % vq_size; + id = (avail_idx * 3) % vq_size; /* Set up virtqueue descriptor for header */ desc = &vq_desc[id]; - desc->addr = (uint64_t)&blkhdr; - desc->len = sizeof(struct virtio_blk_req); - desc->flags = VRING_DESC_F_NEXT; - desc->next = (id + 1) % vq_size; + virtio_fill_desc(desc, dev->is_modern, (uint64_t)&blkhdr, + sizeof(struct virtio_blk_req), + VRING_DESC_F_NEXT, (id + 1) % vq_size); /* Set up virtqueue descriptor for data */ desc = &vq_desc[(id + 1) % vq_size]; - desc->addr = (uint64_t)buf; - desc->len = cnt * blk_size; - desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; - desc->next = (id + 2) % vq_size; + virtio_fill_desc(desc, dev->is_modern, (uint64_t)buf, cnt * blk_size, + VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, + (id + 2) % vq_size); /* Set up virtqueue descriptor for status */ desc = &vq_desc[(id + 2) % vq_size]; - desc->addr = (uint64_t)&status; - desc->len = 1; - desc->flags = VRING_DESC_F_WRITE; - desc->next = 0; + virtio_fill_desc(desc, dev->is_modern, (uint64_t)&status, 1, + VRING_DESC_F_WRITE, 0); - vq_avail->ring[vq_avail->idx % vq_size] = id; + vq_avail->ring[avail_idx % vq_size] = virtio_cpu_to_modern16 (dev, id); mb(); - vq_avail->idx += 1; + vq_avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1); /* Tell HV that the queue is ready */ virtio_queue_notify(dev, 0); diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h index ac8bf2896..2e7b5926b 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h @@ -55,6 +55,6 @@ struct virtio_blk_req { extern int virtioblk_init(struct virtio_device *dev); extern void virtioblk_shutdown(struct virtio_device *dev); -extern int virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt); +extern int virtioblk_read(struct virtio_device *dev, char *buf, uint64_t blocknum, long cnt); #endif /* _VIRTIO_BLK_H */ diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-internal.h b/qemu/roms/SLOF/lib/libvirtio/virtio-internal.h new file mode 100644 index 000000000..08662eab7 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-internal.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * Copyright (c) 2016 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _LIBVIRTIO_INTERNAL_H +#define _LIBVIRTIO_INTERNAL_H + +#include <byteorder.h> + +static inline uint16_t virtio_cpu_to_modern16(struct virtio_device *dev, uint16_t val) +{ + return dev->is_modern ? cpu_to_le16(val) : val; +} + +static inline uint32_t virtio_cpu_to_modern32(struct virtio_device *dev, uint32_t val) +{ + return dev->is_modern ? cpu_to_le32(val) : val; +} + +static inline uint64_t virtio_cpu_to_modern64(struct virtio_device *dev, uint64_t val) +{ + return dev->is_modern ? cpu_to_le64(val) : val; +} + +static inline uint16_t virtio_modern16_to_cpu(struct virtio_device *dev, uint16_t val) +{ + return dev->is_modern ? le16_to_cpu(val) : val; +} + +static inline uint32_t virtio_modern32_to_cpu(struct virtio_device *dev, uint32_t val) +{ + return dev->is_modern ? le32_to_cpu(val) : val; +} + +static inline uint64_t virtio_modern64_to_cpu(struct virtio_device *dev, uint64_t val) +{ + return dev->is_modern ? le64_to_cpu(val) : val; +} + +#endif /* _LIBVIRTIO_INTERNAL_H */ diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-net.c b/qemu/roms/SLOF/lib/libvirtio/virtio-net.c index 99c19d952..fc620a201 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-net.c +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-net.c @@ -26,6 +26,7 @@ #include <byteorder.h> #include "virtio.h" #include "virtio-net.h" +#include "virtio-internal.h" #undef DEBUG //#define DEBUG @@ -37,22 +38,13 @@ #define sync() asm volatile (" sync \n" ::: "memory") -/* PCI virtio header offsets */ -#define VIRTIOHDR_DEVICE_FEATURES 0 -#define VIRTIOHDR_GUEST_FEATURES 4 -#define VIRTIOHDR_QUEUE_ADDRESS 8 -#define VIRTIOHDR_QUEUE_SIZE 12 -#define VIRTIOHDR_QUEUE_SELECT 14 -#define VIRTIOHDR_QUEUE_NOTIFY 16 -#define VIRTIOHDR_DEVICE_STATUS 18 -#define VIRTIOHDR_ISR_STATUS 19 -#define VIRTIOHDR_DEVICE_CONFIG 20 -#define VIRTIOHDR_MAC_ADDRESS 20 +#define DRIVER_FEATURE_SUPPORT (VIRTIO_NET_F_MAC | VIRTIO_F_VERSION_1) struct virtio_device virtiodev; -struct vqs vq[2]; /* Information about virtqueues */ +static struct vqs vq_rx; /* Information about receive virtqueues */ +static struct vqs vq_tx; /* Information about transmit virtqueues */ -/* See Virtio Spec, appendix C, "Device Operation" */ +/* See Virtio Spec, appendix C, "Device Operation" */ struct virtio_net_hdr { uint8_t flags; uint8_t gso_type; @@ -63,6 +55,18 @@ struct virtio_net_hdr { // uint16_t num_buffers; /* Only if VIRTIO_NET_F_MRG_RXBUF */ }; +static unsigned int net_hdr_size; + +struct virtio_net_hdr_v1 { + uint8_t flags; + uint8_t gso_type; + le16 hdr_len; + le16 gso_size; + le16 csum_start; + le16 csum_offset; + le16 num_buffers; +}; + static uint16_t last_rx_idx; /* Last index in RX "used" ring */ /** @@ -72,15 +76,13 @@ static uint16_t last_rx_idx; /* Last index in RX "used" ring */ */ static int virtionet_init_pci(struct virtio_device *dev) { - int i; - dprintf("virtionet: doing virtionet_init_pci!\n"); if (!dev) return -1; - virtiodev.base = dev->base; - virtiodev.type = dev->type; + /* make a copy of the device structure */ + memcpy(&virtiodev, dev, sizeof(struct virtio_device)); /* Reset device */ virtio_reset_device(&virtiodev); @@ -90,29 +92,11 @@ static int virtionet_init_pci(struct virtio_device *dev) * second the transmit queue, and the forth is the control queue for * networking options. * We are only interested in the receive and transmit queue here. */ - - for (i=VQ_RX; i<=VQ_TX; i++) { - /* Select ring (0=RX, 1=TX): */ - vq[i].id = i-VQ_RX; - ci_write_16(virtiodev.base+VIRTIOHDR_QUEUE_SELECT, - cpu_to_le16(vq[i].id)); - - vq[i].size = le16_to_cpu(ci_read_16(virtiodev.base+VIRTIOHDR_QUEUE_SIZE)); - vq[i].desc = SLOF_alloc_mem_aligned(virtio_vring_size(vq[i].size), 4096); - if (!vq[i].desc) { - printf("memory allocation failed!\n"); - return -1; - } - memset(vq[i].desc, 0, virtio_vring_size(vq[i].size)); - ci_write_32(virtiodev.base+VIRTIOHDR_QUEUE_ADDRESS, - cpu_to_le32((long)vq[i].desc / 4096)); - vq[i].avail = (void*)vq[i].desc - + vq[i].size * sizeof(struct vring_desc); - vq[i].used = (void*)VQ_ALIGN((long)vq[i].avail - + vq[i].size * sizeof(struct vring_avail)); - - dprintf("%i: vq.id = %llx\nvq.size =%x\n vq.avail =%p\nvq.used=%p\n", - i, vq[i].id, vq[i].size, vq[i].avail, vq[i].used); + if (virtio_queue_init_vq(dev, &vq_rx, VQ_RX) || + virtio_queue_init_vq(dev, &vq_tx, VQ_TX)) { + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER + |VIRTIO_STAT_FAILED); + return -1; } /* Acknowledge device. */ @@ -129,6 +113,7 @@ static int virtionet_init_pci(struct virtio_device *dev) static int virtionet_init(net_driver_t *driver) { int i; + int status = VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER; dprintf("virtionet_init(%02x:%02x:%02x:%02x:%02x:%02x)\n", driver->mac_addr[0], driver->mac_addr[1], @@ -139,60 +124,69 @@ static int virtionet_init(net_driver_t *driver) return 0; /* Tell HV that we know how to drive the device. */ - virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER); - - /* Device specific setup - we do not support special features right now */ - virtio_set_guest_features(&virtiodev, 0); + virtio_set_status(&virtiodev, status); + + /* Device specific setup */ + if (virtiodev.is_modern) { + if (virtio_negotiate_guest_features(&virtiodev, DRIVER_FEATURE_SUPPORT)) + goto dev_error; + net_hdr_size = sizeof(struct virtio_net_hdr_v1); + virtio_get_status(&virtiodev, &status); + } else { + net_hdr_size = sizeof(struct virtio_net_hdr); + virtio_set_guest_features(&virtiodev, 0); + } /* Allocate memory for one transmit an multiple receive buffers */ - vq[VQ_RX].buf_mem = SLOF_alloc_mem((BUFFER_ENTRY_SIZE+sizeof(struct virtio_net_hdr)) + vq_rx.buf_mem = SLOF_alloc_mem((BUFFER_ENTRY_SIZE+net_hdr_size) * RX_QUEUE_SIZE); - if (!vq[VQ_RX].buf_mem) { + if (!vq_rx.buf_mem) { printf("virtionet: Failed to allocate buffers!\n"); - virtio_set_status(&virtiodev, VIRTIO_STAT_FAILED); - return -1; + goto dev_error; } /* Prepare receive buffer queue */ for (i = 0; i < RX_QUEUE_SIZE; i++) { - struct vring_desc *desc; + uint64_t addr = (uint64_t)vq_rx.buf_mem + + i * (BUFFER_ENTRY_SIZE+net_hdr_size); + uint32_t id = i*2; /* Descriptor for net_hdr: */ - desc = &vq[VQ_RX].desc[i*2]; - desc->addr = (uint64_t)vq[VQ_RX].buf_mem - + i * (BUFFER_ENTRY_SIZE+sizeof(struct virtio_net_hdr)); - desc->len = sizeof(struct virtio_net_hdr); - desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; - desc->next = i*2+1; + virtio_fill_desc(&vq_rx.desc[id], virtiodev.is_modern, addr, net_hdr_size, + VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, id + 1); /* Descriptor for data: */ - desc = &vq[VQ_RX].desc[i*2+1]; - desc->addr = vq[VQ_RX].desc[i*2].addr + sizeof(struct virtio_net_hdr); - desc->len = BUFFER_ENTRY_SIZE; - desc->flags = VRING_DESC_F_WRITE; - desc->next = 0; + virtio_fill_desc(&vq_rx.desc[id+1], virtiodev.is_modern, addr + net_hdr_size, + BUFFER_ENTRY_SIZE, VRING_DESC_F_WRITE, 0); - vq[VQ_RX].avail->ring[i] = i*2; + vq_rx.avail->ring[i] = virtio_cpu_to_modern16(&virtiodev, id); } sync(); - vq[VQ_RX].avail->flags = VRING_AVAIL_F_NO_INTERRUPT; - vq[VQ_RX].avail->idx = RX_QUEUE_SIZE; - last_rx_idx = vq[VQ_RX].used->idx; + vq_rx.avail->flags = virtio_cpu_to_modern16(&virtiodev, VRING_AVAIL_F_NO_INTERRUPT); + vq_rx.avail->idx = virtio_cpu_to_modern16(&virtiodev, RX_QUEUE_SIZE); + + last_rx_idx = virtio_modern16_to_cpu(&virtiodev, vq_rx.used->idx); - vq[VQ_TX].avail->flags = VRING_AVAIL_F_NO_INTERRUPT; - vq[VQ_TX].avail->idx = 0; + vq_tx.avail->flags = virtio_cpu_to_modern16(&virtiodev, VRING_AVAIL_F_NO_INTERRUPT); + vq_tx.avail->idx = 0; /* Tell HV that setup succeeded */ - virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE - |VIRTIO_STAT_DRIVER - |VIRTIO_STAT_DRIVER_OK); + status |= VIRTIO_STAT_DRIVER_OK; + virtio_set_status(&virtiodev, status); /* Tell HV that RX queues are ready */ virtio_queue_notify(&virtiodev, VQ_RX); driver->running = 1; - + for(i = 0; i < (int)sizeof(driver->mac_addr); i++) { + driver->mac_addr[i] = virtio_get_config(&virtiodev, i, 1); + } return 0; + +dev_error: + status |= VIRTIO_STAT_FAILED; + virtio_set_status(&virtiodev, status); + return -1; } @@ -225,9 +219,10 @@ static int virtionet_term(net_driver_t *driver) */ static int virtionet_xmit(char *buf, int len) { - struct vring_desc *desc; - int id; - static struct virtio_net_hdr nethdr; + int id, idx; + static struct virtio_net_hdr_v1 nethdr_v1; + static struct virtio_net_hdr nethdr_legacy; + void *nethdr = &nethdr_legacy; if (len > BUFFER_ENTRY_SIZE) { printf("virtionet: Packet too big!\n"); @@ -236,28 +231,25 @@ static int virtionet_xmit(char *buf, int len) dprintf("\nvirtionet_xmit(packet at %p, %d bytes)\n", buf, len); - memset(&nethdr, 0, sizeof(nethdr)); + if (virtiodev.is_modern) + nethdr = &nethdr_v1; + + memset(nethdr, 0, net_hdr_size); /* Determine descriptor index */ - id = (vq[VQ_TX].avail->idx * 2) % vq[VQ_TX].size; + idx = virtio_modern16_to_cpu(&virtiodev, vq_tx.avail->idx); + id = (idx * 2) % vq_tx.size; /* Set up virtqueue descriptor for header */ - desc = &vq[VQ_TX].desc[id]; - desc->addr = (uint64_t)&nethdr; - desc->len = sizeof(struct virtio_net_hdr); - desc->flags = VRING_DESC_F_NEXT; - desc->next = id + 1; + virtio_fill_desc(&vq_tx.desc[id], virtiodev.is_modern, (uint64_t)nethdr, + net_hdr_size, VRING_DESC_F_NEXT, id + 1); /* Set up virtqueue descriptor for data */ - desc = &vq[VQ_TX].desc[id+1]; - desc->addr = (uint64_t)buf; - desc->len = len; - desc->flags = 0; - desc->next = 0; + virtio_fill_desc(&vq_tx.desc[id+1], virtiodev.is_modern, (uint64_t)buf, len, 0, 0); - vq[VQ_TX].avail->ring[vq[VQ_TX].avail->idx % vq[VQ_TX].size] = id; + vq_tx.avail->ring[idx % vq_tx.size] = virtio_cpu_to_modern16(&virtiodev, id); sync(); - vq[VQ_TX].avail->idx += 1; + vq_tx.avail->idx = virtio_cpu_to_modern16(&virtiodev, idx + 1); sync(); /* Tell HV that TX queue is ready */ @@ -272,23 +264,24 @@ static int virtionet_xmit(char *buf, int len) */ static int virtionet_receive(char *buf, int maxlen) { - int len = 0; - int id; + uint32_t len = 0; + uint32_t id, idx; - if (last_rx_idx == vq[VQ_RX].used->idx) { + idx = virtio_modern16_to_cpu(&virtiodev, vq_rx.used->idx); + + if (last_rx_idx == idx) { /* Nothing received yet */ return 0; } - id = (vq[VQ_RX].used->ring[last_rx_idx % vq[VQ_RX].size].id + 1) - % vq[VQ_RX].size; - len = vq[VQ_RX].used->ring[last_rx_idx % vq[VQ_RX].size].len - - sizeof(struct virtio_net_hdr); - - dprintf("virtionet_receive() last_rx_idx=%i, vq[VQ_RX].used->idx=%i," - " id=%i len=%i\n", last_rx_idx, vq[VQ_RX].used->idx, id, len); + id = (virtio_modern32_to_cpu(&virtiodev, vq_rx.used->ring[last_rx_idx % vq_rx.size].id) + 1) + % vq_rx.size; + len = virtio_modern32_to_cpu(&virtiodev, vq_rx.used->ring[last_rx_idx % vq_rx.size].len) + - net_hdr_size; + dprintf("virtionet_receive() last_rx_idx=%i, vq_rx.used->idx=%i," + " id=%i len=%i\n", last_rx_idx, vq_rx.used->idx, id, len); - if (len > maxlen) { + if (len > (uint32_t)maxlen) { printf("virtio-net: Receive buffer not big enough!\n"); len = maxlen; } @@ -298,7 +291,7 @@ static int virtionet_receive(char *buf, int maxlen) printf("\n"); int i; for (i=0; i<64; i++) { - printf(" %02x", *(uint8_t*)(vq[VQ_RX].desc[id].addr+i)); + printf(" %02x", *(uint8_t*)(vq_rx.desc[id].addr+i)); if ((i%16)==15) printf("\n"); } @@ -306,14 +299,14 @@ static int virtionet_receive(char *buf, int maxlen) #endif /* Copy data to destination buffer */ - memcpy(buf, (void*)vq[VQ_RX].desc[id].addr, len); + memcpy(buf, (void *)virtio_modern64_to_cpu(&virtiodev, vq_rx.desc[id].addr), len); /* Move indices to next entries */ last_rx_idx = last_rx_idx + 1; - vq[VQ_RX].avail->ring[vq[VQ_RX].avail->idx % vq[VQ_RX].size] = id - 1; + vq_rx.avail->ring[idx % vq_rx.size] = virtio_cpu_to_modern16(&virtiodev, id - 1); sync(); - vq[VQ_RX].avail->idx += 1; + vq_rx.avail->idx = virtio_cpu_to_modern16(&virtiodev, idx + 1); /* Tell HV that RX queue entry is ready */ virtio_queue_notify(&virtiodev, VQ_RX); @@ -321,7 +314,7 @@ static int virtionet_receive(char *buf, int maxlen) return len; } -net_driver_t *virtionet_open(char *mac_addr, int len, struct virtio_device *dev) +net_driver_t *virtionet_open(struct virtio_device *dev) { net_driver_t *driver; @@ -331,7 +324,6 @@ net_driver_t *virtionet_open(char *mac_addr, int len, struct virtio_device *dev) return NULL; } - memcpy(driver->mac_addr, mac_addr, 6); driver->running = 0; if (virtionet_init_pci(dev)) diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-net.h b/qemu/roms/SLOF/lib/libvirtio/virtio-net.h index bc7a189f7..c2d8ee336 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-net.h +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-net.h @@ -23,19 +23,10 @@ enum { VQ_TX = 1, /* Transmit Queue */ }; -struct vqs { - uint64_t id; /* Queue ID */ - uint32_t size; - void *buf_mem; - struct vring_desc *desc; - struct vring_avail *avail; - struct vring_used *used; -}; - -/* Device is identified by RX queue ID: */ -#define DEVICE_ID vq[0].id +/* VIRTIO_NET Feature bits */ +#define VIRTIO_NET_F_MAC (1 << 5) -extern net_driver_t *virtionet_open(char *mac_addr, int len, struct virtio_device *dev); +extern net_driver_t *virtionet_open(struct virtio_device *dev); extern void virtionet_close(net_driver_t *driver); extern int virtionet_read(char *buf, int len); extern int virtionet_write(char *buf, int len); diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c b/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c index 48289289a..04181b06c 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c @@ -15,6 +15,7 @@ #include <cpu.h> #include <helpers.h> #include "virtio.h" +#include "virtio-internal.h" #include "virtio-scsi.h" int virtioscsi_send(struct virtio_device *dev, @@ -22,58 +23,54 @@ int virtioscsi_send(struct virtio_device *dev, struct virtio_scsi_resp_cmd *resp, int is_read, void *buf, uint64_t buf_len) { - struct vring_desc *desc; - struct vring_desc *vq_desc; /* Descriptor vring */ - struct vring_avail *vq_avail; /* "Available" vring */ - struct vring_used *vq_used; /* "Used" vring */ - - volatile uint16_t *current_used_idx; - uint16_t last_used_idx; - int id; - uint32_t vq_size, time; - - int vq = VIRTIO_SCSI_REQUEST_VQ; - - vq_size = virtio_get_qsize(dev, vq); - vq_desc = virtio_get_vring_desc(dev, vq); - vq_avail = virtio_get_vring_avail(dev, vq); - vq_used = virtio_get_vring_used(dev, vq); - - last_used_idx = vq_used->idx; - current_used_idx = &vq_used->idx; - - /* Determine descriptor index */ - id = (vq_avail->idx * 3) % vq_size; - - desc = &vq_desc[id]; - desc->addr = (uint64_t)req; - desc->len = sizeof(*req); - desc->flags = VRING_DESC_F_NEXT; - desc->next = (id + 1) % vq_size; - - /* Set up virtqueue descriptor for data */ - desc = &vq_desc[(id + 1) % vq_size]; - desc->addr = (uint64_t)resp; - desc->len = sizeof(*resp); - desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; - desc->next = (id + 2) % vq_size; - - if (buf && buf_len) { - /* Set up virtqueue descriptor for status */ - desc = &vq_desc[(id + 2) % vq_size]; - desc->addr = (uint64_t)buf; - desc->len = buf_len; - desc->flags = is_read ? VRING_DESC_F_WRITE : 0; - desc->next = 0; - } else - desc->flags &= ~VRING_DESC_F_NEXT; - - vq_avail->ring[vq_avail->idx % vq_size] = id; - mb(); - vq_avail->idx += 1; - - /* Tell HV that the vq is ready */ - virtio_queue_notify(dev, vq); + struct vring_desc *vq_desc; /* Descriptor vring */ + struct vring_avail *vq_avail; /* "Available" vring */ + struct vring_used *vq_used; /* "Used" vring */ + + volatile uint16_t *current_used_idx; + uint16_t last_used_idx, avail_idx; + int id; + uint32_t vq_size, time; + + int vq = VIRTIO_SCSI_REQUEST_VQ; + + vq_size = virtio_get_qsize(dev, vq); + vq_desc = virtio_get_vring_desc(dev, vq); + vq_avail = virtio_get_vring_avail(dev, vq); + vq_used = virtio_get_vring_used(dev, vq); + + avail_idx = virtio_modern16_to_cpu(dev, vq_avail->idx); + + last_used_idx = vq_used->idx; + current_used_idx = &vq_used->idx; + + /* Determine descriptor index */ + id = (avail_idx * 3) % vq_size; + virtio_fill_desc(&vq_desc[id], dev->is_modern, (uint64_t)req, sizeof(*req), VRING_DESC_F_NEXT, + (id + 1) % vq_size); + + /* Set up virtqueue descriptor for data */ + if (buf && buf_len) { + virtio_fill_desc(&vq_desc[(id + 1) % vq_size], dev->is_modern, + (uint64_t)resp, sizeof(*resp), + VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, + (id + 2) % vq_size); + /* Set up virtqueue descriptor for status */ + virtio_fill_desc(&vq_desc[(id + 2) % vq_size], dev->is_modern, + (uint64_t)buf, buf_len, + (is_read ? VRING_DESC_F_WRITE : 0), 0); + } else { + virtio_fill_desc(&vq_desc[(id + 1) % vq_size], dev->is_modern, + (uint64_t)resp, sizeof(*resp), + VRING_DESC_F_WRITE, 0); + } + + vq_avail->ring[avail_idx % vq_size] = virtio_cpu_to_modern16(dev, id); + mb(); + vq_avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1); + + /* Tell HV that the vq is ready */ + virtio_queue_notify(dev, vq); /* Wait for host to consume the descriptor */ time = SLOF_GetTimer() + VIRTIO_TIMEOUT; @@ -84,7 +81,7 @@ int virtioscsi_send(struct virtio_device *dev, break; } - return 0; + return 0; } /** @@ -93,42 +90,55 @@ int virtioscsi_send(struct virtio_device *dev, */ int virtioscsi_init(struct virtio_device *dev) { - struct vring_avail *vq_avail; - unsigned int idx = 0; - int qsize = 0; - - /* Reset device */ - // XXX That will clear the virtq base. We need to move - // initializing it to here anyway - // - // virtio_reset_device(dev); - - /* Acknowledge device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE); + struct vring_avail *vq_avail; + unsigned int idx = 0; + int qsize = 0; + int status = VIRTIO_STAT_ACKNOWLEDGE; - /* Tell HV that we know how to drive the device. */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER); - - /* Device specific setup - we do not support special features right now */ - virtio_set_guest_features(dev, 0); + /* Reset device */ + // XXX That will clear the virtq base. We need to move + // initializing it to here anyway + // + // virtio_reset_device(dev); + + /* Acknowledge device. */ + virtio_set_status(dev, status); + + /* Tell HV that we know how to drive the device. */ + status |= VIRTIO_STAT_DRIVER; + virtio_set_status(dev, status); + + /* Device specific setup - we do not support special features right now */ + if (dev->is_modern) { + if (virtio_negotiate_guest_features(dev, VIRTIO_F_VERSION_1)) + goto dev_error; + virtio_get_status(dev, &status); + } else { + virtio_set_guest_features(dev, 0); + } - while(1) { - qsize = virtio_get_qsize(dev, idx); - if (!qsize) - break; - virtio_vring_size(qsize); + while(1) { + qsize = virtio_get_qsize(dev, idx); + if (!qsize) + break; + virtio_vring_size(qsize); - vq_avail = virtio_get_vring_avail(dev, 0); - vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT; - vq_avail->idx = 0; - idx++; - } + vq_avail = virtio_get_vring_avail(dev, idx); + vq_avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT); + vq_avail->idx = 0; + idx++; + } /* Tell HV that setup succeeded */ - virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER - |VIRTIO_STAT_DRIVER_OK); + status |= VIRTIO_STAT_DRIVER_OK; + virtio_set_status(dev, status); return 0; +dev_error: + printf("%s: failed\n", __func__); + status |= VIRTIO_STAT_FAILED; + virtio_set_status(dev, status); + return -1; } /** diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.c b/qemu/roms/SLOF/lib/libvirtio/virtio.c index f9c00a67a..f189941c7 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio.c +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.c @@ -10,10 +10,16 @@ * IBM Corporation - initial implementation *****************************************************************************/ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> #include <cpu.h> #include <cache.h> #include <byteorder.h> #include "virtio.h" +#include "helpers.h" /* PCI virtio header offsets */ #define VIRTIOHDR_DEVICE_FEATURES 0 @@ -26,6 +32,166 @@ #define VIRTIOHDR_ISR_STATUS 19 #define VIRTIOHDR_DEVICE_CONFIG 20 +/* PCI defines */ +#define PCI_BASE_ADDR_SPACE_IO 0x01 +#define PCI_BASE_ADDR_SPACE_64BIT 0x04 +#define PCI_BASE_ADDR_MEM_MASK (~0x0fUL) +#define PCI_BASE_ADDR_IO_MASK (~0x03UL) + +#define PCI_BASE_ADDR_REG_0 0x10 +#define PCI_CONFIG_CAP_REG 0x34 + +#define PCI_CAP_ID_VNDR 0x9 + +/* Common configuration */ +#define VIRTIO_PCI_CAP_COMMON_CFG 1 +/* Notifications */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 +/* ISR access */ +#define VIRTIO_PCI_CAP_ISR_CFG 3 +/* Device specific configuration */ +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 +/* PCI configuration access */ +#define VIRTIO_PCI_CAP_PCI_CFG 5 + +#define VIRTIO_PCI_CAP_VNDR 0 /* Generic PCI field: PCI_CAP_ID_VNDR */ +#define VIRTIO_PCI_CAP_NEXT 1 /* Generic PCI field: next ptr. */ +#define VIRTIO_PCI_CAP_LEN 2 /* Generic PCI field: capability length */ +#define VIRTIO_PCI_CAP_CFG_TYPE 3 /* Identifies the structure. */ +#define VIRTIO_PCI_CAP_BAR 4 /* Where to find it. */ +#define VIRTIO_PCI_CAP_OFFSET 8 /* Offset within bar. */ +#define VIRTIO_PCI_CAP_LENGTH 12 /* Length of the structure, in bytes. */ + +struct virtio_dev_common { + le32 dev_features_sel; + le32 dev_features; + le32 drv_features_sel; + le32 drv_features; + le16 msix_config; + le16 num_queues; + uint8_t dev_status; + uint8_t cfg_generation; + + le16 q_select; + le16 q_size; + le16 q_msix_vec; + le16 q_enable; + le16 q_notify_off; + le64 q_desc; + le64 q_avail; + le64 q_used; +} __attribute__ ((packed)); + +/* virtio 1.0 Spec: 4.1.3 PCI Device Layout + * + * Fields of different sizes are present in the device configuration regions. + * All 64-bit, 32-bit and 16-bit fields are little-endian. 64-bit fields are to + * be treated as two 32-bit fields, with low 32 bit part followed by the high 32 + * bit part. + */ +static void virtio_pci_write64(void *addr, uint64_t val) +{ + uint32_t hi = (val >> 32) & 0xFFFFFFFF; + uint32_t lo = val & 0xFFFFFFFF; + + ci_write_32(addr, cpu_to_le32(lo)); + ci_write_32(addr + 4, cpu_to_le32(hi)); +} + +static uint64_t virtio_pci_read64(void *addr) +{ + uint64_t hi, lo; + + lo = le32_to_cpu(ci_read_32(addr)); + hi = le32_to_cpu(ci_read_32(addr + 4)); + return (hi << 32) | lo; +} + +static void virtio_cap_set_base_addr(struct virtio_cap *cap, uint32_t offset) +{ + uint64_t addr; + + addr = SLOF_pci_config_read32(PCI_BASE_ADDR_REG_0 + 4 * cap->bar); + if (addr & PCI_BASE_ADDR_SPACE_IO) { + addr = addr & PCI_BASE_ADDR_IO_MASK; + cap->is_io = 1; + } else { + if (addr & PCI_BASE_ADDR_SPACE_64BIT) + addr |= SLOF_pci_config_read32(PCI_BASE_ADDR_REG_0 + 4 * (cap->bar + 1)) << 32; + addr = addr & PCI_BASE_ADDR_MEM_MASK; + cap->is_io = 0; + } + addr = (uint64_t)SLOF_translate_my_address((void *)addr); + cap->addr = (void *)addr + offset; +} + +static void virtio_process_cap(struct virtio_device *dev, uint8_t cap_ptr) +{ + struct virtio_cap *cap; + uint8_t cfg_type, bar; + uint32_t offset; + + cfg_type = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_CFG_TYPE); + bar = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_BAR); + offset = SLOF_pci_config_read32(cap_ptr + VIRTIO_PCI_CAP_OFFSET); + + switch(cfg_type) { + case VIRTIO_PCI_CAP_COMMON_CFG: + cap = &dev->common; + break; + case VIRTIO_PCI_CAP_NOTIFY_CFG: + cap = &dev->notify; + dev->notify_off_mul = SLOF_pci_config_read32(cap_ptr + sizeof(struct virtio_cap)); + break; + case VIRTIO_PCI_CAP_ISR_CFG: + cap = &dev->isr; + break; + case VIRTIO_PCI_CAP_DEVICE_CFG: + cap = &dev->device; + break; + default: + return; + } + + cap->bar = bar; + virtio_cap_set_base_addr(cap, offset); + cap->cap_id = cfg_type; +} + +/** + * Reads the virtio device capabilities, gets called from SLOF routines The + * function determines legacy or modern device and sets up driver registers + */ +struct virtio_device *virtio_setup_vd(void) +{ + uint8_t cap_ptr, cap_vndr; + struct virtio_device *dev; + + dev = SLOF_alloc_mem(sizeof(struct virtio_device)); + if (!dev) { + printf("Failed to allocate memory"); + return NULL; + } + + cap_ptr = SLOF_pci_config_read8(PCI_CONFIG_CAP_REG); + while (cap_ptr != 0) { + cap_vndr = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_VNDR); + if (cap_vndr == PCI_CAP_ID_VNDR) + virtio_process_cap(dev, cap_ptr); + cap_ptr = SLOF_pci_config_read8(cap_ptr+VIRTIO_PCI_CAP_NEXT); + } + + if (dev->common.cap_id && dev->notify.cap_id && + dev->isr.cap_id && dev->device.cap_id) { + dev->is_modern = 1; + } else { + dev->is_modern = 0; + dev->legacy.cap_id = 0; + dev->legacy.bar = 0; + virtio_cap_set_base_addr(&dev->legacy, 0); + } + return dev; +} /** * Calculate ring size according to queue size number @@ -33,9 +199,9 @@ unsigned long virtio_vring_size(unsigned int qsize) { return VQ_ALIGN(sizeof(struct vring_desc) * qsize + - sizeof(struct vring_avail) + sizeof(uint16_t) * qsize) + - VQ_ALIGN(sizeof(struct vring_used) + - sizeof(struct vring_used_elem) * qsize); + sizeof(struct vring_avail) + sizeof(uint16_t) * qsize) + + VQ_ALIGN(sizeof(struct vring_used) + + sizeof(struct vring_used_elem) * qsize); } @@ -45,15 +211,22 @@ unsigned long virtio_vring_size(unsigned int qsize) * @param queue virtio queue number * @return number of elements */ -int virtio_get_qsize(struct virtio_device *dev, int queue) +unsigned int virtio_get_qsize(struct virtio_device *dev, int queue) { - int size = 0; + unsigned int size = 0; - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, + if (dev->is_modern) { + void *addr = dev->common.addr + offset_of(struct virtio_dev_common, q_select); + ci_write_16(addr, cpu_to_le16(queue)); + eieio(); + addr = dev->common.addr + offset_of(struct virtio_dev_common, q_size); + size = le16_to_cpu(ci_read_16(addr)); + } + else { + ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SELECT, cpu_to_le16(queue)); eieio(); - size = le16_to_cpu(ci_read_16(dev->base+VIRTIOHDR_QUEUE_SIZE)); + size = le16_to_cpu(ci_read_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SIZE)); } return size; @@ -70,12 +243,19 @@ struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue) { struct vring_desc *desc = 0; - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, + if (dev->is_modern) { + void *q_sel = dev->common.addr + offset_of(struct virtio_dev_common, q_select); + void *q_desc = dev->common.addr + offset_of(struct virtio_dev_common, q_desc); + + ci_write_16(q_sel, cpu_to_le16(queue)); + eieio(); + desc = (void *)(virtio_pci_read64(q_desc)); + } else { + ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SELECT, cpu_to_le16(queue)); eieio(); desc = (void*)(4096L * - le32_to_cpu(ci_read_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS))); + le32_to_cpu(ci_read_32(dev->legacy.addr+VIRTIOHDR_QUEUE_ADDRESS))); } return desc; @@ -90,8 +270,18 @@ struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue) */ struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue) { - return (void*)((uint64_t)virtio_get_vring_desc(dev, queue) - + virtio_get_qsize(dev, queue) * sizeof(struct vring_desc)); + if (dev->is_modern) { + void *q_sel = dev->common.addr + offset_of(struct virtio_dev_common, q_select); + void *q_avail = dev->common.addr + offset_of(struct virtio_dev_common, q_avail); + + ci_write_16(q_sel, cpu_to_le16(queue)); + eieio(); + return (void *)(virtio_pci_read64(q_avail)); + } + else { + return (void*)((uint64_t)virtio_get_vring_desc(dev, queue) + + virtio_get_qsize(dev, queue) * sizeof(struct vring_desc)); + } } @@ -103,20 +293,46 @@ struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue) */ struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue) { - return (void*)VQ_ALIGN((uint64_t)virtio_get_vring_avail(dev, queue) - + virtio_get_qsize(dev, queue) - * sizeof(struct vring_avail)); + if (dev->is_modern) { + void *q_sel = dev->common.addr + offset_of(struct virtio_dev_common, q_select); + void *q_used = dev->common.addr + offset_of(struct virtio_dev_common, q_used); + + ci_write_16(q_sel, cpu_to_le16(queue)); + eieio(); + return (void *)(virtio_pci_read64(q_used)); + } else { + return (void*)VQ_ALIGN((uint64_t)virtio_get_vring_avail(dev, queue) + + virtio_get_qsize(dev, queue) + * sizeof(struct vring_avail)); + } } +/** + * Fill the virtio ring descriptor depending on the legacy mode or virtio 1.0 + */ +void virtio_fill_desc(struct vring_desc *desc, bool is_modern, + uint64_t addr, uint32_t len, + uint16_t flags, uint16_t next) +{ + if (is_modern) { + desc->addr = cpu_to_le64(addr); + desc->len = cpu_to_le32(len); + desc->flags = cpu_to_le16(flags); + desc->next = cpu_to_le16(next); + } else { + desc->addr = addr; + desc->len = len; + desc->flags = flags; + desc->next = next; + } +} /** * Reset virtio device */ void virtio_reset_device(struct virtio_device *dev) { - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, 0); - } + virtio_set_status(dev, 0); } @@ -125,25 +341,64 @@ void virtio_reset_device(struct virtio_device *dev) */ void virtio_queue_notify(struct virtio_device *dev, int queue) { - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_16(dev->base+VIRTIOHDR_QUEUE_NOTIFY, cpu_to_le16(queue)); + if (dev->is_modern) { + void *q_sel = dev->common.addr + offset_of(struct virtio_dev_common, q_select); + void *q_ntfy = dev->common.addr + offset_of(struct virtio_dev_common, q_notify_off); + void *addr; + uint16_t q_notify_off; + + ci_write_16(q_sel, cpu_to_le16(queue)); + eieio(); + q_notify_off = le16_to_cpu(ci_read_16(q_ntfy)); + addr = dev->notify.addr + q_notify_off * dev->notify_off_mul; + ci_write_16(addr, cpu_to_le16(queue)); + } else { + ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_NOTIFY, cpu_to_le16(queue)); } } /** * Set queue address */ -void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned int qaddr) +void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned long qaddr) +{ + if (dev->is_modern) { + uint64_t q_desc = qaddr; + uint64_t q_avail; + uint64_t q_used; + uint32_t q_size = virtio_get_qsize(dev, queue); + + virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_desc), q_desc); + q_avail = q_desc + q_size * sizeof(struct vring_desc); + virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_avail), q_avail); + q_used = VQ_ALIGN(q_avail + sizeof(struct vring_avail) + sizeof(uint16_t) * q_size); + virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_used), q_used); + ci_write_16(dev->common.addr + offset_of(struct virtio_dev_common, q_enable), cpu_to_le16(1)); + } else { + uint32_t val = qaddr; + val = val >> 12; + ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SELECT, + cpu_to_le16(queue)); + eieio(); + ci_write_32(dev->legacy.addr+VIRTIOHDR_QUEUE_ADDRESS, + cpu_to_le32(val)); + } +} + +int virtio_queue_init_vq(struct virtio_device *dev, struct vqs *vq, unsigned int id) { - if (dev->type == VIRTIO_TYPE_PCI) { - uint32_t val = qaddr; - val = val >> 12; - ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, - cpu_to_le16(queue)); - eieio(); - ci_write_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS, - cpu_to_le32(val)); - } + vq->size = virtio_get_qsize(dev, id); + vq->desc = SLOF_alloc_mem_aligned(virtio_vring_size(vq->size), 4096); + if (!vq->desc) { + printf("memory allocation failed!\n"); + return -1; + } + memset(vq->desc, 0, virtio_vring_size(vq->size)); + virtio_set_qaddr(dev, id, (unsigned long)vq->desc); + vq->avail = virtio_get_vring_avail(dev, id); + vq->used = virtio_get_vring_used(dev, id); + vq->id = id; + return 0; } /** @@ -151,34 +406,109 @@ void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned int qaddr) */ void virtio_set_status(struct virtio_device *dev, int status) { - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, status); + if (dev->is_modern) { + ci_write_8(dev->common.addr + + offset_of(struct virtio_dev_common, dev_status), status); + } else { + ci_write_8(dev->legacy.addr+VIRTIOHDR_DEVICE_STATUS, status); } } +/** + * Get device status bits + */ +void virtio_get_status(struct virtio_device *dev, int *status) +{ + if (dev->is_modern) { + *status = ci_read_8(dev->common.addr + + offset_of(struct virtio_dev_common, dev_status)); + } else { + *status = ci_read_8(dev->legacy.addr+VIRTIOHDR_DEVICE_STATUS); + } +} /** * Set guest feature bits */ -void virtio_set_guest_features(struct virtio_device *dev, int features) +void virtio_set_guest_features(struct virtio_device *dev, uint64_t features) { - if (dev->type == VIRTIO_TYPE_PCI) { - ci_write_32(dev->base+VIRTIOHDR_GUEST_FEATURES, bswap_32(features)); + if (dev->is_modern) { + uint32_t f1 = (features >> 32) & 0xFFFFFFFF; + uint32_t f0 = features & 0xFFFFFFFF; + void *addr = dev->common.addr; + + ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features_sel), + cpu_to_le32(1)); + ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features), + cpu_to_le32(f1)); + + ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features_sel), + cpu_to_le32(0)); + ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features), + cpu_to_le32(f0)); + } else { + ci_write_32(dev->legacy.addr+VIRTIOHDR_GUEST_FEATURES, cpu_to_le32(features)); } } /** * Get host feature bits */ -void virtio_get_host_features(struct virtio_device *dev, int *features) +uint64_t virtio_get_host_features(struct virtio_device *dev) { - if (dev->type == VIRTIO_TYPE_PCI && features) { - *features = bswap_32(ci_read_32(dev->base+VIRTIOHDR_DEVICE_FEATURES)); + uint64_t features = 0; + if (dev->is_modern) { + uint32_t f0 = 0, f1 = 0; + void *addr = dev->common.addr; + + ci_write_32(addr + offset_of(struct virtio_dev_common, dev_features_sel), + cpu_to_le32(1)); + f1 = ci_read_32(addr + + offset_of(struct virtio_dev_common, dev_features)); + ci_write_32(addr + offset_of(struct virtio_dev_common, dev_features_sel), + cpu_to_le32(0)); + f0 = ci_read_32(addr + + offset_of(struct virtio_dev_common, dev_features)); + + features = ((uint64_t)le32_to_cpu(f1) << 32) | le32_to_cpu(f0); + } else { + features = le32_to_cpu(ci_read_32(dev->legacy.addr+VIRTIOHDR_DEVICE_FEATURES)); } + return features; } +int virtio_negotiate_guest_features(struct virtio_device *dev, uint64_t features) +{ + uint64_t host_features = 0; + int status; + + /* Negotiate features */ + host_features = virtio_get_host_features(dev); + if (!(host_features & VIRTIO_F_VERSION_1)) { + fprintf(stderr, "Device does not support virtio 1.0 %llx\n", host_features); + return -1; + } + + virtio_set_guest_features(dev, features); + host_features = virtio_get_host_features(dev); + if ((host_features & features) != features) { + fprintf(stderr, "Features error %llx\n", features); + return -1; + } + + virtio_get_status(dev, &status); + status |= VIRTIO_STAT_FEATURES_OK; + virtio_set_status(dev, status); + + /* Read back to verify the FEATURES_OK bit */ + virtio_get_status(dev, &status); + if ((status & VIRTIO_STAT_FEATURES_OK) != VIRTIO_STAT_FEATURES_OK) + return -1; + + return 0; +} /** * Get additional config values @@ -186,32 +516,38 @@ void virtio_get_host_features(struct virtio_device *dev, int *features) uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size) { uint64_t val = ~0ULL; + uint32_t hi, lo; void *confbase; - switch (dev->type) { - case VIRTIO_TYPE_PCI: - confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG; - break; - default: - return ~0ULL; - } + if (dev->is_modern) + confbase = dev->device.addr; + else + confbase = dev->legacy.addr+VIRTIOHDR_DEVICE_CONFIG; + switch (size) { - case 1: + case 1: val = ci_read_8(confbase+offset); break; - case 2: + case 2: val = ci_read_16(confbase+offset); + if (dev->is_modern) + val = le16_to_cpu(val); break; - case 4: + case 4: val = ci_read_32(confbase+offset); + if (dev->is_modern) + val = le32_to_cpu(val); break; - case 8: + case 8: /* We don't support 8 bytes PIO accesses * in qemu and this is all PIO */ - val = ci_read_32(confbase+offset); - val <<= 32; - val |= ci_read_32(confbase+offset+4); + lo = ci_read_32(confbase+offset); + hi = ci_read_32(confbase+offset+4); + if (dev->is_modern) + val = (uint64_t)le32_to_cpu(hi) << 32 | le32_to_cpu(lo); + else + val = (uint64_t)hi << 32 | lo; break; } @@ -222,20 +558,19 @@ uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size) * Get config blob */ int __virtio_read_config(struct virtio_device *dev, void *dst, - int offset, int len) + int offset, int len) { void *confbase; unsigned char *buf = dst; int i; - switch (dev->type) { - case VIRTIO_TYPE_PCI: - confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG; - break; - default: - return 0; - } + if (dev->is_modern) + confbase = dev->device.addr; + else + confbase = dev->legacy.addr+VIRTIOHDR_DEVICE_CONFIG; + for (i = 0; i < len; i++) buf[i] = ci_read_8(confbase + offset + i); + return len; } diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.code b/qemu/roms/SLOF/lib/libvirtio/virtio.code index 258b9bbda..8eec8f055 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio.code +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.code @@ -18,6 +18,11 @@ /******** core virtio ********/ +// : virtio-setup-vd ( -- dev ) +PRIM(virtio_X2d_setup_X2d_vd) + PUSH; TOS.a = virtio_setup_vd(); +MIRP + // : virtio-vring-size ( queuesize -- ringsize ) PRIM(virtio_X2d_vring_X2d_size) TOS.u = virtio_vring_size(TOS.u); @@ -122,20 +127,18 @@ MIRP /******** virtio-net ********/ -// : virtio-net-open ( mac-addr-str len dev -- false | [ driver true ] ) +// : virtio-net-open ( dev -- false | [ driver true ] ) PRIM(virtio_X2d_net_X2d_open) { - void *dev = TOS.a; POP; - int len = TOS.u; POP; - char *mac_addr = TOS.a; + void *dev = TOS.a; - net_driver_t *net_driver = virtionet_open(mac_addr, len, dev); + net_driver_t *net_driver = virtionet_open(dev); - if (net_driver) { - TOS.u = (unsigned long)net_driver; PUSH; - TOS.n = -1; - } else - TOS.n = 0; + if (net_driver) { + TOS.u = (unsigned long)net_driver; PUSH; + TOS.n = -1; + } else + TOS.n = 0; } MIRP diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.h b/qemu/roms/SLOF/lib/libvirtio/virtio.h index d5759b45a..0fee4baec 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio.h +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.h @@ -14,13 +14,23 @@ #define _LIBVIRTIO_H #include <stdint.h> +#include <stdbool.h> /* Device status bits */ #define VIRTIO_STAT_ACKNOWLEDGE 1 #define VIRTIO_STAT_DRIVER 2 #define VIRTIO_STAT_DRIVER_OK 4 +#define VIRTIO_STAT_FEATURES_OK 8 +#define VIRTIO_STAT_NEEDS_RESET 64 #define VIRTIO_STAT_FAILED 128 +#define BIT(x) (1UL << (x)) + +/* VIRTIO 1.0 Device independent feature bits */ +#define VIRTIO_F_RING_INDIRECT_DESC BIT(28) +#define VIRTIO_F_RING_EVENT_IDX BIT(29) +#define VIRTIO_F_VERSION_1 BIT(32) + #define VIRTIO_TIMEOUT 5000 /* 5 sec timeout */ /* Definitions for vring_desc.flags */ @@ -34,7 +44,7 @@ struct vring_desc { uint32_t len; /* Length */ uint16_t flags; /* The flags as indicated above */ uint16_t next; /* Next field if flags & NEXT */ -}; +}; /* Definitions for vring_avail.flags */ #define VRING_AVAIL_F_NO_INTERRUPT 1 @@ -44,8 +54,7 @@ struct vring_avail { uint16_t flags; uint16_t idx; uint16_t ring[]; -}; - +}; /* Definitions for vring_used.flags */ #define VRING_USED_F_NO_NOTIFY 1 @@ -61,27 +70,56 @@ struct vring_used { struct vring_used_elem ring[]; }; -#define VIRTIO_TYPE_PCI 0 /* For virtio-pci interface */ +/* Structure shared with SLOF and is 16bytes */ +struct virtio_cap { + void *addr; + uint8_t bar; + uint8_t is_io; + uint8_t cap_id; +}; + struct virtio_device { - void *base; /* base address */ - int type; /* VIRTIO_TYPE_PCI or VIRTIO_TYPE_VIO */ + uint32_t is_modern; /* Indicates whether to use virtio 1.0 */ + struct virtio_cap legacy; + struct virtio_cap common; + struct virtio_cap notify; + struct virtio_cap isr; + struct virtio_cap device; + struct virtio_cap pci; + uint32_t notify_off_mul; +}; + +struct vqs { + uint64_t id; /* Queue ID */ + uint32_t size; + void *buf_mem; + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; }; /* Parts of the virtqueue are aligned on a 4096 byte page boundary */ #define VQ_ALIGN(addr) (((addr) + 0xfff) & ~0xfff) extern unsigned long virtio_vring_size(unsigned int qsize); -extern int virtio_get_qsize(struct virtio_device *dev, int queue); +extern unsigned int virtio_get_qsize(struct virtio_device *dev, int queue); extern struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue); extern struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue); extern struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue); +extern void virtio_fill_desc(struct vring_desc *desc, bool is_modern, + uint64_t addr, uint32_t len, + uint16_t flags, uint16_t next); +extern int virtio_queue_init_vq(struct virtio_device *dev, struct vqs *vq, unsigned int id); +extern struct virtio_device *virtio_setup_vd(void); extern void virtio_reset_device(struct virtio_device *dev); extern void virtio_queue_notify(struct virtio_device *dev, int queue); extern void virtio_set_status(struct virtio_device *dev, int status); -extern void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned int qaddr); -extern void virtio_set_guest_features(struct virtio_device *dev, int features); -extern void virtio_get_host_features(struct virtio_device *dev, int *features); +extern void virtio_get_status(struct virtio_device *dev, int *status); +extern void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned long qaddr); +extern void virtio_set_guest_features(struct virtio_device *dev, uint64_t features); +extern uint64_t virtio_get_host_features(struct virtio_device *dev); +extern int virtio_negotiate_guest_features(struct virtio_device *dev, uint64_t features); extern uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size); extern int __virtio_read_config(struct virtio_device *dev, void *dst, int offset, int len); diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.in b/qemu/roms/SLOF/lib/libvirtio/virtio.in index c36d127c7..195840e0f 100644 --- a/qemu/roms/SLOF/lib/libvirtio/virtio.in +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.in @@ -10,6 +10,8 @@ * IBM Corporation - initial implementation *****************************************************************************/ +cod(virtio-setup-vd) + cod(virtio-vring-size) cod(virtio-get-qsize) cod(virtio-get-config) |