blob: 8378a3445dea43a4c29ba3a861286782ca5d7fbb [file] [log] [blame]
Gleb Natapov89acfa32010-05-10 11:36:37 +03001// Virtio block boot support.
2//
3// Copyright (C) 2010 Red Hat Inc.
4//
5// Authors:
6// Gleb Natapov <gnatapov@redhat.com>
7//
8// This file may be distributed under the terms of the GNU LGPLv3 license.
9
Kevin O'Connor1902c942013-10-26 11:48:06 -040010#include "biosvar.h" // GET_GLOBALFLAT
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040011#include "config.h" // CONFIG_*
Kevin O'Connor135f3f62013-09-14 23:57:26 -040012#include "block.h" // struct drive_s
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040013#include "malloc.h" // free
14#include "output.h" // dprintf
15#include "pci.h" // foreachpci
Kevin O'Connor7d09d0e2010-05-10 21:51:38 -040016#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK
17#include "pci_regs.h" // PCI_VENDOR_ID
Kevin O'Connor135f3f62013-09-14 23:57:26 -040018#include "std/disk.h" // DISK_RET_SUCCESS
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040019#include "string.h" // memset
20#include "util.h" // usleep
Gleb Natapov89acfa32010-05-10 11:36:37 +030021#include "virtio-pci.h"
Kevin O'Connor7d09d0e2010-05-10 21:51:38 -040022#include "virtio-ring.h"
Gleb Natapov89acfa32010-05-10 11:36:37 +030023#include "virtio-blk.h"
Gleb Natapov89acfa32010-05-10 11:36:37 +030024
25struct virtiodrive_s {
26 struct drive_s drive;
27 struct vring_virtqueue *vq;
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +020028 struct vp_device vp;
Gleb Natapov89acfa32010-05-10 11:36:37 +030029};
30
31static int
Gleb Natapov5feb83c2010-08-23 10:23:45 +030032virtio_blk_op(struct disk_op_s *op, int write)
Gleb Natapov89acfa32010-05-10 11:36:37 +030033{
Kevin O'Connor1902c942013-10-26 11:48:06 -040034 struct virtiodrive_s *vdrive_gf =
35 container_of(op->drive_gf, struct virtiodrive_s, drive);
36 struct vring_virtqueue *vq = GET_GLOBALFLAT(vdrive_gf->vq);
Gleb Natapov89acfa32010-05-10 11:36:37 +030037 struct virtio_blk_outhdr hdr = {
Gleb Natapov5feb83c2010-08-23 10:23:45 +030038 .type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN,
Gleb Natapov89acfa32010-05-10 11:36:37 +030039 .ioprio = 0,
40 .sector = op->lba,
41 };
42 u8 status = VIRTIO_BLK_S_UNSUPP;
43 struct vring_list sg[] = {
44 {
Kevin O'Connor7a08ae72012-03-14 21:11:39 -040045 .addr = MAKE_FLATPTR(GET_SEG(SS), &hdr),
46 .length = sizeof(hdr),
Gleb Natapov89acfa32010-05-10 11:36:37 +030047 },
48 {
Kevin O'Connor7a08ae72012-03-14 21:11:39 -040049 .addr = op->buf_fl,
Kevin O'Connor1902c942013-10-26 11:48:06 -040050 .length = GET_GLOBALFLAT(vdrive_gf->drive.blksize) * op->count,
Gleb Natapov89acfa32010-05-10 11:36:37 +030051 },
52 {
Kevin O'Connor7a08ae72012-03-14 21:11:39 -040053 .addr = MAKE_FLATPTR(GET_SEG(SS), &status),
54 .length = sizeof(status),
Gleb Natapov89acfa32010-05-10 11:36:37 +030055 },
56 };
57
58 /* Add to virtqueue and kick host */
Gleb Natapov5feb83c2010-08-23 10:23:45 +030059 if (write)
60 vring_add_buf(vq, sg, 2, 1, 0, 0);
61 else
62 vring_add_buf(vq, sg, 1, 2, 0, 0);
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +020063 vring_kick(&vdrive_gf->vp, vq, 1);
Gleb Natapov89acfa32010-05-10 11:36:37 +030064
65 /* Wait for reply */
66 while (!vring_more_used(vq))
Kevin O'Connorea8ac632010-05-16 11:34:38 -040067 usleep(5);
Gleb Natapov89acfa32010-05-10 11:36:37 +030068
69 /* Reclaim virtqueue element */
70 vring_get_buf(vq, NULL);
Stefan Hajnoczi4e0daae2010-07-07 13:34:22 +010071
72 /* Clear interrupt status register. Avoid leaving interrupts stuck if
73 * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised.
74 */
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +020075 vp_get_isr(&vdrive_gf->vp);
Stefan Hajnoczi4e0daae2010-07-07 13:34:22 +010076
Gleb Natapov89acfa32010-05-10 11:36:37 +030077 return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK;
78}
79
Gerd Hoffmann1d9e87b2015-06-26 09:44:00 +020080int VISIBLE32FLAT
Paolo Bonzini0e7fb5f2011-11-16 13:02:55 +010081process_virtio_blk_op(struct disk_op_s *op)
Gleb Natapov89acfa32010-05-10 11:36:37 +030082{
Kevin O'Connor897fb112013-02-07 23:32:48 -050083 if (! CONFIG_VIRTIO_BLK)
Kevin O'Connor2515a722010-05-23 10:19:03 -040084 return 0;
Gleb Natapov89acfa32010-05-10 11:36:37 +030085 switch (op->command) {
86 case CMD_READ:
Gleb Natapov5feb83c2010-08-23 10:23:45 +030087 return virtio_blk_op(op, 0);
Gleb Natapov89acfa32010-05-10 11:36:37 +030088 case CMD_WRITE:
Gleb Natapov5feb83c2010-08-23 10:23:45 +030089 return virtio_blk_op(op, 1);
90 case CMD_FORMAT:
Gleb Natapov89acfa32010-05-10 11:36:37 +030091 case CMD_RESET:
92 case CMD_ISREADY:
93 case CMD_VERIFY:
94 case CMD_SEEK:
95 return DISK_RET_SUCCESS;
96 default:
Gleb Natapov89acfa32010-05-10 11:36:37 +030097 return DISK_RET_EPARAM;
98 }
99}
100
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400101static void
Kevin O'Connordc3a7d62011-07-09 14:33:56 -0400102init_virtio_blk(struct pci_device *pci)
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400103{
Kevin O'Connordc3a7d62011-07-09 14:33:56 -0400104 u16 bdf = pci->bdf;
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400105 dprintf(1, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf),
106 pci_bdf_to_dev(bdf));
Kevin O'Connor1902c942013-10-26 11:48:06 -0400107 struct virtiodrive_s *vdrive = malloc_fseg(sizeof(*vdrive));
108 if (!vdrive) {
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400109 warn_noalloc();
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100110 return;
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400111 }
Kevin O'Connor1902c942013-10-26 11:48:06 -0400112 memset(vdrive, 0, sizeof(*vdrive));
113 vdrive->drive.type = DTYPE_VIRTIO_BLK;
114 vdrive->drive.cntl_id = bdf;
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400115
Gerd Hoffmann74d0cdc2015-06-25 10:24:27 +0200116 vp_init_simple(&vdrive->vp, pci);
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200117 if (vp_find_vq(&vdrive->vp, 0, &vdrive->vq) < 0 ) {
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400118 dprintf(1, "fail to find vq for virtio-blk %x:%x\n",
119 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
120 goto fail;
121 }
122
123 struct virtio_blk_config cfg;
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200124 vp_get(&vdrive->vp, 0, &cfg, sizeof(cfg));
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400125
Gerd Hoffmann46d17922015-06-25 12:28:46 +0200126 u64 f = vp_get_features(&vdrive->vp);
Kevin O'Connor1902c942013-10-26 11:48:06 -0400127 vdrive->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ?
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400128 cfg.blk_size : DISK_SECTOR_SIZE;
129
Kevin O'Connor1902c942013-10-26 11:48:06 -0400130 vdrive->drive.sectors = cfg.capacity;
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400131 dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n",
132 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
Kevin O'Connor1902c942013-10-26 11:48:06 -0400133 vdrive->drive.blksize, (u32)vdrive->drive.sectors);
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400134
Kevin O'Connor1902c942013-10-26 11:48:06 -0400135 if (vdrive->drive.blksize != DISK_SECTOR_SIZE) {
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400136 dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n",
137 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
Kevin O'Connor1902c942013-10-26 11:48:06 -0400138 vdrive->drive.blksize);
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400139 goto fail;
140 }
141
Kevin O'Connor1902c942013-10-26 11:48:06 -0400142 vdrive->drive.pchs.cylinder = cfg.cylinders;
143 vdrive->drive.pchs.head = cfg.heads;
144 vdrive->drive.pchs.sector = cfg.sectors;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500145 char *desc = znprintf(MAXDESCSIZE, "Virtio disk PCI:%x:%x",
146 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400147
Kevin O'Connor1902c942013-10-26 11:48:06 -0400148 boot_add_hd(&vdrive->drive, desc, bootprio_find_pci_device(pci));
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400149
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200150 vp_set_status(&vdrive->vp, VIRTIO_CONFIG_S_ACKNOWLEDGE |
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400151 VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
152 return;
153
154fail:
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200155 vp_reset(&vdrive->vp);
Kevin O'Connor1902c942013-10-26 11:48:06 -0400156 free(vdrive->vq);
157 free(vdrive);
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400158}
159
Gleb Natapov89acfa32010-05-10 11:36:37 +0300160void
161virtio_blk_setup(void)
162{
163 ASSERT32FLAT();
Kevin O'Connor897fb112013-02-07 23:32:48 -0500164 if (! CONFIG_VIRTIO_BLK)
Gleb Natapov89acfa32010-05-10 11:36:37 +0300165 return;
166
167 dprintf(3, "init virtio-blk\n");
168
Kevin O'Connor5d74ce02011-06-20 22:22:08 -0400169 struct pci_device *pci;
170 foreachpci(pci) {
171 if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET
172 || pci->device != PCI_DEVICE_ID_VIRTIO_BLK)
Gleb Natapov89acfa32010-05-10 11:36:37 +0300173 continue;
Kevin O'Connordc3a7d62011-07-09 14:33:56 -0400174 init_virtio_blk(pci);
Gleb Natapov89acfa32010-05-10 11:36:37 +0300175 }
176}