[OpenBIOS] [PATCH v2 3/4] drivers: add legacy virtio-blk driver
Mark Cave-Ayland
mark.cave-ayland at ilande.co.uk
Sun Aug 19 12:55:05 CEST 2018
Note that as part of this commit we rename the virtio-blk device node from
"virtio-blk" to "scsi" to match up with the QEMU fw path generator.
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland at ilande.co.uk>
---
drivers/build.xml | 1 +
drivers/pci.c | 18 ++
drivers/pci_database.c | 4 +-
drivers/pci_database.h | 1 +
drivers/virtio.c | 516 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/virtio.h | 330 +++++++++++++++++++++++++++++
include/drivers/drivers.h | 4 +
7 files changed, 872 insertions(+), 2 deletions(-)
create mode 100644 drivers/virtio.c
create mode 100644 drivers/virtio.h
diff --git a/drivers/build.xml b/drivers/build.xml
index 8df074b..5a28bc2 100644
--- a/drivers/build.xml
+++ b/drivers/build.xml
@@ -28,6 +28,7 @@
<object source="usbohci.c" condition="DRIVER_USB"/>
<object source="usbohci_rh.c" condition="DRIVER_USB"/>
<object source="lsi.c" condition="DRIVER_LSI_53C810"/>
+ <object source="virtio.c" condition="DRIVER_VIRTIO_BLK"/>
</library>
<dictionary name="openbios" target="forth">
diff --git a/drivers/pci.c b/drivers/pci.c
index 7a174b4..0168393 100644
--- a/drivers/pci.c
+++ b/drivers/pci.c
@@ -770,6 +770,24 @@ int sungem_config_cb (const pci_config_t *config)
return 0;
}
+int virtio_blk_config_cb(const pci_config_t *config)
+{
+#ifdef CONFIG_DRIVER_VIRTIO_BLK
+ pci_addr addr;
+ uint8_t idx;
+
+ addr = PCI_ADDR(
+ PCI_BUS(config->dev),
+ PCI_DEV(config->dev),
+ PCI_FN(config->dev));
+
+ idx = (uint8_t)(pci_config_read16(addr, PCI_DEVICE_ID) & 0xff) - 1;
+
+ ob_virtio_init(config->path, "virtio-blk", arch->io_base, config->assigned[0] & ~0x0000000F, idx);
+#endif
+ return 0;
+}
+
/*
* "Designing PCI Cards and Drivers for Power Macintosh Computers", p. 454
*
diff --git a/drivers/pci_database.c b/drivers/pci_database.c
index 8288acd..40fdeb6 100644
--- a/drivers/pci_database.c
+++ b/drivers/pci_database.c
@@ -49,10 +49,10 @@ static const pci_dev_t scsi_devices[] = {
{
/* Virtio-block controller */
PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_DEVICE_ID_VIRTIO_BLOCK,
- NULL, "virtio-blk", NULL,
+ NULL, "scsi", NULL,
"pci1af4,1001\0pci1af4,1001\0pciclass,01018f\0",
0, 0, 0,
- NULL, NULL,
+ virtio_blk_config_cb, NULL,
},
{
/* lsi53c810 controller */
diff --git a/drivers/pci_database.h b/drivers/pci_database.h
index 6f5eb39..e39ebfb 100644
--- a/drivers/pci_database.h
+++ b/drivers/pci_database.h
@@ -29,6 +29,7 @@ struct pci_dev_t {
};
extern int ide_config_cb2(const pci_config_t *config);
+extern int virtio_blk_config_cb(const pci_config_t *config);
extern int eth_config_cb(const pci_config_t *config);
extern int macio_heathrow_config_cb(const pci_config_t *config);
extern int macio_keylargo_config_cb(const pci_config_t *config);
diff --git a/drivers/virtio.c b/drivers/virtio.c
new file mode 100644
index 0000000..3bc127a
--- /dev/null
+++ b/drivers/virtio.c
@@ -0,0 +1,516 @@
+/*
+ * OpenBIOS Legacy Virtio driver
+ *
+ * Copyright (c) 2013 Alexander Graf <agraf at suse.de>
+ * Copyright (c) 2016 Mark Cave-Ayland <mark.cave-ayland at ilande.co.uk>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#include "config.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+#include "libopenbios/bindings.h"
+#include "libopenbios/ofmem.h"
+#include "kernel/kernel.h"
+#include "drivers/drivers.h"
+
+#include "virtio.h"
+
+#define VRING_WAIT_REPLY_TIMEOUT 10000
+
+
+static uint8_t virtio_cfg_read8(VDev *vdev, int addr)
+{
+ return inb((uint32_t)(vdev->io_base + addr));
+}
+
+static void virtio_cfg_write8(VDev *vdev, int addr, uint8_t value)
+{
+ outb(value, vdev->io_base + addr);
+}
+
+static uint16_t virtio_cfg_read16(VDev *vdev, int addr)
+{
+ return inw(vdev->io_base + addr);
+}
+
+static void virtio_cfg_write16(VDev *vdev, int addr, uint16_t value)
+{
+ outw(value, vdev->io_base + addr);
+}
+
+static uint32_t virtio_cfg_read32(VDev *vdev, int addr)
+{
+ return inl(vdev->io_base + addr);
+}
+
+static void virtio_cfg_write32(VDev *vdev, int addr, uint32_t value)
+{
+ outl(value, vdev->io_base + addr);
+}
+
+static uint64_t virtio_cfg_read64(VDev *vdev, int addr)
+{
+ uint64_t q;
+ uint8_t *p;
+ int i;
+
+ for (i = 0, p = (uint8_t *)&q; i < 8; i++, p++) {
+ *p = virtio_cfg_read8(vdev, addr + i);
+ }
+
+ return q;
+}
+
+static long virtio_notify(VDev *vdev, int vq_idx, long cookie)
+{
+ virtio_cfg_write16(vdev, VIRTIO_PCI_QUEUE_NOTIFY, vq_idx);
+
+ return 0;
+}
+
+/***********************************************
+ * Virtio functions *
+ ***********************************************/
+
+static void vring_init(VRing *vr, VqInfo *info)
+{
+ void *p = (void *) (uintptr_t)info->queue;
+
+ vr->id = info->index;
+ vr->num = info->num;
+ vr->desc = p;
+ vr->avail = (void *)((uintptr_t)p + info->num * sizeof(VRingDesc));
+ vr->used = (void *)(((unsigned long)&vr->avail->ring[info->num]
+ + info->align - 1) & ~(info->align - 1));
+
+ /* Zero out all relevant field */
+ vr->avail->flags = 0;
+ vr->avail->idx = 0;
+
+ /* We're running with interrupts off anyways, so don't bother */
+ vr->used->flags = VRING_USED_F_NO_NOTIFY;
+ vr->used->idx = 0;
+ vr->used_idx = 0;
+ vr->next_idx = 0;
+ vr->cookie = 0;
+}
+
+static int vring_notify(VDev *vdev, VRing *vr)
+{
+ return virtio_notify(vdev, vr->id, vr->cookie);
+}
+
+static void vring_send_buf(VRing *vr, void *p, int len, int flags)
+{
+ /* For follow-up chains we need to keep the first entry point */
+ if (!(flags & VRING_HIDDEN_IS_CHAIN)) {
+ vr->avail->ring[vr->avail->idx % vr->num] = vr->next_idx;
+ }
+
+ vr->desc[vr->next_idx].addr = (uintptr_t)p;
+ vr->desc[vr->next_idx].len = len;
+ vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN;
+ vr->desc[vr->next_idx].next = vr->next_idx;
+ vr->desc[vr->next_idx].next++;
+ vr->next_idx++;
+
+ /* Chains only have a single ID */
+ if (!(flags & VRING_DESC_F_NEXT)) {
+ vr->avail->idx++;
+ }
+}
+
+static int vr_poll(VDev *vdev, VRing *vr)
+{
+ if (vr->used->idx == vr->used_idx) {
+ vring_notify(vdev, vr);
+ return 0;
+ }
+
+ vr->used_idx = vr->used->idx;
+ vr->next_idx = 0;
+ vr->desc[0].len = 0;
+ vr->desc[0].flags = 0;
+ return 1; /* vr has been updated */
+}
+
+/*
+ * Wait for the host to reply.
+ *
+ * timeout is in msecs if > 0.
+ *
+ * Returns 0 on success, 1 on timeout.
+ */
+static int vring_wait_reply(VDev *vdev)
+{
+ ucell target_ms, get_ms;
+
+ fword("get-msecs");
+ target_ms = POP();
+ target_ms += vdev->wait_reply_timeout;
+
+ /* Wait for any queue to be updated by the host */
+ do {
+ int i, r = 0;
+
+ for (i = 0; i < vdev->nr_vqs; i++) {
+ r += vr_poll(vdev, &vdev->vrings[i]);
+ }
+
+ if (r) {
+ return 0;
+ }
+
+ fword("get-msecs");
+ get_ms = POP();
+
+ } while (!vdev->wait_reply_timeout || (get_ms < target_ms));
+
+ return 1;
+}
+
+static void *vring_addr_translate(VDev *vdev, void *p)
+{
+ ucell phys, mode;
+
+ phys = ofmem_translate(pointer2cell(p), &mode);
+ return cell2pointer(phys);
+}
+
+/***********************************************
+ * Virtio block *
+ ***********************************************/
+
+static int virtio_blk_read_many(VDev *vdev,
+ uint64_t offset, void *load_addr, int len)
+{
+ VirtioBlkOuthdr out_hdr;
+ u8 status;
+ VRing *vr = &vdev->vrings[vdev->cmd_vr_idx];
+ uint8_t discard[VIRTIO_SECTOR_SIZE];
+
+ uint64_t start_sector = offset / virtio_get_block_size(vdev);
+ int head_len = offset & (virtio_get_block_size(vdev) - 1);
+ uint64_t end_sector = (offset + len + virtio_get_block_size(vdev) - 1) /
+ virtio_get_block_size(vdev);
+ int tail_len = end_sector * virtio_get_block_size(vdev) - (offset + len);
+
+ /* Tell the host we want to read */
+ out_hdr.type = VIRTIO_BLK_T_IN;
+ out_hdr.ioprio = 99;
+ out_hdr.sector = virtio_sector_adjust(vdev, start_sector);
+
+ vring_send_buf(vr, vring_addr_translate(vdev, &out_hdr), sizeof(out_hdr),
+ VRING_DESC_F_NEXT);
+
+ /* Discarded head */
+ if (head_len) {
+ vring_send_buf(vr, vring_addr_translate(vdev, &discard), head_len,
+ VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
+ VRING_DESC_F_NEXT);
+ }
+
+ /* This is where we want to receive data */
+ vring_send_buf(vr, vring_addr_translate(vdev, load_addr), len,
+ VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
+ VRING_DESC_F_NEXT);
+
+ /* Discarded tail */
+ if (tail_len) {
+ vring_send_buf(vr, vring_addr_translate(vdev, &discard), tail_len,
+ VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
+ VRING_DESC_F_NEXT);
+ }
+
+ /* status field */
+ vring_send_buf(vr, vring_addr_translate(vdev, &status), sizeof(u8),
+ VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN);
+
+ /* Now we can tell the host to read */
+ vring_wait_reply(vdev);
+
+ return status;
+}
+
+int virtio_read_many(VDev *vdev, uint64_t offset, void *load_addr, int len)
+{
+ switch (vdev->senseid) {
+ case VIRTIO_ID_BLOCK:
+ return virtio_blk_read_many(vdev, offset, load_addr, len);
+ }
+ return -1;
+}
+
+static int virtio_read(VDev *vdev, uint64_t offset, void *load_addr, int len)
+{
+ return virtio_read_many(vdev, offset, load_addr, len);
+}
+
+int virtio_get_block_size(VDev *vdev)
+{
+ switch (vdev->senseid) {
+ case VIRTIO_ID_BLOCK:
+ return vdev->config.blk.blk_size << vdev->config.blk.physical_block_exp;
+ }
+ return 0;
+}
+
+static void
+ob_virtio_disk_open(VDev **_vdev)
+{
+ VDev *vdev = *_vdev;
+ phandle_t ph;
+
+ vdev->pos = 0;
+
+ /* interpose disk-label */
+ ph = find_dev("/packages/disk-label");
+ fword("my-args");
+ PUSH_ph( ph );
+ fword("interpose");
+
+ RET(-1);
+}
+
+static void
+ob_virtio_disk_close(VDev **_vdev)
+{
+ return;
+}
+
+/* ( pos.d -- status ) */
+static void
+ob_virtio_disk_seek(VDev **_vdev)
+{
+ VDev *vdev = *_vdev;
+ uint64_t pos;
+
+ pos = ((uint64_t)POP()) << 32;
+ pos |= POP();
+
+ /* Make sure we are within the physical limits */
+ if (pos < (vdev->config.blk.capacity * virtio_get_block_size(vdev))) {
+ vdev->pos = pos;
+ PUSH(0);
+ } else {
+ PUSH(1);
+ }
+
+ return;
+}
+
+/* ( addr len -- actual ) */
+static void
+ob_virtio_disk_read(VDev **_vdev)
+{
+ VDev *vdev = *_vdev;
+ ucell len = POP();
+ uint8_t *addr = (uint8_t *)POP();
+
+ virtio_read(vdev, vdev->pos, addr, len);
+
+ vdev->pos += len;
+
+ PUSH(len);
+}
+
+static void set_virtio_alias(const char *path, int idx)
+{
+ phandle_t aliases;
+ char name[9];
+
+ aliases = find_dev("/aliases");
+
+ snprintf(name, sizeof(name), "virtio%d", idx);
+
+ set_property(aliases, name, path, strlen(path) + 1);
+}
+
+static void
+ob_virtio_disk_initialize(VDev **_vdev)
+{
+ phandle_t ph = get_cur_dev();
+ VDev *vdev;
+ int len;
+
+ vdev = cell2pointer(get_int_property(ph, "_vdev", &len));
+ push_str("_vdev");
+ feval("delete-property");
+
+ *_vdev = vdev;
+}
+
+DECLARE_UNNAMED_NODE(ob_virtio_disk, 0, sizeof(VDev *));
+
+NODE_METHODS(ob_virtio_disk) = {
+ { NULL, ob_virtio_disk_initialize },
+ { "open", ob_virtio_disk_open },
+ { "close", ob_virtio_disk_close },
+ { "seek", ob_virtio_disk_seek },
+ { "read", ob_virtio_disk_read },
+};
+
+static void
+ob_virtio_open(VDev **_vdev)
+{
+ PUSH(-1);
+}
+
+static void
+ob_virtio_close(VDev **_vdev)
+{
+ return;
+}
+
+static void
+ob_virtio_vdev(VDev **_vdev)
+{
+ PUSH(pointer2cell(_vdev));
+}
+
+static void
+ob_virtio_dma_alloc(__attribute__((unused)) VDev **_vdev)
+{
+ call_parent_method("dma-alloc");
+}
+
+static void
+ob_virtio_dma_free(__attribute__((unused)) VDev **_vdev)
+{
+ call_parent_method("dma-free");
+}
+
+static void
+ob_virtio_dma_map_in(__attribute__((unused)) VDev **_vdev)
+{
+ call_parent_method("dma-map-in");
+}
+
+static void
+ob_virtio_dma_map_out(__attribute__((unused)) VDev **_vdev)
+{
+ call_parent_method("dma-map-out");
+}
+
+static void
+ob_virtio_dma_sync(__attribute__((unused)) VDev **_vdev)
+{
+ call_parent_method("dma-sync");
+}
+
+DECLARE_UNNAMED_NODE(ob_virtio, 0, sizeof(VDev *));
+
+NODE_METHODS(ob_virtio) = {
+ { "open", ob_virtio_open },
+ { "close", ob_virtio_close },
+ { "vdev", ob_virtio_vdev },
+ { "dma-alloc", ob_virtio_dma_alloc },
+ { "dma-free", ob_virtio_dma_free },
+ { "dma-map-in", ob_virtio_dma_map_in },
+ { "dma-map-out", ob_virtio_dma_map_out },
+ { "dma-sync", ob_virtio_dma_sync },
+};
+
+void ob_virtio_init(const char *path, const char *dev_name, uint64_t base,
+ uint64_t offset, int idx)
+{
+ char buf[256];
+ phandle_t ph;
+ ucell addr;
+ int i;
+ uint8_t status;
+ VDev *vdev, **_vdev;
+
+ REGISTER_NODE_METHODS(ob_virtio, path);
+
+ /* Open ob_virtio */
+ fword("my-self");
+ push_str(path);
+ feval("open-dev to my-self");
+
+ ph = find_ih_method("vdev", my_self());
+ PUSH(ph);
+ fword("execute");
+ _vdev = cell2pointer(POP());
+
+ vdev = malloc(sizeof(VDev));
+ vdev->io_base = offset;
+
+ /* Indicate we recognise the device */
+ status = virtio_cfg_read8(vdev, VIRTIO_PCI_STATUS);
+ status |= VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER;
+ virtio_cfg_write8(vdev, VIRTIO_PCI_STATUS, status);
+
+ vdev->senseid = VIRTIO_ID_BLOCK;
+ vdev->nr_vqs = 1;
+ vdev->cmd_vr_idx = 0;
+ vdev->wait_reply_timeout = VRING_WAIT_REPLY_TIMEOUT;
+ vdev->scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE;
+ vdev->blk_factor = 1;
+
+ PUSH(sizeof(VRing) * VIRTIO_MAX_VQS);
+ feval("dma-alloc");
+ addr = POP();
+ vdev->vrings = cell2pointer(addr);
+
+ PUSH(VIRTIO_RING_SIZE * VIRTIO_MAX_VQS);
+ feval("dma-alloc");
+ addr = POP();
+ vdev->ring_area = cell2pointer(addr);
+
+ for (i = 0; i < vdev->nr_vqs; i++) {
+ VqInfo info = {
+ .queue = (uintptr_t) vdev->ring_area + (i * VIRTIO_RING_SIZE),
+ .align = VIRTIO_PCI_VRING_ALIGN,
+ .index = i,
+ .num = 0,
+ };
+
+ virtio_cfg_write16(vdev, VIRTIO_PCI_QUEUE_SEL, i);
+ info.num = virtio_cfg_read16(vdev, VIRTIO_PCI_QUEUE_NUM);
+
+ vring_init(&vdev->vrings[i], &info);
+
+ /* Set block information */
+ vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
+ vdev->config.blk.blk_size = VIRTIO_SECTOR_SIZE;
+ vdev->config.blk.physical_block_exp = 0;
+
+ /* Read sectors */
+ vdev->config.blk.capacity = virtio_cfg_read64(vdev, 0x14);
+
+ /* Set queue address */
+ virtio_cfg_read32(vdev, VIRTIO_PCI_QUEUE_PFN);
+ virtio_cfg_write32(vdev, VIRTIO_PCI_QUEUE_PFN,
+ va2pa(info.queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
+ }
+
+ *_vdev = vdev;
+ feval("to my-self");
+
+ /* Initialisation complete */
+ status |= VIRTIO_CONFIG_S_DRIVER_OK;
+ virtio_cfg_write8(vdev, VIRTIO_PCI_STATUS, status);
+
+ fword("new-device");
+ push_str("disk");
+ fword("device-name");
+ push_str("block");
+ fword("device-type");
+
+ PUSH(pointer2cell(vdev));
+ fword("encode-int");
+ push_str("_vdev");
+ fword("property");
+
+ fword("finish-device");
+
+ snprintf(buf, sizeof(buf), "%s/disk", path);
+ REGISTER_NODE_METHODS(ob_virtio_disk, buf);
+
+ set_virtio_alias(buf, idx);
+}
diff --git a/drivers/virtio.h b/drivers/virtio.h
new file mode 100644
index 0000000..414d5f9
--- /dev/null
+++ b/drivers/virtio.h
@@ -0,0 +1,330 @@
+/*
+ * Virtio driver bits
+ *
+ * Copyright (c) 2013 Alexander Graf <agraf at suse.de>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#ifndef VIRTIO_H
+#define VIRTIO_H
+
+/* A 32-bit r/o bitmask of the features supported by the host */
+#define VIRTIO_PCI_HOST_FEATURES 0
+
+/* A 32-bit r/w bitmask of features activated by the guest */
+#define VIRTIO_PCI_GUEST_FEATURES 4
+
+/* A 32-bit r/w PFN for the currently selected queue */
+#define VIRTIO_PCI_QUEUE_PFN 8
+
+/* A 16-bit r/o queue size for the currently selected queue */
+#define VIRTIO_PCI_QUEUE_NUM 12
+
+/* A 16-bit r/w queue selector */
+#define VIRTIO_PCI_QUEUE_SEL 14
+
+/* A 16-bit r/w queue notifier */
+#define VIRTIO_PCI_QUEUE_NOTIFY 16
+
+/* An 8-bit device status register. */
+#define VIRTIO_PCI_STATUS 18
+
+/* An 8-bit r/o interrupt status register. Reading the value will return the
+ * current contents of the ISR and will also clear it. This is effectively
+ * a read-and-acknowledge. */
+#define VIRTIO_PCI_ISR 19
+
+/* MSI-X registers: only enabled if MSI-X is enabled. */
+/* A 16-bit vector for configuration changes. */
+#define VIRTIO_MSI_CONFIG_VECTOR 20
+/* A 16-bit vector for selected queue notifications. */
+#define VIRTIO_MSI_QUEUE_VECTOR 22
+
+/* How many bits to shift physical queue address written to QUEUE_PFN.
+ * 12 is historical, and due to x86 page size. */
+#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12
+
+/* The alignment to use between consumer and producer parts of vring.
+ * x86 pagesize again. */
+#define VIRTIO_PCI_VRING_ALIGN 4096
+
+/* Status byte for guest to report progress, and synchronize features. */
+/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
+#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
+/* We have found a driver for the device. */
+#define VIRTIO_CONFIG_S_DRIVER 2
+/* Driver has used its parts of the config, and is happy */
+#define VIRTIO_CONFIG_S_DRIVER_OK 4
+/* We've given up on this device. */
+#define VIRTIO_CONFIG_S_FAILED 0x80
+
+enum VirtioDevType {
+ VIRTIO_ID_NET = 1,
+ VIRTIO_ID_BLOCK = 2,
+ VIRTIO_ID_CONSOLE = 3,
+ VIRTIO_ID_BALLOON = 5,
+ VIRTIO_ID_SCSI = 8,
+};
+typedef enum VirtioDevType VirtioDevType;
+
+struct VirtioDevHeader {
+ VirtioDevType type:8;
+ uint8_t num_vq;
+ uint8_t feature_len;
+ uint8_t config_len;
+ uint8_t status;
+ uint8_t vqconfig[];
+} __attribute__((packed));
+typedef struct VirtioDevHeader VirtioDevHeader;
+
+struct VirtioVqConfig {
+ uint64_t token;
+ uint64_t address;
+ uint16_t num;
+ uint8_t pad[6];
+} __attribute__((packed));
+typedef struct VirtioVqConfig VirtioVqConfig;
+
+struct VqInfo {
+ uint32_t queue;
+ uint32_t align;
+ uint16_t index;
+ uint16_t num;
+} __attribute__((packed));
+typedef struct VqInfo VqInfo;
+
+struct VqConfig {
+ uint16_t index;
+ uint16_t num;
+} __attribute__((packed));
+typedef struct VqConfig VqConfig;
+
+struct VirtioDev {
+ VirtioDevHeader *header;
+ VirtioVqConfig *vqconfig;
+ char *host_features;
+ char *guest_features;
+ char *config;
+};
+typedef struct VirtioDev VirtioDev;
+
+#define VIRTIO_RING_SIZE (PAGE_SIZE * 8)
+#define VIRTIO_MAX_VQS 3
+#define KVM_S390_VIRTIO_RING_ALIGN 4096
+
+#define VRING_USED_F_NO_NOTIFY 1
+
+/* This marks a buffer as continuing via the next field. */
+#define VRING_DESC_F_NEXT 1
+/* This marks a buffer as write-only (otherwise read-only). */
+#define VRING_DESC_F_WRITE 2
+/* This means the buffer contains a list of buffer descriptors. */
+#define VRING_DESC_F_INDIRECT 4
+
+/* Internal flag to mark follow-up segments as such */
+#define VRING_HIDDEN_IS_CHAIN 256
+
+/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */
+struct VRingDesc {
+ /* Address (guest-physical). */
+ uint64_t addr;
+ /* Length. */
+ uint32_t len;
+ /* The flags as indicated above. */
+ uint16_t flags;
+ /* We chain unused descriptors via this, too */
+ uint16_t next;
+} __attribute__((packed));
+typedef struct VRingDesc VRingDesc;
+
+struct VRingAvail {
+ uint16_t flags;
+ uint16_t idx;
+ uint16_t ring[];
+} __attribute__((packed));
+typedef struct VRingAvail VRingAvail;
+
+/* uint32_t is used here for ids for padding reasons. */
+struct VRingUsedElem {
+ /* Index of start of used descriptor chain. */
+ uint32_t id;
+ /* Total length of the descriptor chain which was used (written to) */
+ uint32_t len;
+} __attribute__((packed));
+typedef struct VRingUsedElem VRingUsedElem;
+
+struct VRingUsed {
+ uint16_t flags;
+ uint16_t idx;
+ VRingUsedElem ring[];
+} __attribute__((packed));
+typedef struct VRingUsed VRingUsed;
+
+struct VRing {
+ unsigned int num;
+ int next_idx;
+ int used_idx;
+ VRingDesc *desc;
+ VRingAvail *avail;
+ VRingUsed *used;
+ long cookie;
+ int id;
+};
+typedef struct VRing VRing;
+
+
+/***********************************************
+ * Virtio block *
+ ***********************************************/
+
+/*
+ * Command types
+ *
+ * Usage is a bit tricky as some bits are used as flags and some are not.
+ *
+ * Rules:
+ * VIRTIO_BLK_T_OUT may be combined with VIRTIO_BLK_T_SCSI_CMD or
+ * VIRTIO_BLK_T_BARRIER. VIRTIO_BLK_T_FLUSH is a command of its own
+ * and may not be combined with any of the other flags.
+ */
+
+/* These two define direction. */
+#define VIRTIO_BLK_T_IN 0
+#define VIRTIO_BLK_T_OUT 1
+
+/* This bit says it's a scsi command, not an actual read or write. */
+#define VIRTIO_BLK_T_SCSI_CMD 2
+
+/* Cache flush command */
+#define VIRTIO_BLK_T_FLUSH 4
+
+/* Barrier before this op. */
+#define VIRTIO_BLK_T_BARRIER 0x80000000
+
+/* This is the first element of the read scatter-gather list. */
+struct VirtioBlkOuthdr {
+ /* VIRTIO_BLK_T* */
+ uint32_t type;
+ /* io priority. */
+ uint32_t ioprio;
+ /* Sector (ie. 512 byte offset) */
+ uint64_t sector;
+};
+typedef struct VirtioBlkOuthdr VirtioBlkOuthdr;
+
+struct VirtioBlkConfig {
+ uint64_t capacity; /* in 512-byte sectors */
+ uint32_t size_max; /* max segment size (if VIRTIO_BLK_F_SIZE_MAX) */
+ uint32_t seg_max; /* max number of segments (if VIRTIO_BLK_F_SEG_MAX) */
+
+ struct VirtioBlkGeometry {
+ uint16_t cylinders;
+ uint8_t heads;
+ uint8_t sectors;
+ } geometry; /* (if VIRTIO_BLK_F_GEOMETRY) */
+
+ uint32_t blk_size; /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */
+
+ /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */
+ uint8_t physical_block_exp; /* exponent for physical blk per logical blk */
+ uint8_t alignment_offset; /* alignment offset in logical blocks */
+ uint16_t min_io_size; /* min I/O size without performance penalty
+ in logical blocks */
+ uint32_t opt_io_size; /* optimal sustained I/O size in logical blks */
+
+ uint8_t wce; /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */
+} __attribute__((packed));
+typedef struct VirtioBlkConfig VirtioBlkConfig;
+
+enum guessed_disk_nature_type {
+ VIRTIO_GDN_NONE = 0,
+ VIRTIO_GDN_DASD = 1,
+ VIRTIO_GDN_CDROM = 2,
+ VIRTIO_GDN_SCSI = 3,
+};
+typedef enum guessed_disk_nature_type VirtioGDN;
+
+#define VIRTIO_SECTOR_SIZE 512
+#define VIRTIO_ISO_BLOCK_SIZE 2048
+#define VIRTIO_SCSI_BLOCK_SIZE 512
+
+struct VirtioScsiConfig {
+ uint32_t num_queues;
+ uint32_t seg_max;
+ uint32_t max_sectors;
+ uint32_t cmd_per_lun;
+ uint32_t event_info_size;
+ uint32_t sense_size;
+ uint32_t cdb_size;
+ uint16_t max_channel;
+ uint16_t max_target;
+ uint32_t max_lun;
+} __attribute__((packed));
+typedef struct VirtioScsiConfig VirtioScsiConfig;
+
+struct ScsiDevice {
+ uint16_t channel; /* Always 0 in QEMU */
+ uint16_t target; /* will be scanned over */
+ uint32_t lun; /* will be reported */
+};
+typedef struct ScsiDevice ScsiDevice;
+
+struct VDev {
+ uint32_t io_base;
+ uint64_t pos;
+ int nr_vqs;
+ VRing *vrings;
+ int cmd_vr_idx;
+ void *ring_area;
+ long wait_reply_timeout;
+ VirtioGDN guessed_disk_nature;
+ int senseid;
+ union {
+ VirtioBlkConfig blk;
+ VirtioScsiConfig scsi;
+ } config;
+ ScsiDevice *scsi_device;
+ int is_cdrom;
+ int scsi_block_size;
+ int blk_factor;
+ uint64_t scsi_last_block;
+ uint32_t scsi_dev_cyls;
+ uint8_t scsi_dev_heads;
+ int scsi_device_selected;
+ ScsiDevice selected_scsi_device;
+};
+typedef struct VDev VDev;
+
+extern int virtio_get_block_size(VDev *vdev);
+extern uint8_t virtio_get_heads(VDev *vdev);
+extern uint8_t virtio_get_sectors(VDev *vdev);
+extern uint64_t virtio_get_blocks(VDev *vdev);
+
+static inline uint64_t virtio_sector_adjust(VDev *vdev, uint64_t sector)
+{
+ return sector * (virtio_get_block_size(vdev) / VIRTIO_SECTOR_SIZE);
+}
+
+VirtioGDN virtio_guessed_disk_nature(VDev *vdev);
+void virtio_assume_scsi(VDev *vdev);
+void virtio_assume_eckd(VDev *vdev);
+void virtio_assume_iso9660(VDev *vdev);
+
+extern int virtio_disk_is_scsi(VDev *vdev);
+extern int virtio_disk_is_eckd(VDev *vdev);
+
+int virtio_read_many(VDev *vdev, uint64_t sector, void *load_addr, int sec_num);
+VDev *virtio_get_device(void);
+VirtioDevType virtio_get_device_type(void);
+
+struct VirtioCmd {
+ void *data;
+ int size;
+ int flags;
+};
+typedef struct VirtioCmd VirtioCmd;
+
+#endif /* VIRTIO_H */
diff --git a/include/drivers/drivers.h b/include/drivers/drivers.h
index 117429e..75172c2 100644
--- a/include/drivers/drivers.h
+++ b/include/drivers/drivers.h
@@ -139,6 +139,10 @@ int keyboard_dataready(void);
unsigned char keyboard_readdata(void);
#endif
#endif
+#ifdef CONFIG_DRIVER_VIRTIO_BLK
+void ob_virtio_init(const char *path, const char *dev_name, uint64_t base,
+ uint64_t offset, int idx);
+#endif
int macio_get_nvram_size(void);
void macio_nvram_put(char *buf);
void macio_nvram_get(char *buf);
--
2.11.0
More information about the OpenBIOS
mailing list