blob: a9d118b87e7a67bfea1c3c0950bc47e832b6501d [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
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +030020#include "std/disk.h" // DISK_RET_SUCCESS
21#include "string.h" // memset
22#include "util.h" // usleep
Kevin O'Connor60f3b802013-12-27 12:08:50 -050023#include "x86.h" // writel
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +030024
25#define MASK(n) ((1 << (n)) - 1)
26
27#define SIMPLE_QUEUE_TAG 0x20
28
29#define PVSCSI_INTR_CMPL_0 (1 << 0)
30#define PVSCSI_INTR_CMPL_1 (1 << 1)
31#define PVSCSI_INTR_CMPL_MASK MASK(2)
32
33#define PVSCSI_INTR_MSG_0 (1 << 2)
34#define PVSCSI_INTR_MSG_1 (1 << 3)
35#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2)
36#define PVSCSI_INTR_ALL_SUPPORTED MASK(4)
37
38#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0)
39#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1)
40#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2)
41#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3)
42#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4)
43
44enum PVSCSIRegOffset {
45 PVSCSI_REG_OFFSET_COMMAND = 0x0,
46 PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4,
47 PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8,
48 PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100,
49 PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104,
50 PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108,
51 PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c,
52 PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c,
53 PVSCSI_REG_OFFSET_INTR_MASK = 0x2010,
54 PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014,
55 PVSCSI_REG_OFFSET_DEBUG = 0x3018,
56 PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018,
57};
58
59enum PVSCSICommands {
60 PVSCSI_CMD_FIRST = 0,
61 PVSCSI_CMD_ADAPTER_RESET = 1,
62 PVSCSI_CMD_ISSUE_SCSI = 2,
63 PVSCSI_CMD_SETUP_RINGS = 3,
64 PVSCSI_CMD_RESET_BUS = 4,
65 PVSCSI_CMD_RESET_DEVICE = 5,
66 PVSCSI_CMD_ABORT_CMD = 6,
67 PVSCSI_CMD_CONFIG = 7,
68 PVSCSI_CMD_SETUP_MSG_RING = 8,
69 PVSCSI_CMD_DEVICE_UNPLUG = 9,
70 PVSCSI_CMD_LAST = 10
71};
72
73#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32
74struct PVSCSICmdDescSetupRings {
75 u32 reqRingNumPages;
76 u32 cmpRingNumPages;
77 u64 ringsStatePPN;
78 u64 reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
79 u64 cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
80} PACKED;
81
82struct PVSCSIRingCmpDesc {
83 u64 context;
84 u64 dataLen;
85 u32 senseLen;
86 u16 hostStatus;
87 u16 scsiStatus;
88 u32 pad[2];
89} PACKED;
90
91struct PVSCSIRingsState {
92 u32 reqProdIdx;
93 u32 reqConsIdx;
94 u32 reqNumEntriesLog2;
95
96 u32 cmpProdIdx;
97 u32 cmpConsIdx;
98 u32 cmpNumEntriesLog2;
99
100 u8 pad[104];
101
102 u32 msgProdIdx;
103 u32 msgConsIdx;
104 u32 msgNumEntriesLog2;
105} PACKED;
106
107struct PVSCSIRingReqDesc {
108 u64 context;
109 u64 dataAddr;
110 u64 dataLen;
111 u64 senseAddr;
112 u32 senseLen;
113 u32 flags;
114 u8 cdb[16];
115 u8 cdbLen;
116 u8 lun[8];
117 u8 tag;
118 u8 bus;
119 u8 target;
120 u8 vcpuHint;
121 u8 unused[59];
122} PACKED;
123
124struct pvscsi_ring_dsc_s {
125 struct PVSCSIRingsState *ring_state;
126 struct PVSCSIRingReqDesc *ring_reqs;
127 struct PVSCSIRingCmpDesc *ring_cmps;
128};
129
130struct pvscsi_lun_s {
131 struct drive_s drive;
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500132 void *iobase;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300133 u8 target;
134 u8 lun;
135 struct pvscsi_ring_dsc_s *ring_dsc;
136};
137
138static void
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500139pvscsi_write_cmd_desc(void *iobase, u32 cmd, const void *desc, size_t len)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300140{
141 const u32 *ptr = desc;
142 size_t i;
143
144 len /= sizeof(*ptr);
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500145 writel(iobase + PVSCSI_REG_OFFSET_COMMAND, cmd);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300146 for (i = 0; i < len; i++)
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500147 writel(iobase + PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300148}
149
150static void
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500151pvscsi_kick_rw_io(void *iobase)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300152{
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500153 writel(iobase + PVSCSI_REG_OFFSET_KICK_RW_IO, 0);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300154}
155
156static void
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500157pvscsi_wait_intr_cmpl(void *iobase)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300158{
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500159 while (!(readl(iobase + PVSCSI_REG_OFFSET_INTR_STATUS) & PVSCSI_INTR_CMPL_MASK))
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300160 usleep(5);
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500161 writel(iobase + PVSCSI_REG_OFFSET_INTR_STATUS, PVSCSI_INTR_CMPL_MASK);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300162}
163
164static void
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500165pvscsi_init_rings(void *iobase, struct pvscsi_ring_dsc_s **ring_dsc)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300166{
167 struct PVSCSICmdDescSetupRings cmd = {0,};
168
169 struct pvscsi_ring_dsc_s *dsc = memalign_low(sizeof(*dsc), PAGE_SIZE);
170 if (!dsc) {
171 warn_noalloc();
172 return;
173 }
174
175 dsc->ring_state =
176 (struct PVSCSIRingsState *)memalign_low(PAGE_SIZE, PAGE_SIZE);
177 dsc->ring_reqs =
178 (struct PVSCSIRingReqDesc *)memalign_low(PAGE_SIZE, PAGE_SIZE);
179 dsc->ring_cmps =
180 (struct PVSCSIRingCmpDesc *)memalign_low(PAGE_SIZE, PAGE_SIZE);
181 if (!dsc->ring_state || !dsc->ring_reqs || !dsc->ring_cmps) {
182 warn_noalloc();
183 return;
184 }
185 memset(dsc->ring_state, 0, PAGE_SIZE);
186 memset(dsc->ring_reqs, 0, PAGE_SIZE);
187 memset(dsc->ring_cmps, 0, PAGE_SIZE);
188
189 cmd.reqRingNumPages = 1;
190 cmd.cmpRingNumPages = 1;
191 cmd.ringsStatePPN = virt_to_phys(dsc->ring_state) >> PAGE_SHIFT;
192 cmd.reqRingPPNs[0] = virt_to_phys(dsc->ring_reqs) >> PAGE_SHIFT;
193 cmd.cmpRingPPNs[0] = virt_to_phys(dsc->ring_cmps) >> PAGE_SHIFT;
194
195 pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_SETUP_RINGS,
196 &cmd, sizeof(cmd));
197 *ring_dsc = dsc;
198}
199
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300200static u32
201pvscsi_get_rsp(struct PVSCSIRingsState *s,
202 struct PVSCSIRingCmpDesc *rsp)
203{
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500204 u32 status = rsp->hostStatus;
205 s->cmpConsIdx = s->cmpConsIdx + 1;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300206 return status;
207}
208
Kevin O'Connor2dd08322015-07-07 12:35:34 -0400209int
210pvscsi_process_op(struct disk_op_s *op)
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300211{
Kevin O'Connor2dd08322015-07-07 12:35:34 -0400212 if (!CONFIG_PVSCSI)
213 return DISK_RET_EBADTRACK;
214 struct pvscsi_lun_s *plun =
215 container_of(op->drive_gf, struct pvscsi_lun_s, drive);
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500216 struct pvscsi_ring_dsc_s *ring_dsc = plun->ring_dsc;
217 struct PVSCSIRingsState *s = ring_dsc->ring_state;
218 u32 req_entries = s->reqNumEntriesLog2;
219 u32 cmp_entries = s->cmpNumEntriesLog2;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300220 struct PVSCSIRingReqDesc *req;
221 struct PVSCSIRingCmpDesc *rsp;
222 u32 status;
223
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500224 if (s->reqProdIdx - s->cmpConsIdx >= 1 << req_entries) {
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300225 dprintf(1, "pvscsi: ring full: reqProdIdx=%d cmpConsIdx=%d\n",
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500226 s->reqProdIdx, s->cmpConsIdx);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300227 return DISK_RET_EBADTRACK;
228 }
229
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500230 req = ring_dsc->ring_reqs + (s->reqProdIdx & MASK(req_entries));
Kevin O'Connor2dd08322015-07-07 12:35:34 -0400231 int blocksize = scsi_fill_cmd(op, req->cdb, 16);
232 if (blocksize < 0)
233 return default_process_op(op);
Kevin O'Connor113f5d22015-07-07 12:24:50 -0400234 req->bus = 0;
Kevin O'Connor2dd08322015-07-07 12:35:34 -0400235 req->target = plun->target;
Kevin O'Connor113f5d22015-07-07 12:24:50 -0400236 memset(req->lun, 0, sizeof(req->lun));
Kevin O'Connor2dd08322015-07-07 12:35:34 -0400237 req->lun[1] = plun->lun;
Kevin O'Connor113f5d22015-07-07 12:24:50 -0400238 req->senseLen = 0;
239 req->senseAddr = 0;
240 req->cdbLen = 16;
241 req->vcpuHint = 0;
Kevin O'Connor113f5d22015-07-07 12:24:50 -0400242 req->tag = SIMPLE_QUEUE_TAG;
Kevin O'Connor5dcd1ee2015-07-07 14:43:01 -0400243 req->flags = scsi_is_read(op) ?
Kevin O'Connor113f5d22015-07-07 12:24:50 -0400244 PVSCSI_FLAG_CMD_DIR_TOHOST : PVSCSI_FLAG_CMD_DIR_TODEVICE;
245 req->dataLen = op->count * blocksize;
246 req->dataAddr = (u32)op->buf_fl;
247 s->reqProdIdx = s->reqProdIdx + 1;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300248
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500249 pvscsi_kick_rw_io(plun->iobase);
250 pvscsi_wait_intr_cmpl(plun->iobase);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300251
Kevin O'Connor60f3b802013-12-27 12:08:50 -0500252 rsp = ring_dsc->ring_cmps + (s->cmpConsIdx & MASK(cmp_entries));
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300253 status = pvscsi_get_rsp(s, rsp);
254
255 return status == 0 ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK;
256}
257
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300258static int
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500259pvscsi_add_lun(struct pci_device *pci, void *iobase,
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300260 struct pvscsi_ring_dsc_s *ring_dsc, u8 target, u8 lun)
261{
262 struct pvscsi_lun_s *plun = malloc_fseg(sizeof(*plun));
263 if (!plun) {
264 warn_noalloc();
265 return -1;
266 }
267 memset(plun, 0, sizeof(*plun));
268 plun->drive.type = DTYPE_PVSCSI;
269 plun->drive.cntl_id = pci->bdf;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300270 plun->target = target;
271 plun->lun = lun;
272 plun->iobase = iobase;
273 plun->ring_dsc = ring_dsc;
274
Kevin O'Connor937ca6f2016-02-03 03:27:36 -0500275 char *name = znprintf(MAXDESCSIZE, "pvscsi %pP %d:%d", pci, target, lun);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300276 int prio = bootprio_find_scsi_device(pci, target, lun);
277 int ret = scsi_drive_setup(&plun->drive, name, prio);
278 free(name);
279 if (ret)
280 goto fail;
281 return 0;
282
283fail:
284 free(plun);
285 return -1;
286}
287
288static void
Kevin O'Connor4b400a12013-12-24 00:46:15 -0500289pvscsi_scan_target(struct pci_device *pci, void *iobase,
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300290 struct pvscsi_ring_dsc_s *ring_dsc, u8 target)
291{
292 /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */
293 pvscsi_add_lun(pci, iobase, ring_dsc, target, 0);
294}
295
296static void
297init_pvscsi(struct pci_device *pci)
298{
Kevin O'Connor03f3b3e2016-02-02 22:21:49 -0500299 void *iobase = pci_enable_membar(pci, PCI_BASE_ADDRESS_0);
300 if (!iobase)
301 return;
302 pci_enable_busmaster(pci);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300303
Kevin O'Connor7b673002016-02-03 03:03:15 -0500304 dprintf(1, "found pvscsi at %pP, io @ %p\n", pci, iobase);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300305
306 pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_ADAPTER_RESET, NULL, 0);
307
Kevin O'Connor03f3b3e2016-02-02 22:21:49 -0500308 struct pvscsi_ring_dsc_s *ring_dsc = NULL;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300309 pvscsi_init_rings(iobase, &ring_dsc);
Kevin O'Connor03f3b3e2016-02-02 22:21:49 -0500310 int i;
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300311 for (i = 0; i < 7; i++)
312 pvscsi_scan_target(pci, iobase, ring_dsc, i);
313
314 return;
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;
331 init_pvscsi(pci);
332 }
333}