#include "biosvar.h" // GET_GLOBAL
#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK
#include "pci_regs.h" // PCI_VENDOR_ID
-#include "boot.h" // add_bcv_internal
+#include "boot.h" // boot_add_hd
#include "virtio-pci.h"
#include "virtio-ring.h"
#include "virtio-blk.h"
};
static int
-virtio_blk_read(struct disk_op_s *op)
+virtio_blk_op(struct disk_op_s *op, int write)
{
struct virtiodrive_s *vdrive_g =
container_of(op->drive_g, struct virtiodrive_s, drive);
struct vring_virtqueue *vq = GET_GLOBAL(vdrive_g->vq);
struct virtio_blk_outhdr hdr = {
- .type = VIRTIO_BLK_T_IN,
+ .type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN,
.ioprio = 0,
.sector = op->lba,
};
};
/* Add to virtqueue and kick host */
- vring_add_buf(vq, sg, 1, 2, 0, 0);
+ if (write)
+ vring_add_buf(vq, sg, 2, 1, 0, 0);
+ else
+ vring_add_buf(vq, sg, 1, 2, 0, 0);
vring_kick(GET_GLOBAL(vdrive_g->ioaddr), vq, 1);
/* Wait for reply */
while (!vring_more_used(vq))
- udelay(5);
+ usleep(5);
/* Reclaim virtqueue element */
vring_get_buf(vq, NULL);
+
+ /* Clear interrupt status register. Avoid leaving interrupts stuck if
+ * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised.
+ */
+ vp_get_isr(GET_GLOBAL(vdrive_g->ioaddr));
+
return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK;
}
int
-process_virtio_op(struct disk_op_s *op)
+process_virtio_blk_op(struct disk_op_s *op)
{
+ if (! CONFIG_VIRTIO_BLK || CONFIG_COREBOOT)
+ return 0;
switch (op->command) {
case CMD_READ:
- return virtio_blk_read(op);
- case CMD_FORMAT:
+ return virtio_blk_op(op, 0);
case CMD_WRITE:
- return DISK_RET_EWRITEPROTECT;
+ return virtio_blk_op(op, 1);
+ case CMD_FORMAT:
case CMD_RESET:
case CMD_ISREADY:
case CMD_VERIFY:
}
}
-void
-virtio_blk_setup(void)
+static void
+init_virtio_blk(struct pci_device *pci)
{
- ASSERT32FLAT();
- if (! CONFIG_VIRTIO_BLK)
+ u16 bdf = pci->bdf;
+ dprintf(1, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf),
+ pci_bdf_to_dev(bdf));
+ struct virtiodrive_s *vdrive_g = malloc_fseg(sizeof(*vdrive_g));
+ if (!vdrive_g) {
+ warn_noalloc();
return;
+ }
+ memset(vdrive_g, 0, sizeof(*vdrive_g));
+ vdrive_g->drive.type = DTYPE_VIRTIO_BLK;
+ vdrive_g->drive.cntl_id = bdf;
+
+ u16 ioaddr = vp_init_simple(bdf);
+ vdrive_g->ioaddr = ioaddr;
+ if (vp_find_vq(ioaddr, 0, &vdrive_g->vq) < 0 ) {
+ dprintf(1, "fail to find vq for virtio-blk %x:%x\n",
+ pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
+ goto fail;
+ }
- dprintf(3, "init virtio-blk\n");
+ struct virtio_blk_config cfg;
+ vp_get(ioaddr, 0, &cfg, sizeof(cfg));
- int bdf, max;
- u32 id = PCI_VENDOR_ID_REDHAT_QUMRANET | (PCI_DEVICE_ID_VIRTIO_BLK << 16);
- foreachpci(bdf, max) {
- u32 v = pci_config_readl(bdf, PCI_VENDOR_ID);
- if (v != id)
- continue;
- dprintf(3, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf),
- pci_bdf_to_dev(bdf));
- char *desc = malloc_tmphigh(MAXDESCSIZE);
- struct virtiodrive_s *vdrive_g = malloc_fseg(sizeof(*vdrive_g));
- struct vring_virtqueue *vq = malloc_low(sizeof(*vq));
- if (!vdrive_g || !desc || !vq) {
- free(vdrive_g);
- free(desc);
- free(vq);
- warn_noalloc();
- return;
- }
- memset(vdrive_g, 0, sizeof(*vdrive_g));
- vdrive_g->drive.type = DTYPE_VIRTIO;
- vdrive_g->drive.cntl_id = bdf;
- vdrive_g->vq = vq;
-
- u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) &
- PCI_BASE_ADDRESS_IO_MASK;
-
- vdrive_g->ioaddr = ioaddr;
-
- vp_reset(ioaddr);
- vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
- VIRTIO_CONFIG_S_DRIVER );
-
- if (vp_find_vq(ioaddr, 0, vdrive_g->vq) < 0 ) {
- free(vdrive_g);
- free(desc);
- free(vq);
- dprintf(1, "fail to find vq for virtio-blk %x:%x\n",
- pci_bdf_to_bus (bdf), pci_bdf_to_dev(bdf));
- continue;
- }
+ u32 f = vp_get_features(ioaddr);
+ vdrive_g->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ?
+ cfg.blk_size : DISK_SECTOR_SIZE;
- struct virtio_blk_config cfg;
- vp_get(ioaddr, 0, &cfg, sizeof(cfg));
+ vdrive_g->drive.sectors = cfg.capacity;
+ dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n",
+ pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
+ vdrive_g->drive.blksize, (u32)vdrive_g->drive.sectors);
- vdrive_g->drive.blksize = cfg.blk_size;
- vdrive_g->drive.sectors = cfg.capacity;
- dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n",
- pci_bdf_to_bus (bdf), pci_bdf_to_dev(bdf),
- vdrive_g->drive.blksize, (u32)vdrive_g->drive.sectors);
+ if (vdrive_g->drive.blksize != DISK_SECTOR_SIZE) {
+ dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n",
+ pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
+ vdrive_g->drive.blksize);
+ goto fail;
+ }
+
+ vdrive_g->drive.pchs.cylinders = cfg.cylinders;
+ vdrive_g->drive.pchs.heads = cfg.heads;
+ vdrive_g->drive.pchs.spt = cfg.sectors;
+ char *desc = znprintf(MAXDESCSIZE, "Virtio disk PCI:%x:%x",
+ pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
+
+ boot_add_hd(&vdrive_g->drive, desc, bootprio_find_pci_device(pci));
- vdrive_g->drive.pchs.cylinders = cfg.cylinders;
- vdrive_g->drive.pchs.heads = cfg.heads;
- vdrive_g->drive.pchs.spt = cfg.sectors;
+ vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
+ VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
+ return;
- setup_translation(&vdrive_g->drive);
- add_bcv_internal(&vdrive_g->drive);
+fail:
+ free(vdrive_g->vq);
+ free(vdrive_g);
+}
- snprintf(desc, MAXDESCSIZE, "Virtio disk PCI:%x:%x",
- pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
+void
+virtio_blk_setup(void)
+{
+ ASSERT32FLAT();
+ if (! CONFIG_VIRTIO_BLK || CONFIG_COREBOOT)
+ return;
- vdrive_g->drive.desc = desc;
+ dprintf(3, "init virtio-blk\n");
- vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
- VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
+ struct pci_device *pci;
+ foreachpci(pci) {
+ if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET
+ || pci->device != PCI_DEVICE_ID_VIRTIO_BLK)
+ continue;
+ init_virtio_blk(pci);
}
}
-