blob: 4eea5c3902b2f1a5d0cd635edf6ab423f92e25a4 [file] [log] [blame]
Paolo Bonzinic5c488f2012-02-27 17:22:23 +01001// Virtio SCSI boot support.
2//
3// Copyright (C) 2012 Red Hat Inc.
4//
5// Authors:
6// Paolo Bonzini <pbonzini@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'Connor135f3f62013-09-14 23:57:26 -040011#include "block.h" // struct drive_s
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040012#include "blockcmd.h" // scsi_drive_setup
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040013#include "config.h" // CONFIG_*
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040014#include "malloc.h" // free
15#include "output.h" // dprintf
Kevin O'Connor4d8510c2016-02-03 01:28:20 -050016#include "pcidevice.h" // foreachpci
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010017#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK
18#include "pci_regs.h" // PCI_VENDOR_ID
Kevin O'Connorb35ea542016-04-01 20:05:45 -040019#include "stacks.h" // run_thread
Kevin O'Connor135f3f62013-09-14 23:57:26 -040020#include "std/disk.h" // DISK_RET_SUCCESS
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040021#include "string.h" // memset
22#include "util.h" // usleep
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010023#include "virtio-pci.h"
24#include "virtio-ring.h"
25#include "virtio-scsi.h"
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010026
27struct virtio_lun_s {
28 struct drive_s drive;
29 struct pci_device *pci;
30 struct vring_virtqueue *vq;
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +020031 struct vp_device *vp;
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010032 u16 target;
33 u16 lun;
34};
35
Kevin O'Connore0eb5a02015-07-07 12:19:53 -040036int
37virtio_scsi_process_op(struct disk_op_s *op)
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010038{
Kevin O'Connore0eb5a02015-07-07 12:19:53 -040039 if (! CONFIG_VIRTIO_SCSI)
40 return 0;
41 struct virtio_lun_s *vlun =
42 container_of(op->drive_gf, struct virtio_lun_s, drive);
43 struct vp_device *vp = vlun->vp;
44 struct vring_virtqueue *vq = vlun->vq;
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010045 struct virtio_scsi_req_cmd req;
46 struct virtio_scsi_resp_cmd resp;
47 struct vring_list sg[3];
48
49 memset(&req, 0, sizeof(req));
Kevin O'Connore0eb5a02015-07-07 12:19:53 -040050 int blocksize = scsi_fill_cmd(op, req.cdb, 16);
51 if (blocksize < 0)
52 return default_process_op(op);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010053 req.lun[0] = 1;
Kevin O'Connore0eb5a02015-07-07 12:19:53 -040054 req.lun[1] = vlun->target;
55 req.lun[2] = (vlun->lun >> 8) | 0x40;
56 req.lun[3] = (vlun->lun & 0xff);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010057
Kevin O'Connor1fd9a892012-03-14 21:27:45 -040058 u32 len = op->count * blocksize;
Kevin O'Connor5dcd1ee2015-07-07 14:43:01 -040059 int datain = scsi_is_read(op);
Paolo Bonzini8c313072012-03-16 18:54:46 +010060 int in_num = (datain ? 2 : 1);
61 int out_num = (len ? 3 : 2) - in_num;
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010062
Gerd Hoffmannb7dd90f2015-07-01 14:38:59 +020063 sg[0].addr = (void*)(&req);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010064 sg[0].length = sizeof(req);
65
Gerd Hoffmannb7dd90f2015-07-01 14:38:59 +020066 sg[out_num].addr = (void*)(&resp);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010067 sg[out_num].length = sizeof(resp);
68
Paolo Bonzini8c313072012-03-16 18:54:46 +010069 if (len) {
70 int data_idx = (datain ? 2 : 1);
71 sg[data_idx].addr = op->buf_fl;
72 sg[data_idx].length = len;
73 }
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010074
75 /* Add to virtqueue and kick host */
76 vring_add_buf(vq, sg, out_num, in_num, 0, 0);
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +020077 vring_kick(vp, vq, 1);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010078
79 /* Wait for reply */
80 while (!vring_more_used(vq))
81 usleep(5);
82
83 /* Reclaim virtqueue element */
84 vring_get_buf(vq, NULL);
85
86 /* Clear interrupt status register. Avoid leaving interrupts stuck if
87 * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised.
88 */
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +020089 vp_get_isr(vp);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +010090
91 if (resp.response == VIRTIO_SCSI_S_OK && resp.status == 0) {
92 return DISK_RET_SUCCESS;
93 }
94 return DISK_RET_EBADTRACK;
95}
96
Roman Kagan69ebdef2017-04-26 17:18:03 +030097static void
98virtio_scsi_init_lun(struct virtio_lun_s *vlun, struct pci_device *pci,
99 struct vp_device *vp, struct vring_virtqueue *vq,
100 u16 target, u16 lun)
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100101{
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100102 memset(vlun, 0, sizeof(*vlun));
103 vlun->drive.type = DTYPE_VIRTIO_SCSI;
104 vlun->drive.cntl_id = pci->bdf;
105 vlun->pci = pci;
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200106 vlun->vp = vp;
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100107 vlun->vq = vq;
108 vlun->target = target;
109 vlun->lun = lun;
Roman Kagan69ebdef2017-04-26 17:18:03 +0300110}
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100111
Roman Kagan69ebdef2017-04-26 17:18:03 +0300112static int
113virtio_scsi_add_lun(u32 lun, struct drive_s *tmpl_drv)
114{
115 struct virtio_lun_s *tmpl_vlun =
116 container_of(tmpl_drv, struct virtio_lun_s, drive);
117 struct virtio_lun_s *vlun = malloc_fseg(sizeof(*vlun));
118 if (!vlun) {
119 warn_noalloc();
120 return -1;
121 }
122 virtio_scsi_init_lun(vlun, tmpl_vlun->pci, tmpl_vlun->vp, tmpl_vlun->vq,
123 tmpl_vlun->target, lun);
124
125 int prio = bootprio_find_scsi_device(vlun->pci, vlun->target, vlun->lun);
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500126 int ret = scsi_drive_setup(&vlun->drive, "virtio-scsi", prio);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100127 if (ret)
128 goto fail;
129 return 0;
130
131fail:
132 free(vlun);
133 return -1;
134}
135
136static int
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200137virtio_scsi_scan_target(struct pci_device *pci, struct vp_device *vp,
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100138 struct vring_virtqueue *vq, u16 target)
139{
Roman Kagan69ebdef2017-04-26 17:18:03 +0300140
141 struct virtio_lun_s vlun0;
142
143 virtio_scsi_init_lun(&vlun0, pci, vp, vq, target, 0);
144
145 int ret = scsi_rep_luns_scan(&vlun0.drive, virtio_scsi_add_lun);
146 return ret < 0 ? 0 : ret;
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100147}
148
149static void
Kevin O'Connorb35ea542016-04-01 20:05:45 -0400150init_virtio_scsi(void *data)
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100151{
Kevin O'Connorb35ea542016-04-01 20:05:45 -0400152 struct pci_device *pci = data;
Kevin O'Connor7b673002016-02-03 03:03:15 -0500153 dprintf(1, "found virtio-scsi at %pP\n", pci);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100154 struct vring_virtqueue *vq = NULL;
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200155 struct vp_device *vp = malloc_high(sizeof(*vp));
156 if (!vp) {
157 warn_noalloc();
158 return;
159 }
Gerd Hoffmann74d0cdc2015-06-25 10:24:27 +0200160 vp_init_simple(vp, pci);
Gerd Hoffmann604a6f12015-06-26 10:22:55 +0200161 u8 status = VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER;
162
163 if (vp->use_modern) {
164 u64 features = vp_get_features(vp);
165 u64 version1 = 1ull << VIRTIO_F_VERSION_1;
Jason Wangdd9bba52017-07-05 15:49:51 +0800166 u64 iommu_platform = 1ull << VIRTIO_F_IOMMU_PLATFORM;
Gerd Hoffmann604a6f12015-06-26 10:22:55 +0200167 if (!(features & version1)) {
Kevin O'Connor7b673002016-02-03 03:03:15 -0500168 dprintf(1, "modern device without virtio_1 feature bit: %pP\n", pci);
Gerd Hoffmann604a6f12015-06-26 10:22:55 +0200169 goto fail;
170 }
171
Jason Wangdd9bba52017-07-05 15:49:51 +0800172 vp_set_features(vp, features & (version1 | iommu_platform));
Gerd Hoffmann604a6f12015-06-26 10:22:55 +0200173 status |= VIRTIO_CONFIG_S_FEATURES_OK;
174 vp_set_status(vp, status);
175 if (!(vp_get_status(vp) & VIRTIO_CONFIG_S_FEATURES_OK)) {
Kevin O'Connor7b673002016-02-03 03:03:15 -0500176 dprintf(1, "device didn't accept features: %pP\n", pci);
Gerd Hoffmann604a6f12015-06-26 10:22:55 +0200177 goto fail;
178 }
179 }
180
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200181 if (vp_find_vq(vp, 2, &vq) < 0 ) {
Kevin O'Connor7b673002016-02-03 03:03:15 -0500182 dprintf(1, "fail to find vq for virtio-scsi %pP\n", pci);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100183 goto fail;
184 }
185
Gerd Hoffmann604a6f12015-06-26 10:22:55 +0200186 status |= VIRTIO_CONFIG_S_DRIVER_OK;
187 vp_set_status(vp, status);
Asias He5a7730d2013-03-15 09:45:15 +0800188
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100189 int i, tot;
190 for (tot = 0, i = 0; i < 256; i++)
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200191 tot += virtio_scsi_scan_target(pci, vp, vq, i);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100192
Eric Northup5e038812014-03-12 13:42:35 -0700193 if (!tot)
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100194 goto fail;
195
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100196 return;
197
198fail:
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200199 vp_reset(vp);
200 free(vp);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100201 free(vq);
202}
203
204void
205virtio_scsi_setup(void)
206{
207 ASSERT32FLAT();
Kevin O'Connor897fb112013-02-07 23:32:48 -0500208 if (! CONFIG_VIRTIO_SCSI)
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100209 return;
210
211 dprintf(3, "init virtio-scsi\n");
212
213 struct pci_device *pci;
214 foreachpci(pci) {
Gerd Hoffmann71acef82015-06-26 09:50:22 +0200215 if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET ||
216 (pci->device != PCI_DEVICE_ID_VIRTIO_SCSI_09 &&
217 pci->device != PCI_DEVICE_ID_VIRTIO_SCSI_10))
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100218 continue;
Kevin O'Connorb35ea542016-04-01 20:05:45 -0400219 run_thread(init_virtio_scsi, pci);
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100220 }
221}