blob: cd4046c1b468b3a076c8b30291e69bb9a172f35b [file] [log] [blame]
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +03001// QEMU VMWARE Paravirtualized SCSI boot support.
2//
3// Copyright (c) 2013 Ravello Systems LTD (http://ravellosystems.com)
4//
5// Authors:
6// Evgeny Budilovsky <evgeny.budilovsky@ravellosystems.com>
7//
8// This file may be distributed under the terms of the GNU LGPLv3 license.
9
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +030010#include "block.h" // struct drive_s
11#include "blockcmd.h" // scsi_drive_setup
12#include "config.h" // CONFIG_*
13#include "malloc.h" // free
Kevin O'Connoreee06e02015-09-29 10:14:58 -040014#include "memmap.h" // PAGE_SHIFT, virt_to_phys
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +030015#include "output.h" // dprintf
Kevin O'Connor4d8510c2016-02-03 01:28:20 -050016#include "pcidevice.h" // foreachpci
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +030017#include "pci_ids.h" // PCI_DEVICE_ID_VMWARE_PVSCSI
18#include "pci_regs.h" // PCI_VENDOR_ID
Kevin O'Connor60f3b802013-12-27 12:08:50 -050019#include "pvscsi.h" // pvscsi_setup
Kevin O'Connor79bafa12016-04-05 13:04:07 -040020#include "stacks.h" // run_thread
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +030021#include "std/disk.h" // DISK_RET_SUCCESS
22#include "string.h" // memset
23#include "util.h" // usleep
Kevin O'Connor60f3b802013-12-27 12:08:50 -050024#include "x86.h" // writel
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +030025
26#define MASK(n) ((1 << (n)) - 1)
27
28#define SIMPLE_QUEUE_TAG 0x20
29
30#define PVSCSI_INTR_CMPL_0 (1 << 0)
31#define PVSCSI_INTR_CMPL_1 (1 << 1)
32#define PVSCSI_INTR_CMPL_MASK MASK(2)
33
34#define PVSCSI_INTR_MSG_0 (1 << 2)
35#define PVSCSI_INTR_MSG_1 (1 << 3)
36#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2)
37#define PVSCSI_INTR_ALL_SUPPORTED MASK(4)
38
39#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0)
40#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1)
41#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2)
42#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3)
43#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4)
44
45enum PVSCSIRegOffset {
46 PVSCSI_REG_OFFSET_COMMAND = 0x0,
47 PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4,
48 PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8,
49 PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100,
50 PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104,
51 PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108,
52 PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c,
53 PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c,
54 PVSCSI_REG_OFFSET_INTR_MASK = 0x2010,
55 PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014,
56 PVSCSI_REG_OFFSET_DEBUG = 0x3018,
57 PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018,
58};
59
60enum PVSCSICommands {
61 PVSCSI_CMD_FIRST = 0,
62 PVSCSI_CMD_ADAPTER_RESET = 1,
63 PVSCSI_CMD_ISSUE_SCSI = 2,
64 PVSCSI_CMD_SETUP_RINGS = 3,
65 PVSCSI_CMD_RESET_BUS = 4,
66 PVSCSI_CMD_RESET_DEVICE = 5,
67 PVSCSI_CMD_ABORT_CMD = 6,
68 PVSCSI_CMD_CONFIG = 7,
69 PVSCSI_CMD_SETUP_MSG_RING = 8,
70 PVSCSI_CMD_DEVICE_UNPLUG = 9,
71 PVSCSI_CMD_LAST = 10
72};
73
74#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32
75struct PVSCSICmdDescSetupRings {
76 u32 reqRingNumPages;
77 u32 cmpRingNumPages;
78 u64 ringsStatePPN;
79 u64 reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
80 u64 cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
81} PACKED;
82
83struct PVSCSIRingCmpDesc {
84 u64 context;
85 u64 dataLen;
86 u32 senseLen;
87 u16 hostStatus;
88 u16 scsiStatus;
89 u32 pad[2];
90} PACKED;
91
92struct PVSCSIRingsState {
93 u32 reqProdIdx;
94 u32 reqConsIdx;
95 u32 reqNumEntriesLog2;
96
97 u32 cmpProdIdx;
98 u32 cmpConsIdx;
99 u32 cmpNumEntriesLog2;
100
101 u8 pad[104];
102
103 u32 msgProdIdx;
104 u32 msgConsIdx;
105 u32 msgNumEntriesLog2;
106} PACKED;
107
108struct PVSCSIRingReqDesc {
109 u64 context;
110 u64 dataAddr;
111 u64 dataLen;
112 u64 senseAddr;
113 u32 senseLen;
114 u32 flags;
115 u8 cdb[16];
116 u8 cdbLen;
117 u8 lun[8];
118 u8 tag;
119 u8 bus;
120 u8 target;
121 u8 vcpuHint;
122 u8 unused[59];
123} PACKED;
124
125struct pvscsi_ring_dsc_s {
126 struct PVSCSIRingsState *ring_state;
127 struct PVSCSIRingReqDesc *ring_reqs;
128 struct PVSCSIRingCmpDesc *ring_cmps;
129};
130
131struct pvscsi_lun_s {
132 struct drive_s drive;
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500133 void *iobase;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300134 u8 target;
135 u8 lun;
136 struct pvscsi_ring_dsc_s *ring_dsc;
137};
138
139static void
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500140pvscsi_write_cmd_desc(void *iobase, u32 cmd, const void *desc, size_t len)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300141{
142 const u32 *ptr = desc;
143 size_t i;
144
145 len /= sizeof(*ptr);
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500146 writel(iobase + PVSCSI_REG_OFFSET_COMMAND, cmd);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300147 for (i = 0; i < len; i++)
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500148 writel(iobase + PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300149}
150
151static void
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500152pvscsi_kick_rw_io(void *iobase)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300153{
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500154 writel(iobase + PVSCSI_REG_OFFSET_KICK_RW_IO, 0);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300155}
156
157static void
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500158pvscsi_wait_intr_cmpl(void *iobase)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300159{
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500160 while (!(readl(iobase + PVSCSI_REG_OFFSET_INTR_STATUS) & PVSCSI_INTR_CMPL_MASK))
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300161 usleep(5);
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500162 writel(iobase + PVSCSI_REG_OFFSET_INTR_STATUS, PVSCSI_INTR_CMPL_MASK);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300163}
164
165static void
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500166pvscsi_init_rings(void *iobase, struct pvscsi_ring_dsc_s **ring_dsc)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300167{
168 struct PVSCSICmdDescSetupRings cmd = {0,};
169
Dana Rubin8bc6d9f2016-08-04 14:22:31 +0300170 struct pvscsi_ring_dsc_s *dsc = memalign_high(PAGE_SIZE, sizeof(*dsc));
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300171 if (!dsc) {
172 warn_noalloc();
173 return;
174 }
175
176 dsc->ring_state =
Dana Rubin8bc6d9f2016-08-04 14:22:31 +0300177 (struct PVSCSIRingsState *)memalign_high(PAGE_SIZE, PAGE_SIZE);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300178 dsc->ring_reqs =
Dana Rubin8bc6d9f2016-08-04 14:22:31 +0300179 (struct PVSCSIRingReqDesc *)memalign_high(PAGE_SIZE, PAGE_SIZE);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300180 dsc->ring_cmps =
Dana Rubin8bc6d9f2016-08-04 14:22:31 +0300181 (struct PVSCSIRingCmpDesc *)memalign_high(PAGE_SIZE, PAGE_SIZE);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300182 if (!dsc->ring_state || !dsc->ring_reqs || !dsc->ring_cmps) {
183 warn_noalloc();
184 return;
185 }
186 memset(dsc->ring_state, 0, PAGE_SIZE);
187 memset(dsc->ring_reqs, 0, PAGE_SIZE);
188 memset(dsc->ring_cmps, 0, PAGE_SIZE);
189
190 cmd.reqRingNumPages = 1;
191 cmd.cmpRingNumPages = 1;
192 cmd.ringsStatePPN = virt_to_phys(dsc->ring_state) >> PAGE_SHIFT;
193 cmd.reqRingPPNs[0] = virt_to_phys(dsc->ring_reqs) >> PAGE_SHIFT;
194 cmd.cmpRingPPNs[0] = virt_to_phys(dsc->ring_cmps) >> PAGE_SHIFT;
195
196 pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_SETUP_RINGS,
197 &cmd, sizeof(cmd));
198 *ring_dsc = dsc;
199}
200
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300201static u32
202pvscsi_get_rsp(struct PVSCSIRingsState *s,
203 struct PVSCSIRingCmpDesc *rsp)
204{
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500205 u32 status = rsp->hostStatus;
206 s->cmpConsIdx = s->cmpConsIdx + 1;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300207 return status;
208}
209
Kevin O'Connor2dd08322015-07-07 12:35:34 -0400210int
211pvscsi_process_op(struct disk_op_s *op)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300212{
Kevin O'Connor2dd08322015-07-07 12:35:34 -0400213 if (!CONFIG_PVSCSI)
214 return DISK_RET_EBADTRACK;
215 struct pvscsi_lun_s *plun =
216 container_of(op->drive_gf, struct pvscsi_lun_s, drive);
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500217 struct pvscsi_ring_dsc_s *ring_dsc = plun->ring_dsc;
218 struct PVSCSIRingsState *s = ring_dsc->ring_state;
219 u32 req_entries = s->reqNumEntriesLog2;
220 u32 cmp_entries = s->cmpNumEntriesLog2;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300221 struct PVSCSIRingReqDesc *req;
222 struct PVSCSIRingCmpDesc *rsp;
223 u32 status;
224
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500225 if (s->reqProdIdx - s->cmpConsIdx >= 1 << req_entries) {
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300226 dprintf(1, "pvscsi: ring full: reqProdIdx=%d cmpConsIdx=%d\n",
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500227 s->reqProdIdx, s->cmpConsIdx);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300228 return DISK_RET_EBADTRACK;
229 }
230
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500231 req = ring_dsc->ring_reqs + (s->reqProdIdx & MASK(req_entries));
Kevin O'Connor2dd08322015-07-07 12:35:34 -0400232 int blocksize = scsi_fill_cmd(op, req->cdb, 16);
233 if (blocksize < 0)
234 return default_process_op(op);
Kevin O'Connor113f5d22015-07-07 12:24:50 -0400235 req->bus = 0;
Kevin O'Connor2dd08322015-07-07 12:35:34 -0400236 req->target = plun->target;
Kevin O'Connor113f5d22015-07-07 12:24:50 -0400237 memset(req->lun, 0, sizeof(req->lun));
Kevin O'Connor2dd08322015-07-07 12:35:34 -0400238 req->lun[1] = plun->lun;
Kevin O'Connor113f5d22015-07-07 12:24:50 -0400239 req->senseLen = 0;
240 req->senseAddr = 0;
241 req->cdbLen = 16;
242 req->vcpuHint = 0;
Kevin O'Connor113f5d22015-07-07 12:24:50 -0400243 req->tag = SIMPLE_QUEUE_TAG;
Kevin O'Connor5dcd1ee2015-07-07 14:43:01 -0400244 req->flags = scsi_is_read(op) ?
Kevin O'Connor113f5d22015-07-07 12:24:50 -0400245 PVSCSI_FLAG_CMD_DIR_TOHOST : PVSCSI_FLAG_CMD_DIR_TODEVICE;
246 req->dataLen = op->count * blocksize;
247 req->dataAddr = (u32)op->buf_fl;
248 s->reqProdIdx = s->reqProdIdx + 1;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300249
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500250 pvscsi_kick_rw_io(plun->iobase);
251 pvscsi_wait_intr_cmpl(plun->iobase);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300252
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500253 rsp = ring_dsc->ring_cmps + (s->cmpConsIdx & MASK(cmp_entries));
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300254 status = pvscsi_get_rsp(s, rsp);
255
256 return status == 0 ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK;
257}
258
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300259static int
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500260pvscsi_add_lun(struct pci_device *pci, void *iobase,
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300261 struct pvscsi_ring_dsc_s *ring_dsc, u8 target, u8 lun)
262{
263 struct pvscsi_lun_s *plun = malloc_fseg(sizeof(*plun));
264 if (!plun) {
265 warn_noalloc();
266 return -1;
267 }
268 memset(plun, 0, sizeof(*plun));
269 plun->drive.type = DTYPE_PVSCSI;
270 plun->drive.cntl_id = pci->bdf;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300271 plun->target = target;
272 plun->lun = lun;
273 plun->iobase = iobase;
274 plun->ring_dsc = ring_dsc;
275
Kevin O'Connor937ca6f2016-02-03 03:27:36 -0500276 char *name = znprintf(MAXDESCSIZE, "pvscsi %pP %d:%d", pci, target, lun);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300277 int prio = bootprio_find_scsi_device(pci, target, lun);
278 int ret = scsi_drive_setup(&plun->drive, name, prio);
279 free(name);
280 if (ret)
281 goto fail;
282 return 0;
283
284fail:
285 free(plun);
286 return -1;
287}
288
289static void
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500290pvscsi_scan_target(struct pci_device *pci, void *iobase,
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300291 struct pvscsi_ring_dsc_s *ring_dsc, u8 target)
292{
293 /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */
294 pvscsi_add_lun(pci, iobase, ring_dsc, target, 0);
295}
296
297static void
Kevin O'Connor79bafa12016-04-05 13:04:07 -0400298init_pvscsi(void *data)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300299{
Kevin O'Connor79bafa12016-04-05 13:04:07 -0400300 struct pci_device *pci = data;
Kevin O'Connor03f3b3e2016-02-02 22:21:49 -0500301 void *iobase = pci_enable_membar(pci, PCI_BASE_ADDRESS_0);
302 if (!iobase)
303 return;
304 pci_enable_busmaster(pci);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300305
Kevin O'Connor7b673002016-02-03 03:03:15 -0500306 dprintf(1, "found pvscsi at %pP, io @ %p\n", pci, iobase);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300307
308 pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_ADAPTER_RESET, NULL, 0);
309
Kevin O'Connor03f3b3e2016-02-02 22:21:49 -0500310 struct pvscsi_ring_dsc_s *ring_dsc = NULL;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300311 pvscsi_init_rings(iobase, &ring_dsc);
Kevin O'Connor03f3b3e2016-02-02 22:21:49 -0500312 int i;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300313 for (i = 0; i < 7; i++)
314 pvscsi_scan_target(pci, iobase, ring_dsc, i);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300315}
316
317void
318pvscsi_setup(void)
319{
320 ASSERT32FLAT();
321 if (! CONFIG_PVSCSI)
322 return;
323
324 dprintf(3, "init pvscsi\n");
325
326 struct pci_device *pci;
327 foreachpci(pci) {
328 if (pci->vendor != PCI_VENDOR_ID_VMWARE
329 || pci->device != PCI_DEVICE_ID_VMWARE_PVSCSI)
330 continue;
Kevin O'Connor79bafa12016-04-05 13:04:07 -0400331 run_thread(init_pvscsi, pci);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300332 }
333}