These patches add support for booting from virtio-blk (PCI) devices as used by QEMU.
Patches 1 and 2 add bootindex support which is the new way for passing boot order information from QEMU to OpenBIOS. Note that there are also corresponding patches for QEMU required, without which the generated device paths will be incorrect and cause virtio boot to fail.
Patch 3 adds the virtio-1.0 compliant virtio-blk PCI driver.
Patch 4 enables the new driver for both PPC and SPARC64 architectures so that virtio-blk-pci devices will be usable out-of-the-box with QEMU.
Once these patches have been applied to OpenBIOS (along with the corresponding fw path fixes for QEMU) it is possible to boot from virtio-blk-pci devices like this:
PPC:
./qemu-system-ppc -drive file=debian-9.0-powerpc-NETINST.iso,if=none,index=0,id=cd,media=cdrom \ -device virtio-blk-pci,drive=cd,bootindex=0 -m 256 -boot d
SPARC:
./qemu-system-sparc64 -drive debian-9.0-sparc64-NETINST.iso,if=none,index=0,id=cd,media=cdrom \ -device virtio-blk-pci,bus=pciB,drive=cd,bootindex=0 -m 256 -boot d -nographic
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk
v3: - Rebase onto master - Convert from legacy driver to virtio-1.0 driver
v2: - Switch over to using dma-* words to allocate rings and descriptors - Fix up some minor whitespace issues
Mark Cave-Ayland (4): ppc: add bootindex support SPARC64: add bootindex support drivers: add virtio-1.0 virtio-blk driver config: enable virtio-blk driver for default PPC and SPARC64 builds
arch/ppc/qemu/init.c | 55 +++- arch/sparc64/boot.c | 2 +- arch/sparc64/boot.h | 2 +- arch/sparc64/openbios.c | 38 ++- config/examples/ppc_config.xml | 1 + config/examples/sparc64_config.xml | 1 + drivers/build.xml | 1 + drivers/pci.c | 63 +++++ drivers/pci_database.c | 14 +- drivers/pci_database.h | 1 + drivers/virtio.c | 555 +++++++++++++++++++++++++++++++++++++ drivers/virtio.h | 370 +++++++++++++++++++++++++ include/drivers/drivers.h | 5 + include/drivers/pci.h | 6 + 14 files changed, 1093 insertions(+), 21 deletions(-) create mode 100644 drivers/virtio.c create mode 100644 drivers/virtio.h
This provides an alternative mechanism for supporting boot device order information from QEMU compared with the legacy FW_CFG_BOOT_DEVICE functionality specified via -boot.
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk --- arch/ppc/qemu/init.c | 55 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 10 deletions(-)
diff --git a/arch/ppc/qemu/init.c b/arch/ppc/qemu/init.c index f5acf87..759f7ac 100644 --- a/arch/ppc/qemu/init.c +++ b/arch/ppc/qemu/init.c @@ -839,10 +839,11 @@ arch_of_init(void) #endif uint64_t ram_size; const struct cpudef *cpu; - char buf[64], qemu_uuid[16]; + char buf[256], qemu_uuid[16]; const char *stdin_path, *stdout_path, *boot_path; uint32_t temp = 0; - char *boot_device; + char *boot_device, *bootorder_file; + uint32_t bootorder_sz, sz; ofmem_t *ofmem = ofmem_arch_get_private(); ucell load_base;
@@ -1054,11 +1055,17 @@ arch_of_init(void) push_str("/options"); fword("find-device");
- /* Setup default boot devices (not overriding user settings) */ - fword("boot-device"); - boot_device = pop_fstr_copy(); - if (boot_device && strcmp(boot_device, "disk") == 0) { - switch (fw_cfg_read_i16(FW_CFG_BOOT_DEVICE)) { + /* Boot order */ + bootorder_file = fw_cfg_read_file("bootorder", &bootorder_sz); + + if (bootorder_file == NULL) { + /* No bootorder present, use fw_cfg device if no custom + boot-device specified */ + fword("boot-device"); + boot_device = pop_fstr_copy(); + + if (boot_device && strcmp(boot_device, "disk") == 0) { + switch (fw_cfg_read_i16(FW_CFG_BOOT_DEVICE)) { case 'c': boot_path = "hd"; break; @@ -1066,15 +1073,43 @@ arch_of_init(void) case 'd': boot_path = "cd"; break; + } + + snprintf(buf, sizeof(buf), + "%s:,\\:tbxi " + "%s:,\ppc\bootinfo.txt " + "%s:,%%BOOT", + boot_path, boot_path, boot_path); + + push_str(buf); + fword("encode-string"); + push_str("boot-device"); + fword("property"); }
- snprintf(buf, sizeof(buf), "%s:,\\:tbxi %s:,\ppc\bootinfo.txt %s:,%%BOOT", boot_path, boot_path, boot_path); - push_str(buf); + free(boot_device); + } else { + sz = bootorder_sz * (3 * 2); + boot_device = malloc(sz); + memset(boot_device, 0, sz); + + while ((boot_path = strsep(&bootorder_file, "\n")) != NULL) { + snprintf(buf, sizeof(buf), + "%s:,\\:tbxi " + "%s:,\ppc\bootinfo.txt " + "%s:,%%BOOT ", + boot_path, boot_path, boot_path); + + strncat(boot_device, buf, sz); + } + + push_str(boot_device); fword("encode-string"); push_str("boot-device"); fword("property"); + + free(boot_device); } - free(boot_device);
/* Set up other properties */
This provides an alternative mechanism for supporting boot device order information from QEMU compared with the legacy FW_CFG_BOOT_DEVICE functionality specified via -boot.
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk --- arch/sparc64/boot.c | 2 +- arch/sparc64/boot.h | 2 +- arch/sparc64/openbios.c | 38 ++++++++++++++++++++++++++++++++------ 3 files changed, 34 insertions(+), 8 deletions(-)
diff --git a/arch/sparc64/boot.c b/arch/sparc64/boot.c index 7a287f2..54f6f7a 100644 --- a/arch/sparc64/boot.c +++ b/arch/sparc64/boot.c @@ -15,7 +15,7 @@ uint64_t kernel_image; uint64_t kernel_size; uint64_t qemu_cmdline; uint64_t cmdline_size; -char boot_device; +char *boot_device;
extern int sparc64_of_client_interface( int *params );
diff --git a/arch/sparc64/boot.h b/arch/sparc64/boot.h index e1b8717..88beb1c 100644 --- a/arch/sparc64/boot.h +++ b/arch/sparc64/boot.h @@ -14,7 +14,7 @@ extern uint64_t kernel_image; extern uint64_t kernel_size; extern uint64_t qemu_cmdline; extern uint64_t cmdline_size; -extern char boot_device; +extern char *boot_device; extern void boot(void);
// sys_info.c diff --git a/arch/sparc64/openbios.c b/arch/sparc64/openbios.c index e9e08fd..b2e79d0 100644 --- a/arch/sparc64/openbios.c +++ b/arch/sparc64/openbios.c @@ -26,6 +26,7 @@ #include "arch/common/fw_cfg.h" #include "arch/sparc64/ofmem_sparc64.h" #include "spitfire.h" +#include "libc/vsprintf.h"
#define UUID_FMT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
@@ -538,6 +539,8 @@ void arch_nvram_get(char *data) uint32_t clock_frequency; uint16_t machine_id; const char *stdin_path, *stdout_path; + char *bootorder_file, *boot_path; + uint32_t bootorder_sz, sz;
fw_cfg_init();
@@ -570,7 +573,6 @@ void arch_nvram_get(char *data) } qemu_cmdline = (uint64_t)obio_cmdline; cmdline_size = size; - boot_device = fw_cfg_read_i16(FW_CFG_BOOT_DEVICE);
if (kernel_size) printk("kernel addr %llx size %llx\n", kernel_image, kernel_size); @@ -630,7 +632,11 @@ void arch_nvram_get(char *data) push_str("/options"); fword("find-device");
- switch (boot_device) { + /* Boot order */ + bootorder_file = fw_cfg_read_file("bootorder", &bootorder_sz); + + if (bootorder_file == NULL) { + switch (fw_cfg_read_i16(FW_CFG_BOOT_DEVICE)) { case 'a': push_str("/obio/SUNW,fdtwo"); break; @@ -644,11 +650,31 @@ void arch_nvram_get(char *data) case 'n': push_str("net"); break; - } + }
- fword("encode-string"); - push_str("boot-device"); - fword("property"); + fword("encode-string"); + push_str("boot-device"); + fword("property"); + } else { + sz = bootorder_sz * (3 * 2); + boot_device = malloc(sz); + memset(boot_device, 0, sz); + + while ((boot_path = strsep(&bootorder_file, "\n")) != NULL) { + snprintf(buf, sizeof(buf), + "%s:f " + "%s:a " + "%s ", + boot_path, boot_path, boot_path); + + strncat(boot_device, buf, sz); + } + + push_str(boot_device); + fword("encode-string"); + push_str("boot-device"); + fword("property"); + }
push_str(obio_cmdline); fword("encode-string");
Note that as part of this commit we rename the legacy 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@ilande.co.uk --- drivers/build.xml | 1 + drivers/pci.c | 63 ++++++ drivers/pci_database.c | 14 +- drivers/pci_database.h | 1 + drivers/virtio.c | 555 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/virtio.h | 370 +++++++++++++++++++++++++++++++ include/drivers/drivers.h | 5 + include/drivers/pci.h | 6 + 8 files changed, 1012 insertions(+), 3 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..43eb913 100644 --- a/drivers/pci.c +++ b/drivers/pci.c @@ -37,6 +37,9 @@ #ifdef CONFIG_DRIVER_USB #include "drivers/usb.h" #endif +#ifdef CONFIG_DRIVER_VIRTIO_BLK +#include "virtio.h" +#endif
#if defined (CONFIG_DEBUG_PCI) # define PCI_DPRINTF(format, ...) printk(format, ## __VA_ARGS__) @@ -770,6 +773,66 @@ 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, cap_idx, cap_vndr; + uint8_t cfg_type, bar; + uint16_t status; + uint32_t offset, notify_mult = 0; + uint64_t common_cfg = 0, device_cfg = 0, notify_base = 0; + + 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; + + /* Check PCI capabilties: if they don't exist then we're certainly not + a 1.0 device */ + status = pci_config_read16(addr, PCI_STATUS); + if (!(status & PCI_STATUS_CAP_LIST)) { + return 0; + } + + /* Locate VIRTIO_PCI_CAP_COMMON_CFG and VIRTIO_PCI_CAP_DEVICE_CFG */ + cap_idx = pci_config_read8(addr, PCI_CAPABILITY_LIST); + while ((cap_vndr = pci_config_read8(addr, cap_idx)) != 0) { + if (cap_vndr == PCI_CAP_ID_VNDR) { + cfg_type = pci_config_read8(addr, cap_idx + 0x3); + bar = pci_config_read8(addr, cap_idx + 0x4); + offset = pci_config_read32(addr, cap_idx + 0x8); + + switch (cfg_type) { + case VIRTIO_PCI_CAP_COMMON_CFG: + common_cfg = arch->host_pci_base + (config->assigned[bar] & ~0x0000000F) + offset; + break; + case VIRTIO_PCI_CAP_NOTIFY_CFG: + notify_base = arch->host_pci_base + (config->assigned[bar] & ~0x0000000F) + offset; + notify_mult = pci_config_read32(addr, cap_idx + 16); + break; + case VIRTIO_PCI_CAP_DEVICE_CFG: + device_cfg = arch->host_pci_base + (config->assigned[bar] & ~0x0000000F) + offset; + break; + } + } + + cap_idx = pci_config_read8(addr, cap_idx + 1); + } + + /* If we didn't find the required configuration then exit */ + if (common_cfg == 0 || device_cfg == 0 || notify_base == 0) { + return 0; + } + + ob_virtio_init(config->path, "virtio-blk", common_cfg, device_cfg, + notify_base, notify_mult, 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..14ebd29 100644 --- a/drivers/pci_database.c +++ b/drivers/pci_database.c @@ -47,12 +47,20 @@ static const pci_subclass_t undef_subclass[] = {
static const pci_dev_t scsi_devices[] = { { - /* Virtio-block controller */ + /* Legacy 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, + }, + { + /* Modern virtio-block controller */ + PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_DEVICE_ID_VIRTIO_BLOCK + 0x41, + NULL, "scsi", NULL, + "pci1af4,1042\0pci1af4,1042\0pciclass,01018f\0", + 0, 0, 0, + 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..171db5a --- /dev/null +++ b/drivers/virtio.c @@ -0,0 +1,555 @@ +/* + * OpenBIOS virtio-1.0 virtio-blk driver + * + * Copyright (c) 2013 Alexander Graf agraf@suse.de + * Copyright (c) 2018 Mark Cave-Ayland mark.cave-ayland@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(uint64_t cfg_addr, int addr) +{ + return in_8((uint8_t *)(uintptr_t)(cfg_addr + addr)); +} + +static void virtio_cfg_write8(uint64_t cfg_addr, int addr, uint8_t value) +{ + out_8((uint8_t *)(uintptr_t)(cfg_addr + addr), value); +} + +static uint16_t virtio_cfg_read16(uint64_t cfg_addr, int addr) +{ + return in_le16((uint16_t *)(uintptr_t)(cfg_addr + addr)); +} + +static void virtio_cfg_write16(uint64_t cfg_addr, int addr, uint16_t value) +{ + out_le16((uint16_t *)(uintptr_t)(cfg_addr + addr), value); +} + +static uint32_t virtio_cfg_read32(uint64_t cfg_addr, int addr) +{ + return in_le32((uint32_t *)(uintptr_t)(cfg_addr + addr)); +} + +static void virtio_cfg_write32(uint64_t cfg_addr, int addr, uint32_t value) +{ + out_le32((uint32_t *)(uintptr_t)(cfg_addr + addr), value); +} + +static uint64_t virtio_cfg_read64(uint64_t cfg_addr, int addr) +{ + uint64_t q = ((uint64_t)virtio_cfg_read32(cfg_addr + 4, addr) << 32); + q |= virtio_cfg_read32(cfg_addr, addr); + + return q; +} + +static void virtio_cfg_write64(uint64_t cfg_addr, int addr, uint64_t value) +{ + virtio_cfg_write32(cfg_addr, addr, (value & 0xffffffff)); + virtio_cfg_write32(cfg_addr, addr + 4, ((value >> 32) & 0xffffffff)); +} + +static long virtio_notify(VDev *vdev, int vq_idx, long cookie) +{ + uint16_t notify_offset = virtio_cfg_read16(vdev->common_cfg, VIRTIO_PCI_COMMON_Q_NOFF); + + virtio_cfg_write16(vdev->notify_base, notify_offset + + vq_idx * vdev->notify_mult, 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 = __cpu_to_le16(0); + vr->avail->idx = __cpu_to_le16(0); + + /* We're running with interrupts off anyways, so don't bother */ + vr->used->flags = __cpu_to_le16(VRING_USED_F_NO_NOTIFY); + vr->used->idx = __cpu_to_le16(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, uint64_t 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[__le16_to_cpu(vr->avail->idx) % vr->num] = __cpu_to_le16(vr->next_idx); + } + + vr->desc[vr->next_idx].addr = __cpu_to_le64(p); + vr->desc[vr->next_idx].len = __cpu_to_le32(len); + vr->desc[vr->next_idx].flags = __cpu_to_le16(flags & ~VRING_HIDDEN_IS_CHAIN); + vr->desc[vr->next_idx].next = __cpu_to_le16(vr->next_idx); + vr->desc[vr->next_idx].next = __cpu_to_le16(__le16_to_cpu(vr->desc[vr->next_idx].next) + 1); + vr->next_idx++; + + /* Chains only have a single ID */ + if (!(flags & VRING_DESC_F_NEXT)) { + vr->avail->idx = __cpu_to_le16(__le16_to_cpu(vr->avail->idx) + 1); + } +} + +static int vr_poll(VDev *vdev, VRing *vr) +{ + if (__le16_to_cpu(vr->used->idx) == vr->used_idx) { + vring_notify(vdev, vr); + return 0; + } + + vr->used_idx = __le16_to_cpu(vr->used->idx); + vr->next_idx = 0; + vr->desc[0].len = __cpu_to_le32(0); + vr->desc[0].flags = __cpu_to_le16(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 uint64_t vring_addr_translate(VDev *vdev, void *p) +{ + ucell mode; + uint64_t iova; + + iova = ofmem_translate(pointer2cell(p), &mode); + return iova; +} + +/*********************************************** + * 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 = __cpu_to_le32(VIRTIO_BLK_T_IN); + out_hdr.ioprio = __cpu_to_le32(99); + out_hdr.sector = __cpu_to_le64(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_configure_device(VDev *vdev) +{ + uint32_t feature; + uint8_t status; + int i; + + /* Indicate we recognise the device */ + status = virtio_cfg_read8(vdev->common_cfg, VIRTIO_PCI_COMMON_STATUS); + status |= VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER; + virtio_cfg_write8(vdev->common_cfg, VIRTIO_PCI_COMMON_STATUS, status); + + /* Negotiate features: acknowledge VIRTIO_F_VERSION_1 for 1.0 specification + little-endian access */ + virtio_cfg_write32(vdev->common_cfg, VIRTIO_PCI_COMMON_DFSELECT, 0x1); + virtio_cfg_write32(vdev->common_cfg, VIRTIO_PCI_COMMON_GFSELECT, 0x1); + feature = virtio_cfg_read32(vdev->common_cfg, VIRTIO_PCI_COMMON_DF); + feature &= (1ULL << (VIRTIO_F_VERSION_1 - 32)); + virtio_cfg_write32(vdev->common_cfg, VIRTIO_PCI_COMMON_GF, feature); + + status = virtio_cfg_read8(vdev->common_cfg, VIRTIO_PCI_COMMON_STATUS); + status |= VIRTIO_CONFIG_S_FEATURES_OK; + virtio_cfg_write8(vdev->common_cfg, VIRTIO_PCI_COMMON_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; + + 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->common_cfg, VIRTIO_PCI_COMMON_Q_SELECT, i); + info.num = virtio_cfg_read16(vdev->common_cfg, VIRTIO_PCI_COMMON_Q_SIZE); + + 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->device_cfg, 0); + + /* Set queue addresses */ + virtio_cfg_write64(vdev->common_cfg, VIRTIO_PCI_COMMON_Q_DESCLO, + vring_addr_translate(vdev, &vdev->vrings[i].desc[0])); + virtio_cfg_write64(vdev->common_cfg, VIRTIO_PCI_COMMON_Q_AVAILLO, + vring_addr_translate(vdev, &vdev->vrings[i].avail[0])); + virtio_cfg_write64(vdev->common_cfg, VIRTIO_PCI_COMMON_Q_USEDLO, + vring_addr_translate(vdev, &vdev->vrings[i].used[0])); + + /* Enable queue */ + virtio_cfg_write16(vdev->common_cfg, VIRTIO_PCI_COMMON_Q_ENABLE, 1); + } + + /* Initialisation complete */ + status |= VIRTIO_CONFIG_S_DRIVER_OK; + virtio_cfg_write8(vdev->common_cfg, VIRTIO_PCI_COMMON_STATUS, status); + + vdev->configured = 1; +} + +static void +ob_virtio_disk_open(VDev **_vdev) +{ + VDev *vdev = *_vdev; + phandle_t ph; + + vdev->pos = 0; + + if (!vdev->configured) { + ob_virtio_configure_device(vdev); + } + + /* 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 common_cfg, + uint64_t device_cfg, uint64_t notify_base, uint32_t notify_mult, + int idx) +{ + char buf[256]; + phandle_t ph; + ucell addr; + 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->common_cfg = common_cfg; + vdev->device_cfg = device_cfg; + vdev->notify_base = notify_base; + vdev->notify_mult = notify_mult; + vdev->configured = 0; + + 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); + + *_vdev = vdev; + feval("to my-self"); + + 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..64b49e7 --- /dev/null +++ b/drivers/virtio.h @@ -0,0 +1,370 @@ +/* + * Virtio driver bits + * + * Copyright (c) 2013 Alexander Graf agraf@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 +/* Driver has finished configuring features */ +#define VIRTIO_CONFIG_S_FEATURES_OK 8 +/* We've given up on this device. */ +#define VIRTIO_CONFIG_S_FAILED 0x80 + +/* v1.0 compliant. */ +#define VIRTIO_F_VERSION_1 32 + +/* Common configuration */ +#define VIRTIO_PCI_CAP_COMMON_CFG 1 +/* Notifications */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 +/* ISR Status */ +#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_COMMON_DFSELECT 0 +#define VIRTIO_PCI_COMMON_DF 4 +#define VIRTIO_PCI_COMMON_GFSELECT 8 +#define VIRTIO_PCI_COMMON_GF 12 +#define VIRTIO_PCI_COMMON_MSIX 16 +#define VIRTIO_PCI_COMMON_NUMQ 18 +#define VIRTIO_PCI_COMMON_STATUS 20 +#define VIRTIO_PCI_COMMON_CFGGENERATION 21 +#define VIRTIO_PCI_COMMON_Q_SELECT 22 +#define VIRTIO_PCI_COMMON_Q_SIZE 24 +#define VIRTIO_PCI_COMMON_Q_MSIX 26 +#define VIRTIO_PCI_COMMON_Q_ENABLE 28 +#define VIRTIO_PCI_COMMON_Q_NOFF 30 +#define VIRTIO_PCI_COMMON_Q_DESCLO 32 +#define VIRTIO_PCI_COMMON_Q_DESCHI 36 +#define VIRTIO_PCI_COMMON_Q_AVAILLO 40 +#define VIRTIO_PCI_COMMON_Q_AVAILHI 44 +#define VIRTIO_PCI_COMMON_Q_USEDLO 48 +#define VIRTIO_PCI_COMMON_Q_USEDHI 52 + +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 { + uint64_t common_cfg; + uint64_t device_cfg; + uint64_t notify_base; + uint32_t notify_mult; + uint64_t pos; + int configured; + 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..d162901 100644 --- a/include/drivers/drivers.h +++ b/include/drivers/drivers.h @@ -139,6 +139,11 @@ 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 common_cfg, + uint64_t device_cfg, uint64_t notify_base, uint32_t notify_mult, + int idx); +#endif int macio_get_nvram_size(void); void macio_nvram_put(char *buf); void macio_nvram_get(char *buf); diff --git a/include/drivers/pci.h b/include/drivers/pci.h index fc7573a..990d071 100644 --- a/include/drivers/pci.h +++ b/include/drivers/pci.h @@ -40,6 +40,12 @@ struct pci_arch_t {
extern const pci_arch_t *arch;
+/* Vendor-Specific */ +#define PCI_CAP_ID_VNDR 0x9 + +/* Offset of first capability list entry */ +#define PCI_CAPABILITY_LIST 0x34 + /* Device tree offsets */
#define PCI_INT_MAP_PCI0 0
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk --- config/examples/ppc_config.xml | 1 + config/examples/sparc64_config.xml | 1 + 2 files changed, 2 insertions(+)
diff --git a/config/examples/ppc_config.xml b/config/examples/ppc_config.xml index 43277a0..19dc043 100644 --- a/config/examples/ppc_config.xml +++ b/config/examples/ppc_config.xml @@ -85,3 +85,4 @@ <option name="CONFIG_DEBUG_USB" type="boolean" value="false"/> <option name="CONFIG_USB_HID" type="boolean" value="true"/> <option name="CONFIG_DRIVER_LSI_53C810" type="boolean" value="true"/> + <option name="CONFIG_DRIVER_VIRTIO_BLK" type="boolean" value="true"/> diff --git a/config/examples/sparc64_config.xml b/config/examples/sparc64_config.xml index a4e1336..a4172df 100644 --- a/config/examples/sparc64_config.xml +++ b/config/examples/sparc64_config.xml @@ -71,4 +71,5 @@ <option name="CONFIG_DRIVER_PC_KBD" type="boolean" value="true"/> <option name="CONFIG_DRIVER_PC_SERIAL" type="boolean" value="true"/> <option name="CONFIG_DRIVER_FW_CFG" type="boolean" value="true"/> + <option name="CONFIG_DRIVER_VIRTIO_BLK" type="boolean" value="true"/> <option name="CONFIG_FW_CFG_ADDR" type="integer" value="0x510"/>
On 26/08/18 16:14, Mark Cave-Ayland wrote:
These patches add support for booting from virtio-blk (PCI) devices as used by QEMU.
Patches 1 and 2 add bootindex support which is the new way for passing boot order information from QEMU to OpenBIOS. Note that there are also corresponding patches for QEMU required, without which the generated device paths will be incorrect and cause virtio boot to fail.
Patch 3 adds the virtio-1.0 compliant virtio-blk PCI driver.
Patch 4 enables the new driver for both PPC and SPARC64 architectures so that virtio-blk-pci devices will be usable out-of-the-box with QEMU.
Once these patches have been applied to OpenBIOS (along with the corresponding fw path fixes for QEMU) it is possible to boot from virtio-blk-pci devices like this:
PPC:
./qemu-system-ppc -drive file=debian-9.0-powerpc-NETINST.iso,if=none,index=0,id=cd,media=cdrom \ -device virtio-blk-pci,drive=cd,bootindex=0 -m 256 -boot d
SPARC:
./qemu-system-sparc64 -drive debian-9.0-sparc64-NETINST.iso,if=none,index=0,id=cd,media=cdrom \ -device virtio-blk-pci,bus=pciB,drive=cd,bootindex=0 -m 256 -boot d -nographic
Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk
v3:
- Rebase onto master
- Convert from legacy driver to virtio-1.0 driver
v2:
- Switch over to using dma-* words to allocate rings and descriptors
- Fix up some minor whitespace issues
Mark Cave-Ayland (4): ppc: add bootindex support SPARC64: add bootindex support drivers: add virtio-1.0 virtio-blk driver config: enable virtio-blk driver for default PPC and SPARC64 builds
arch/ppc/qemu/init.c | 55 +++- arch/sparc64/boot.c | 2 +- arch/sparc64/boot.h | 2 +- arch/sparc64/openbios.c | 38 ++- config/examples/ppc_config.xml | 1 + config/examples/sparc64_config.xml | 1 + drivers/build.xml | 1 + drivers/pci.c | 63 +++++ drivers/pci_database.c | 14 +- drivers/pci_database.h | 1 + drivers/virtio.c | 555 +++++++++++++++++++++++++++++++++++++ drivers/virtio.h | 370 +++++++++++++++++++++++++ include/drivers/drivers.h | 5 + include/drivers/pci.h | 6 + 14 files changed, 1093 insertions(+), 21 deletions(-) create mode 100644 drivers/virtio.c create mode 100644 drivers/virtio.h
Applied to master.
ATB,
Mark.