blob: 0290d671cf4e47c76686966d286141ff613d3a08 [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;
28 u16 ioaddr;
29};
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);
Kevin O'Connor1902c942013-10-26 11:48:06 -040063 vring_kick(GET_GLOBALFLAT(vdrive_gf->ioaddr), 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 */
Kevin O'Connor1902c942013-10-26 11:48:06 -040075 vp_get_isr(GET_GLOBALFLAT(vdrive_gf->ioaddr));
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
80int
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:
97 op->count = 0;
98 return DISK_RET_EPARAM;
99 }
100}
101
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400102static void
Kevin O'Connordc3a7d62011-07-09 14:33:56 -0400103init_virtio_blk(struct pci_device *pci)
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400104{
Kevin O'Connordc3a7d62011-07-09 14:33:56 -0400105 u16 bdf = pci->bdf;
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400106 dprintf(1, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf),
107 pci_bdf_to_dev(bdf));
Kevin O'Connor1902c942013-10-26 11:48:06 -0400108 struct virtiodrive_s *vdrive = malloc_fseg(sizeof(*vdrive));
109 if (!vdrive) {
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400110 warn_noalloc();
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100111 return;
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400112 }
Kevin O'Connor1902c942013-10-26 11:48:06 -0400113 memset(vdrive, 0, sizeof(*vdrive));
114 vdrive->drive.type = DTYPE_VIRTIO_BLK;
115 vdrive->drive.cntl_id = bdf;
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400116
Paolo Bonzini4e343322011-11-16 13:02:56 +0100117 u16 ioaddr = vp_init_simple(bdf);
Kevin O'Connor1902c942013-10-26 11:48:06 -0400118 vdrive->ioaddr = ioaddr;
119 if (vp_find_vq(ioaddr, 0, &vdrive->vq) < 0 ) {
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400120 dprintf(1, "fail to find vq for virtio-blk %x:%x\n",
121 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
122 goto fail;
123 }
124
125 struct virtio_blk_config cfg;
126 vp_get(ioaddr, 0, &cfg, sizeof(cfg));
127
128 u32 f = vp_get_features(ioaddr);
Kevin O'Connor1902c942013-10-26 11:48:06 -0400129 vdrive->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ?
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400130 cfg.blk_size : DISK_SECTOR_SIZE;
131
Kevin O'Connor1902c942013-10-26 11:48:06 -0400132 vdrive->drive.sectors = cfg.capacity;
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400133 dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n",
134 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
Kevin O'Connor1902c942013-10-26 11:48:06 -0400135 vdrive->drive.blksize, (u32)vdrive->drive.sectors);
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400136
Kevin O'Connor1902c942013-10-26 11:48:06 -0400137 if (vdrive->drive.blksize != DISK_SECTOR_SIZE) {
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400138 dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n",
139 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
Kevin O'Connor1902c942013-10-26 11:48:06 -0400140 vdrive->drive.blksize);
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400141 goto fail;
142 }
143
Kevin O'Connor1902c942013-10-26 11:48:06 -0400144 vdrive->drive.pchs.cylinder = cfg.cylinders;
145 vdrive->drive.pchs.head = cfg.heads;
146 vdrive->drive.pchs.sector = cfg.sectors;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500147 char *desc = znprintf(MAXDESCSIZE, "Virtio disk PCI:%x:%x",
148 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400149
Kevin O'Connor1902c942013-10-26 11:48:06 -0400150 boot_add_hd(&vdrive->drive, desc, bootprio_find_pci_device(pci));
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400151
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400152 vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
153 VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
154 return;
155
156fail:
Kevin O'Connor1902c942013-10-26 11:48:06 -0400157 free(vdrive->vq);
158 free(vdrive);
Kevin O'Connorc4fe1352010-05-20 00:22:02 -0400159}
160
Gleb Natapov89acfa32010-05-10 11:36:37 +0300161void
162virtio_blk_setup(void)
163{
164 ASSERT32FLAT();
Kevin O'Connor897fb112013-02-07 23:32:48 -0500165 if (! CONFIG_VIRTIO_BLK)
Gleb Natapov89acfa32010-05-10 11:36:37 +0300166 return;
167
168 dprintf(3, "init virtio-blk\n");
169
Kevin O'Connor5d74ce02011-06-20 22:22:08 -0400170 struct pci_device *pci;
171 foreachpci(pci) {
172 if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET
173 || pci->device != PCI_DEVICE_ID_VIRTIO_BLK)
Gleb Natapov89acfa32010-05-10 11:36:37 +0300174 continue;
Kevin O'Connordc3a7d62011-07-09 14:33:56 -0400175 init_virtio_blk(pci);
Gleb Natapov89acfa32010-05-10 11:36:37 +0300176 }
177}