blob: 6911230cba277e906f6361f2e39974cc125e563f [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
Kevin O'Connor1902c942013-10-26 11:48:06 -040010#include "biosvar.h" // GET_GLOBALFLAT
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +030011#include "block.h" // struct drive_s
12#include "blockcmd.h" // scsi_drive_setup
13#include "config.h" // CONFIG_*
14#include "malloc.h" // free
15#include "output.h" // dprintf
16#include "pci.h" // foreachpci
17#include "pci_ids.h" // PCI_DEVICE_ID_VMWARE_PVSCSI
18#include "pci_regs.h" // PCI_VENDOR_ID
19#include "std/disk.h" // DISK_RET_SUCCESS
20#include "string.h" // memset
21#include "util.h" // usleep
22#include "pvscsi.h"
23#include "virtio-ring.h" // PAGE_SHIFT, virt_to_phys
24
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;
132 struct pci_device *pci;
133 u32 iobase;
134 u8 target;
135 u8 lun;
136 struct pvscsi_ring_dsc_s *ring_dsc;
137};
138
139static void
140pvscsi_write_cmd_desc(u32 iobase, u32 cmd, const void *desc, size_t len)
141{
142 const u32 *ptr = desc;
143 size_t i;
144
145 len /= sizeof(*ptr);
146 pci_writel(iobase + PVSCSI_REG_OFFSET_COMMAND, cmd);
147 for (i = 0; i < len; i++)
148 pci_writel(iobase + PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]);
149}
150
151static void
152pvscsi_kick_rw_io(u32 iobase)
153{
154 pci_writel(iobase + PVSCSI_REG_OFFSET_KICK_RW_IO, 0);
155}
156
157static void
158pvscsi_wait_intr_cmpl(u32 iobase)
159{
160 while (!(pci_readl(iobase + PVSCSI_REG_OFFSET_INTR_STATUS) & PVSCSI_INTR_CMPL_MASK))
161 usleep(5);
162 pci_writel(iobase + PVSCSI_REG_OFFSET_INTR_STATUS, PVSCSI_INTR_CMPL_MASK);
163
164}
165
166static void
167pvscsi_init_rings(u32 iobase, struct pvscsi_ring_dsc_s **ring_dsc)
168{
169 struct PVSCSICmdDescSetupRings cmd = {0,};
170
171 struct pvscsi_ring_dsc_s *dsc = memalign_low(sizeof(*dsc), PAGE_SIZE);
172 if (!dsc) {
173 warn_noalloc();
174 return;
175 }
176
177 dsc->ring_state =
178 (struct PVSCSIRingsState *)memalign_low(PAGE_SIZE, PAGE_SIZE);
179 dsc->ring_reqs =
180 (struct PVSCSIRingReqDesc *)memalign_low(PAGE_SIZE, PAGE_SIZE);
181 dsc->ring_cmps =
182 (struct PVSCSIRingCmpDesc *)memalign_low(PAGE_SIZE, PAGE_SIZE);
183 if (!dsc->ring_state || !dsc->ring_reqs || !dsc->ring_cmps) {
184 warn_noalloc();
185 return;
186 }
187 memset(dsc->ring_state, 0, PAGE_SIZE);
188 memset(dsc->ring_reqs, 0, PAGE_SIZE);
189 memset(dsc->ring_cmps, 0, PAGE_SIZE);
190
191 cmd.reqRingNumPages = 1;
192 cmd.cmpRingNumPages = 1;
193 cmd.ringsStatePPN = virt_to_phys(dsc->ring_state) >> PAGE_SHIFT;
194 cmd.reqRingPPNs[0] = virt_to_phys(dsc->ring_reqs) >> PAGE_SHIFT;
195 cmd.cmpRingPPNs[0] = virt_to_phys(dsc->ring_cmps) >> PAGE_SHIFT;
196
197 pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_SETUP_RINGS,
198 &cmd, sizeof(cmd));
199 *ring_dsc = dsc;
200}
201
202static void pvscsi_fill_req(struct PVSCSIRingsState *s,
203 struct PVSCSIRingReqDesc *req,
204 u16 target, u16 lun, void *cdbcmd, u16 blocksize,
205 struct disk_op_s *op)
206{
207 SET_LOWFLAT(req->bus, 0);
208 SET_LOWFLAT(req->target, target);
209 memset(LOWFLAT2LOW(&req->lun[0]), 0, sizeof(req->lun));
210 SET_LOWFLAT(req->lun[1], lun);
211 SET_LOWFLAT(req->senseLen, 0);
212 SET_LOWFLAT(req->senseAddr, 0);
213 SET_LOWFLAT(req->cdbLen, 16);
214 SET_LOWFLAT(req->vcpuHint, 0);
215 memcpy(LOWFLAT2LOW(&req->cdb[0]), cdbcmd, 16);
216 SET_LOWFLAT(req->tag, SIMPLE_QUEUE_TAG);
217 SET_LOWFLAT(req->flags,
218 cdb_is_read(cdbcmd, blocksize) ?
219 PVSCSI_FLAG_CMD_DIR_TOHOST : PVSCSI_FLAG_CMD_DIR_TODEVICE);
220
221 SET_LOWFLAT(req->dataLen, op->count * blocksize);
222 SET_LOWFLAT(req->dataAddr, (u32)op->buf_fl);
223 SET_LOWFLAT(s->reqProdIdx, GET_LOWFLAT(s->reqProdIdx) + 1);
224
225}
226
227static u32
228pvscsi_get_rsp(struct PVSCSIRingsState *s,
229 struct PVSCSIRingCmpDesc *rsp)
230{
231 u32 status = GET_LOWFLAT(rsp->hostStatus);
232 SET_LOWFLAT(s->cmpConsIdx, GET_LOWFLAT(s->cmpConsIdx)+1);
233 return status;
234}
235
236static int
Kevin O'Connor1902c942013-10-26 11:48:06 -0400237pvscsi_cmd(struct pvscsi_lun_s *plun_gf, struct disk_op_s *op,
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300238 void *cdbcmd, u16 target, u16 lun, u16 blocksize)
239{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400240 struct pvscsi_ring_dsc_s *ring_dsc = GET_GLOBALFLAT(plun_gf->ring_dsc);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300241 struct PVSCSIRingsState *s = GET_LOWFLAT(ring_dsc->ring_state);
242 u32 req_entries = GET_LOWFLAT(s->reqNumEntriesLog2);
243 u32 cmp_entries = GET_LOWFLAT(s->cmpNumEntriesLog2);
244 struct PVSCSIRingReqDesc *req;
245 struct PVSCSIRingCmpDesc *rsp;
246 u32 status;
247
248 if (GET_LOWFLAT(s->reqProdIdx) - GET_LOWFLAT(s->cmpConsIdx) >= 1 << req_entries) {
249 dprintf(1, "pvscsi: ring full: reqProdIdx=%d cmpConsIdx=%d\n",
250 GET_LOWFLAT(s->reqProdIdx), GET_LOWFLAT(s->cmpConsIdx));
251 return DISK_RET_EBADTRACK;
252 }
253
254 req = GET_LOWFLAT(ring_dsc->ring_reqs) + (GET_LOWFLAT(s->reqProdIdx) & MASK(req_entries));
255 pvscsi_fill_req(s, req, target, lun, cdbcmd, blocksize, op);
256
Kevin O'Connor1902c942013-10-26 11:48:06 -0400257 pvscsi_kick_rw_io(GET_GLOBALFLAT(plun_gf->iobase));
258 pvscsi_wait_intr_cmpl(GET_GLOBALFLAT(plun_gf->iobase));
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300259
260 rsp = GET_LOWFLAT(ring_dsc->ring_cmps) + (GET_LOWFLAT(s->cmpConsIdx) & MASK(cmp_entries));
261 status = pvscsi_get_rsp(s, rsp);
262
263 return status == 0 ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK;
264}
265
266int
267pvscsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
268{
269 if (!CONFIG_PVSCSI)
270 return DISK_RET_EBADTRACK;
271
Kevin O'Connor1902c942013-10-26 11:48:06 -0400272 struct pvscsi_lun_s *plun_gf =
273 container_of(op->drive_gf, struct pvscsi_lun_s, drive);
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300274
Kevin O'Connor1902c942013-10-26 11:48:06 -0400275 return pvscsi_cmd(plun_gf, op, cdbcmd,
276 GET_GLOBALFLAT(plun_gf->target),
277 GET_GLOBALFLAT(plun_gf->lun),
Evgeny Budilovsky83d60b32013-10-14 18:03:36 +0300278 blocksize);
279
280}
281
282static int
283pvscsi_add_lun(struct pci_device *pci, u32 iobase,
284 struct pvscsi_ring_dsc_s *ring_dsc, u8 target, u8 lun)
285{
286 struct pvscsi_lun_s *plun = malloc_fseg(sizeof(*plun));
287 if (!plun) {
288 warn_noalloc();
289 return -1;
290 }
291 memset(plun, 0, sizeof(*plun));
292 plun->drive.type = DTYPE_PVSCSI;
293 plun->drive.cntl_id = pci->bdf;
294 plun->pci = pci;
295 plun->target = target;
296 plun->lun = lun;
297 plun->iobase = iobase;
298 plun->ring_dsc = ring_dsc;
299
300 char *name = znprintf(16, "pvscsi %02x:%02x.%x %d:%d",
301 pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf),
302 pci_bdf_to_fn(pci->bdf), target, lun);
303 int prio = bootprio_find_scsi_device(pci, target, lun);
304 int ret = scsi_drive_setup(&plun->drive, name, prio);
305 free(name);
306 if (ret)
307 goto fail;
308 return 0;
309
310fail:
311 free(plun);
312 return -1;
313}
314
315static void
316pvscsi_scan_target(struct pci_device *pci, u32 iobase,
317 struct pvscsi_ring_dsc_s *ring_dsc, u8 target)
318{
319 /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */
320 pvscsi_add_lun(pci, iobase, ring_dsc, target, 0);
321}
322
323static void
324init_pvscsi(struct pci_device *pci)
325{
326 struct pvscsi_ring_dsc_s *ring_dsc = NULL;
327 int i;
328 u16 bdf = pci->bdf;
329 u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0)
330 & PCI_BASE_ADDRESS_MEM_MASK;
331
332 pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
333
334 dprintf(1, "found pvscsi at %02x:%02x.%x, io @ %x\n",
335 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
336 pci_bdf_to_fn(bdf), iobase);
337
338 pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_ADAPTER_RESET, NULL, 0);
339
340 pvscsi_init_rings(iobase, &ring_dsc);
341 for (i = 0; i < 7; i++)
342 pvscsi_scan_target(pci, iobase, ring_dsc, i);
343
344 return;
345
346}
347
348void
349pvscsi_setup(void)
350{
351 ASSERT32FLAT();
352 if (! CONFIG_PVSCSI)
353 return;
354
355 dprintf(3, "init pvscsi\n");
356
357 struct pci_device *pci;
358 foreachpci(pci) {
359 if (pci->vendor != PCI_VENDOR_ID_VMWARE
360 || pci->device != PCI_DEVICE_ID_VMWARE_PVSCSI)
361 continue;
362 init_pvscsi(pci);
363 }
364}