VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/virtio.c

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.8 KB
Line 
1/* $Id: virtio.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VirtIO-SCSI host adapter driver to boot from disks.
4 */
5
6/*
7 * Copyright (C) 2019-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <stdint.h>
29#include <string.h>
30#include "biosint.h"
31#include "ebda.h"
32#include "inlines.h"
33#include "pciutil.h"
34#include "vds.h"
35#include "scsi.h"
36
37//#define DEBUG_VIRTIO 1
38#if DEBUG_VIRTIO
39# define DBG_VIRTIO(...) BX_INFO(__VA_ARGS__)
40#else
41# define DBG_VIRTIO(...)
42#endif
43
44/* The maximum CDB size. */
45#define VIRTIO_SCSI_CDB_SZ 16
46/** Maximum sense data to return. */
47#define VIRTIO_SCSI_SENSE_SZ 32
48
49#define VIRTIO_SCSI_RING_ELEM 3
50
51/**
52 * VirtIO queue descriptor.
53 */
54typedef struct
55{
56 /** 64bit guest physical address of the buffer, split into high and low part because we work in real mode. */
57 uint32_t GCPhysBufLow;
58 uint32_t GCPhysBufHigh;
59 /** Length of the buffer in bytes. */
60 uint32_t cbBuf;
61 /** Flags for the buffer. */
62 uint16_t fFlags;
63 /** Next field where the buffer is continued if _NEXT flag is set. */
64 uint16_t idxNext;
65} virtio_q_desc_t;
66
67#define VIRTIO_Q_DESC_F_NEXT 0x1
68#define VIRTIO_Q_DESC_F_WRITE 0x2
69#define VIRTIO_Q_DESC_F_INDIRECT 0x4
70
71/**
72 * VirtIO available ring.
73 */
74typedef struct
75{
76 /** Flags. */
77 volatile uint16_t fFlags;
78 /** Next index to write an available buffer by the driver. */
79 volatile uint16_t idxNextFree;
80 /** The ring - we only provide one entry. */
81 volatile uint16_t au16Ring[VIRTIO_SCSI_RING_ELEM];
82 /** Used event index. */
83 volatile uint16_t u16EvtUsed;
84} virtio_q_avail_t;
85
86/**
87 * VirtIO queue used element.
88 */
89typedef struct
90{
91 /** Index of the start of the descriptor chain. */
92 uint32_t u32Id;
93 /** Number of bytes used in the descriptor chain. */
94 uint32_t cbUsed;
95} virtio_q_used_elem_t;
96
97/**
98 * VirtIo used ring.
99 */
100typedef struct
101{
102 /** Flags. */
103 volatile uint16_t fFlags;
104 /** Index where the next entry would be written by the device. */
105 volatile uint16_t idxNextUsed;
106 /** The used ring. */
107 virtio_q_used_elem_t aRing[VIRTIO_SCSI_RING_ELEM];
108} virtio_q_used_t;
109
110/**
111 * VirtIO queue structure we are using, needs to be aligned on a 16byte boundary.
112 */
113typedef struct
114{
115 /** The descriptor table, using 3 max. */
116 virtio_q_desc_t aDescTbl[3];
117 /** Available ring. */
118 virtio_q_avail_t AvailRing;
119 /** Used ring. */
120 virtio_q_used_t UsedRing;
121 /** The notification offset for the queue. */
122 uint32_t offNotify;
123} virtio_q_t;
124
125/**
126 * VirtIO SCSI request structure passed in the queue.
127 */
128typedef struct
129{
130 /** The LUN to address. */
131 uint8_t au8Lun[8];
132 /** Request ID - split into low and high part. */
133 uint32_t u32IdLow;
134 uint32_t u32IdHigh;
135 /** Task attributes. */
136 uint8_t u8TaskAttr;
137 /** Priority. */
138 uint8_t u8Prio;
139 /** CRN value, usually 0. */
140 uint8_t u8Crn;
141 /** The CDB. */
142 uint8_t abCdb[VIRTIO_SCSI_CDB_SZ];
143} virtio_scsi_req_hdr_t;
144
145/**
146 * VirtIO SCSI status structure filled by the device.
147 */
148typedef struct
149{
150 /** Returned sense length. */
151 uint32_t cbSense;
152 /** Residual amount of bytes left. */
153 uint32_t cbResidual;
154 /** Status qualifier. */
155 uint16_t u16StatusQual;
156 /** Status code. */
157 uint8_t u8Status;
158 /** Response code. */
159 uint8_t u8Response;
160 /** Sense data. */
161 uint8_t abSense[VIRTIO_SCSI_SENSE_SZ];
162} virtio_scsi_req_sts_t;
163
164/**
165 * VirtIO config for the different data structures.
166 */
167typedef struct
168{
169 /** BAR where to find it. */
170 uint8_t u8Bar;
171 /** Padding. */
172 uint8_t abPad[3];
173 /** Offset within the bar. */
174 uint32_t u32Offset;
175 /** Length of the structure in bytes. */
176 uint32_t u32Length;
177} virtio_bar_cfg_t;
178
179/**
180 * VirtIO PCI capability structure.
181 */
182typedef struct
183{
184 /** Capability typem should always be PCI_CAP_ID_VNDR*/
185 uint8_t u8PciCapId;
186 /** Offset where to find the next capability or 0 if last capability. */
187 uint8_t u8PciCapNext;
188 /** Size of the capability in bytes. */
189 uint8_t u8PciCapLen;
190 /** VirtIO capability type. */
191 uint8_t u8VirtIoCfgType;
192 /** BAR where to find it. */
193 uint8_t u8Bar;
194 /** Padding. */
195 uint8_t abPad[3];
196 /** Offset within the bar. */
197 uint32_t u32Offset;
198 /** Length of the structure in bytes. */
199 uint32_t u32Length;
200} virtio_pci_cap_t;
201
202/**
203 * VirtIO-SCSI controller data.
204 */
205typedef struct
206{
207 /** The queue used - must be first for alignment reasons. */
208 virtio_q_t Queue;
209 /** The BAR configs read from the PCI configuration space, see VIRTIO_PCI_CAP_*_CFG,
210 * only use 4 because VIRTIO_PCI_CAP_PCI_CFG is not part of this. */
211 virtio_bar_cfg_t aBarCfgs[4];
212 /** The start offset in the PCI configuration space where to find the VIRTIO_PCI_CAP_PCI_CFG
213 * capability for the alternate access method to the registers. */
214 uint8_t u8PciCfgOff;
215 /** The notification offset multiplier. */
216 uint32_t u32NotifyOffMult;
217 /** PCI bus where the device is located. */
218 uint8_t u8Bus;
219 /** Device/Function number. */
220 uint8_t u8DevFn;
221 /** The current executed command structure. */
222 virtio_scsi_req_hdr_t ScsiReqHdr;
223 virtio_scsi_req_sts_t ScsiReqSts;
224} virtio_t;
225
226/* The VirtIO specific data must fit into 1KB (statically allocated). */
227ct_assert(sizeof(virtio_t) <= 1024);
228
229/** PCI configuration fields. */
230#define PCI_CONFIG_CAP 0x34
231
232#define PCI_CAP_ID_VNDR 0x09
233
234#define VBOX_VIRTIO_NIL_CFG 0xff
235
236#define VIRTIO_PCI_CAP_COMMON_CFG 0x01
237#define VIRTIO_PCI_CAP_NOTIFY_CFG 0x02
238#define VIRTIO_PCI_CAP_ISR_CFG 0x03
239#define VIRTIO_PCI_CAP_DEVICE_CFG 0x04
240#define VIRTIO_PCI_CAP_PCI_CFG 0x05
241
242#define RT_BIT_32(bit) ((uint32_t)(1L << (bit)))
243
244#define VIRTIO_COMMON_REG_DEV_FEAT_SLCT 0x00
245#define VIRTIO_COMMON_REG_DEV_FEAT 0x04
246# define VIRTIO_CMN_REG_DEV_FEAT_SCSI_INOUT 0x01
247#define VIRTIO_COMMON_REG_DRV_FEAT_SLCT 0x08
248#define VIRTIO_COMMON_REG_DRV_FEAT 0x0c
249#define VIRTIO_COMMON_REG_MSIX_CFG 0x10
250#define VIRTIO_COMMON_REG_NUM_QUEUES 0x12
251#define VIRTIO_COMMON_REG_DEV_STS 0x14
252# define VIRTIO_CMN_REG_DEV_STS_F_RST 0x00
253# define VIRTIO_CMN_REG_DEV_STS_F_ACK 0x01
254# define VIRTIO_CMN_REG_DEV_STS_F_DRV 0x02
255# define VIRTIO_CMN_REG_DEV_STS_F_DRV_OK 0x04
256# define VIRTIO_CMN_REG_DEV_STS_F_FEAT_OK 0x08
257# define VIRTIO_CMN_REG_DEV_STS_F_DEV_RST 0x40
258# define VIRTIO_CMN_REG_DEV_STS_F_FAILED 0x80
259#define VIRTIO_COMMON_REG_CFG_GEN 0x15
260
261#define VIRTIO_COMMON_REG_Q_SELECT 0x16
262#define VIRTIO_COMMON_REG_Q_SIZE 0x18
263#define VIRTIO_COMMON_REG_Q_MSIX_VEC 0x1a
264#define VIRTIO_COMMON_REG_Q_ENABLE 0x1c
265#define VIRTIO_COMMON_REG_Q_NOTIFY_OFF 0x1e
266#define VIRTIO_COMMON_REG_Q_DESC 0x20
267#define VIRTIO_COMMON_REG_Q_DRIVER 0x28
268#define VIRTIO_COMMON_REG_Q_DEVICE 0x30
269
270#define VIRTIO_DEV_CFG_REG_Q_NUM 0x00
271#define VIRTIO_DEV_CFG_REG_SEG_MAX 0x04
272#define VIRTIO_DEV_CFG_REG_SECT_MAX 0x08
273#define VIRTIO_DEV_CFG_REG_CMD_PER_LUN 0x0c
274#define VIRTIO_DEV_CFG_REG_EVT_INFO_SZ 0x10
275#define VIRTIO_DEV_CFG_REG_SENSE_SZ 0x14
276#define VIRTIO_DEV_CFG_REG_CDB_SZ 0x18
277#define VIRTIO_DEV_CFG_REG_MAX_CHANNEL 0x1c
278#define VIRTIO_DEV_CFG_REG_MAX_TGT 0x1e
279#define VIRTIO_DEV_CFG_REG_MAX_LUN 0x20
280
281#define VIRTIO_SCSI_Q_CONTROL 0x00
282#define VIRTIO_SCSI_Q_EVENT 0x01
283#define VIRTIO_SCSI_Q_REQUEST 0x02
284
285#define VIRTIO_SCSI_STS_RESPONSE_OK 0x00
286
287static void virtio_reg_set_bar_offset_length(virtio_t __far *virtio, uint8_t u8Bar, uint32_t offReg, uint32_t cb)
288{
289 pci_write_config_byte(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + 4, u8Bar);
290 pci_write_config_dword(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + 8, offReg);
291 pci_write_config_dword(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + 12, cb);
292}
293
294static void virtio_reg_common_access_prepare(virtio_t __far *virtio, uint16_t offReg, uint32_t cbAcc)
295{
296 virtio_reg_set_bar_offset_length(virtio,
297 virtio->aBarCfgs[VIRTIO_PCI_CAP_COMMON_CFG - 1].u8Bar,
298 virtio->aBarCfgs[VIRTIO_PCI_CAP_COMMON_CFG - 1].u32Offset + offReg,
299 cbAcc);
300}
301
302static void virtio_reg_dev_access_prepare(virtio_t __far *virtio, uint16_t offReg, uint32_t cbAcc)
303{
304 virtio_reg_set_bar_offset_length(virtio,
305 virtio->aBarCfgs[VIRTIO_PCI_CAP_DEVICE_CFG - 1].u8Bar,
306 virtio->aBarCfgs[VIRTIO_PCI_CAP_DEVICE_CFG - 1].u32Offset + offReg,
307 cbAcc);
308}
309
310static void virtio_reg_notify_access_prepare(virtio_t __far *virtio, uint16_t offReg, uint32_t cbAcc)
311{
312 virtio_reg_set_bar_offset_length(virtio,
313 virtio->aBarCfgs[VIRTIO_PCI_CAP_NOTIFY_CFG - 1].u8Bar,
314 virtio->aBarCfgs[VIRTIO_PCI_CAP_NOTIFY_CFG - 1].u32Offset + offReg,
315 cbAcc);
316}
317
318static void virtio_reg_isr_prepare(virtio_t __far *virtio, uint32_t cbAcc)
319{
320 virtio_reg_set_bar_offset_length(virtio,
321 virtio->aBarCfgs[VIRTIO_PCI_CAP_ISR_CFG - 1].u8Bar,
322 virtio->aBarCfgs[VIRTIO_PCI_CAP_ISR_CFG - 1].u32Offset,
323 cbAcc);
324}
325
326static uint8_t virtio_reg_common_read_u8(virtio_t __far *virtio, uint16_t offReg)
327{
328 virtio_reg_common_access_prepare(virtio, offReg, sizeof(uint8_t));
329 return pci_read_config_byte(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t));
330}
331
332static void virtio_reg_common_write_u8(virtio_t __far *virtio, uint16_t offReg, uint8_t u8Val)
333{
334 virtio_reg_common_access_prepare(virtio, offReg, sizeof(uint8_t));
335 pci_write_config_byte(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t), u8Val);
336}
337
338static uint16_t virtio_reg_common_read_u16(virtio_t __far *virtio, uint16_t offReg)
339{
340 virtio_reg_common_access_prepare(virtio, offReg, sizeof(uint16_t));
341 return pci_read_config_word(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t));
342}
343
344static void virtio_reg_common_write_u16(virtio_t __far *virtio, uint16_t offReg, uint16_t u16Val)
345{
346 virtio_reg_common_access_prepare(virtio, offReg, sizeof(uint16_t));
347 pci_write_config_word(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t), u16Val);
348}
349
350static void virtio_reg_common_write_u32(virtio_t __far *virtio, uint16_t offReg, uint32_t u32Val)
351{
352 virtio_reg_common_access_prepare(virtio, offReg, sizeof(uint32_t));
353 pci_write_config_dword(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t), u32Val);
354}
355
356static uint32_t virtio_reg_dev_cfg_read_u32(virtio_t __far *virtio, uint16_t offReg)
357{
358 virtio_reg_dev_access_prepare(virtio, offReg, sizeof(uint32_t));
359 return pci_read_config_dword(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t));
360}
361
362static void virtio_reg_dev_cfg_write_u32(virtio_t __far *virtio, uint16_t offReg, uint32_t u32Val)
363{
364 virtio_reg_dev_access_prepare(virtio, offReg, sizeof(uint32_t));
365 pci_write_config_dword(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t), u32Val);
366}
367
368static void virtio_reg_notify_write_u16(virtio_t __far *virtio, uint16_t offReg, uint16_t u16Val)
369{
370 virtio_reg_notify_access_prepare(virtio, offReg, sizeof(uint16_t));
371 pci_write_config_word(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t), u16Val);
372}
373
374static uint8_t virtio_reg_isr_read_u8(virtio_t __far *virtio)
375{
376 virtio_reg_isr_prepare(virtio, sizeof(uint8_t));
377 return pci_read_config_byte(virtio->u8Bus, virtio->u8DevFn, virtio->u8PciCfgOff + sizeof(virtio_pci_cap_t));
378}
379
380/**
381 * Converts a segment:offset pair into a 32bit physical address.
382 */
383static uint32_t virtio_addr_to_phys(void __far *ptr)
384{
385 return ((uint32_t)FP_SEG(ptr) << 4) + FP_OFF(ptr);
386}
387
388int virtio_scsi_cmd_data_out(void __far *pvHba, uint8_t idTgt, uint8_t __far *aCDB,
389 uint8_t cbCDB, uint8_t __far *buffer, uint32_t length)
390{
391 virtio_t __far *virtio = (virtio_t __far *)pvHba;
392 uint16_t idxUsedOld = virtio->Queue.UsedRing.idxNextUsed;
393
394 _fmemset(&virtio->ScsiReqHdr, 0, sizeof(virtio->ScsiReqHdr));
395 _fmemset(&virtio->ScsiReqSts, 0, sizeof(virtio->ScsiReqSts));
396
397 virtio->ScsiReqHdr.au8Lun[0] = 0x1;
398 virtio->ScsiReqHdr.au8Lun[1] = idTgt;
399 virtio->ScsiReqHdr.au8Lun[2] = 0;
400 virtio->ScsiReqHdr.au8Lun[3] = 0;
401 _fmemcpy(&virtio->ScsiReqHdr.abCdb[0], aCDB, cbCDB);
402
403 /* Fill in the descriptors. */
404 virtio->Queue.aDescTbl[0].GCPhysBufLow = virtio_addr_to_phys(&virtio->ScsiReqHdr);
405 virtio->Queue.aDescTbl[0].GCPhysBufHigh = 0;
406 virtio->Queue.aDescTbl[0].cbBuf = sizeof(virtio->ScsiReqHdr);
407 virtio->Queue.aDescTbl[0].fFlags = VIRTIO_Q_DESC_F_NEXT;
408 virtio->Queue.aDescTbl[0].idxNext = 1;
409
410 virtio->Queue.aDescTbl[1].GCPhysBufLow = virtio_addr_to_phys(buffer);
411 virtio->Queue.aDescTbl[1].GCPhysBufHigh = 0;
412 virtio->Queue.aDescTbl[1].cbBuf = length;
413 virtio->Queue.aDescTbl[1].fFlags = VIRTIO_Q_DESC_F_NEXT;
414 virtio->Queue.aDescTbl[1].idxNext = 2;
415
416 virtio->Queue.aDescTbl[2].GCPhysBufLow = virtio_addr_to_phys(&virtio->ScsiReqSts);
417 virtio->Queue.aDescTbl[2].GCPhysBufHigh = 0;
418 virtio->Queue.aDescTbl[2].cbBuf = sizeof(virtio->ScsiReqSts);
419 virtio->Queue.aDescTbl[2].fFlags = VIRTIO_Q_DESC_F_WRITE; /* End of chain. */
420 virtio->Queue.aDescTbl[2].idxNext = 0;
421
422 /* Put it into the queue. */
423 virtio->Queue.AvailRing.au16Ring[virtio->Queue.AvailRing.idxNextFree % VIRTIO_SCSI_RING_ELEM] = 0;
424 virtio->Queue.AvailRing.idxNextFree++;
425
426 /* Notify the device about the new command. */
427 DBG_VIRTIO("VirtIO: Submitting new request, Queue.offNotify=0x%x\n", virtio->Queue.offNotify);
428 virtio_reg_notify_write_u16(virtio, virtio->Queue.offNotify, VIRTIO_SCSI_Q_REQUEST);
429
430 /* Wait for it to complete. */
431 while (idxUsedOld == virtio->Queue.UsedRing.idxNextUsed);
432
433 DBG_VIRTIO("VirtIO: Request complete u8Response=%u\n", virtio->ScsiReqSts.u8Response);
434
435 /* Read ISR register to de-assert the interrupt, don't need to do anything with it. */
436 virtio_reg_isr_read_u8(virtio);
437
438 if (virtio->ScsiReqSts.u8Response != VIRTIO_SCSI_STS_RESPONSE_OK)
439 return 4;
440
441 return 0;
442}
443
444int virtio_scsi_cmd_data_in(void __far *pvHba, uint8_t idTgt, uint8_t __far *aCDB,
445 uint8_t cbCDB, uint8_t __far *buffer, uint32_t length)
446{
447 virtio_t __far *virtio = (virtio_t __far *)pvHba;
448 uint16_t idxUsedOld = virtio->Queue.UsedRing.idxNextUsed;
449
450 _fmemset(&virtio->ScsiReqHdr, 0, sizeof(virtio->ScsiReqHdr));
451 _fmemset(&virtio->ScsiReqSts, 0, sizeof(virtio->ScsiReqSts));
452
453 virtio->ScsiReqHdr.au8Lun[0] = 0x1;
454 virtio->ScsiReqHdr.au8Lun[1] = idTgt;
455 virtio->ScsiReqHdr.au8Lun[2] = 0;
456 virtio->ScsiReqHdr.au8Lun[3] = 0;
457 _fmemcpy(&virtio->ScsiReqHdr.abCdb[0], aCDB, cbCDB);
458
459 /* Fill in the descriptors. */
460 virtio->Queue.aDescTbl[0].GCPhysBufLow = virtio_addr_to_phys(&virtio->ScsiReqHdr);
461 virtio->Queue.aDescTbl[0].GCPhysBufHigh = 0;
462 virtio->Queue.aDescTbl[0].cbBuf = sizeof(virtio->ScsiReqHdr);
463 virtio->Queue.aDescTbl[0].fFlags = VIRTIO_Q_DESC_F_NEXT;
464 virtio->Queue.aDescTbl[0].idxNext = 1;
465
466 /* No data out buffer, the status comes right after this in the next descriptor. */
467 virtio->Queue.aDescTbl[1].GCPhysBufLow = virtio_addr_to_phys(&virtio->ScsiReqSts);
468 virtio->Queue.aDescTbl[1].GCPhysBufHigh = 0;
469 virtio->Queue.aDescTbl[1].cbBuf = sizeof(virtio->ScsiReqSts);
470 virtio->Queue.aDescTbl[1].fFlags = VIRTIO_Q_DESC_F_WRITE | VIRTIO_Q_DESC_F_NEXT;
471 virtio->Queue.aDescTbl[1].idxNext = 2;
472
473 virtio->Queue.aDescTbl[2].GCPhysBufLow = virtio_addr_to_phys(buffer);
474 virtio->Queue.aDescTbl[2].GCPhysBufHigh = 0;
475 virtio->Queue.aDescTbl[2].cbBuf = length;
476 virtio->Queue.aDescTbl[2].fFlags = VIRTIO_Q_DESC_F_WRITE; /* End of chain. */
477 virtio->Queue.aDescTbl[2].idxNext = 0;
478
479 /* Put it into the queue, the index is supposed to be free-running and clipped to the ring size
480 * internally. The free running index is what the driver sees. */
481 virtio->Queue.AvailRing.au16Ring[virtio->Queue.AvailRing.idxNextFree % VIRTIO_SCSI_RING_ELEM] = 0;
482 virtio->Queue.AvailRing.idxNextFree++;
483
484 /* Notify the device about the new command. */
485 DBG_VIRTIO("VirtIO: Submitting new request, Queue.offNotify=0x%x\n", virtio->Queue.offNotify);
486 virtio_reg_notify_write_u16(virtio, virtio->Queue.offNotify, VIRTIO_SCSI_Q_REQUEST);
487
488 /* Wait for it to complete. */
489 while (idxUsedOld == virtio->Queue.UsedRing.idxNextUsed);
490
491 DBG_VIRTIO("VirtIO: Request complete u8Response=%u\n", virtio->ScsiReqSts.u8Response);
492
493 /* Read ISR register to de-assert the interrupt, don't need to do anything with it. */
494 virtio_reg_isr_read_u8(virtio);
495
496 if (virtio->ScsiReqSts.u8Response != VIRTIO_SCSI_STS_RESPONSE_OK)
497 return 4;
498
499 return 0;
500}
501
502/**
503 * Initializes the VirtIO SCSI HBA and detects attached devices.
504 */
505static int virtio_scsi_hba_init(virtio_t __far *virtio, uint8_t u8Bus, uint8_t u8DevFn, uint8_t u8PciCapOffVirtIo)
506{
507 uint8_t u8PciCapOff;
508 uint8_t u8DevStat;
509
510 virtio->u8Bus = u8Bus;
511 virtio->u8DevFn = u8DevFn;
512
513 /*
514 * Go through the config space again, read the complete config capabilities
515 * this time and fill in the data.
516 */
517 u8PciCapOff = u8PciCapOffVirtIo;
518 while (u8PciCapOff != 0)
519 {
520 uint8_t u8PciCapId = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff);
521 uint8_t cbPciCap = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff + 2); /* Capability length. */
522
523 DBG_VIRTIO("Capability ID 0x%x at 0x%x\n", u8PciCapId, u8PciCapOff);
524
525 if ( u8PciCapId == PCI_CAP_ID_VNDR
526 && cbPciCap >= sizeof(virtio_pci_cap_t))
527 {
528 /* Read in the config type and see what we got. */
529 uint8_t u8PciVirtioCfg = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff + 3);
530
531 DBG_VIRTIO("VirtIO: CFG ID 0x%x\n", u8PciVirtioCfg);
532 switch (u8PciVirtioCfg)
533 {
534 case VIRTIO_PCI_CAP_COMMON_CFG:
535 case VIRTIO_PCI_CAP_NOTIFY_CFG:
536 case VIRTIO_PCI_CAP_ISR_CFG:
537 case VIRTIO_PCI_CAP_DEVICE_CFG:
538 {
539 virtio_bar_cfg_t __far *pBarCfg = &virtio->aBarCfgs[u8PciVirtioCfg - 1];
540
541 pBarCfg->u8Bar = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff + 4);
542 pBarCfg->u32Offset = pci_read_config_dword(u8Bus, u8DevFn, u8PciCapOff + 8);
543 pBarCfg->u32Length = pci_read_config_dword(u8Bus, u8DevFn, u8PciCapOff + 12);
544 if (u8PciVirtioCfg == VIRTIO_PCI_CAP_NOTIFY_CFG)
545 {
546 virtio->u32NotifyOffMult = pci_read_config_dword(u8Bus, u8DevFn, u8PciCapOff + 16);
547 DBG_VIRTIO("VirtIO: u32NotifyOffMult 0x%x\n", virtio->u32NotifyOffMult);
548 }
549 break;
550 }
551 case VIRTIO_PCI_CAP_PCI_CFG:
552 virtio->u8PciCfgOff = u8PciCapOff;
553 DBG_VIRTIO("VirtIO PCI CAP window offset: %x\n", u8PciCapOff);
554 break;
555 default:
556 DBG_VIRTIO("VirtIO SCSI HBA with unknown PCI capability type 0x%x\n", u8PciVirtioCfg);
557 break;
558 }
559 }
560
561 u8PciCapOff = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff + 1);
562 }
563
564 /* Reset the device. */
565 u8DevStat = VIRTIO_CMN_REG_DEV_STS_F_RST;
566 virtio_reg_common_write_u8(virtio, VIRTIO_COMMON_REG_DEV_STS, u8DevStat);
567 /* Acknowledge presence. */
568 u8DevStat |= VIRTIO_CMN_REG_DEV_STS_F_ACK;
569 virtio_reg_common_write_u8(virtio, VIRTIO_COMMON_REG_DEV_STS, u8DevStat);
570 /* Our driver knows how to operate the device. */
571 u8DevStat |= VIRTIO_CMN_REG_DEV_STS_F_DRV;
572 virtio_reg_common_write_u8(virtio, VIRTIO_COMMON_REG_DEV_STS, u8DevStat);
573
574#if 0
575 /* Read the feature bits and only program the VIRTIO_CMN_REG_DEV_FEAT_SCSI_INOUT bit if available. */
576 fFeatures = virtio_reg_common_read_u32(virtio, VIRTIO_COMMON_REG_DEV_FEAT);
577 fFeatures &= VIRTIO_CMN_REG_DEV_FEAT_SCSI_INOUT;
578#endif
579
580 /* Check that the device is sane. */
581 if ( virtio_reg_dev_cfg_read_u32(virtio, VIRTIO_DEV_CFG_REG_Q_NUM) < 1
582 || virtio_reg_dev_cfg_read_u32(virtio, VIRTIO_DEV_CFG_REG_CDB_SZ) < 16
583 || virtio_reg_dev_cfg_read_u32(virtio, VIRTIO_DEV_CFG_REG_SENSE_SZ) < 32
584 || virtio_reg_dev_cfg_read_u32(virtio, VIRTIO_DEV_CFG_REG_SECT_MAX) < 1)
585 {
586 DBG_VIRTIO("VirtIO-SCSI: Invalid SCSI device configuration, ignoring device\n");
587 return 1;
588 }
589
590 virtio_reg_common_write_u32(virtio, VIRTIO_COMMON_REG_DRV_FEAT, VIRTIO_CMN_REG_DEV_FEAT_SCSI_INOUT);
591
592 /* Set the features OK bit. */
593 u8DevStat |= VIRTIO_CMN_REG_DEV_STS_F_FEAT_OK;
594 virtio_reg_common_write_u8(virtio, VIRTIO_COMMON_REG_DEV_STS, u8DevStat);
595
596 /* Read again and check the the okay bit is still set. */
597 if (!(virtio_reg_common_read_u8(virtio, VIRTIO_COMMON_REG_DEV_STS) & VIRTIO_CMN_REG_DEV_STS_F_FEAT_OK))
598 {
599 DBG_VIRTIO("VirtIO-SCSI: Device doesn't accept our feature set, ignoring device\n");
600 return 1;
601 }
602
603 /* Disable event and control queue. */
604 virtio_reg_common_write_u16(virtio, VIRTIO_COMMON_REG_Q_SELECT, VIRTIO_SCSI_Q_CONTROL);
605 virtio_reg_common_write_u16(virtio, VIRTIO_COMMON_REG_Q_SIZE, 0);
606 virtio_reg_common_write_u16(virtio, VIRTIO_COMMON_REG_Q_ENABLE, 0);
607
608 virtio_reg_common_write_u16(virtio, VIRTIO_COMMON_REG_Q_SELECT, VIRTIO_SCSI_Q_EVENT);
609 virtio_reg_common_write_u16(virtio, VIRTIO_COMMON_REG_Q_SIZE, 0);
610 virtio_reg_common_write_u16(virtio, VIRTIO_COMMON_REG_Q_ENABLE, 0);
611
612 /* Setup the request queue. */
613 virtio_reg_common_write_u16(virtio, VIRTIO_COMMON_REG_Q_SELECT, VIRTIO_SCSI_Q_REQUEST);
614 virtio_reg_common_write_u16(virtio, VIRTIO_COMMON_REG_Q_SIZE, VIRTIO_SCSI_RING_ELEM);
615 virtio_reg_common_write_u16(virtio, VIRTIO_COMMON_REG_Q_ENABLE, 1);
616
617 /* Set queue area addresses (only low part, leave high part 0). */
618 virtio_reg_common_write_u32(virtio, VIRTIO_COMMON_REG_Q_DESC, virtio_addr_to_phys(&virtio->Queue.aDescTbl[0]));
619 virtio_reg_common_write_u32(virtio, VIRTIO_COMMON_REG_Q_DESC + 4, 0);
620
621 virtio_reg_common_write_u32(virtio, VIRTIO_COMMON_REG_Q_DRIVER, virtio_addr_to_phys(&virtio->Queue.AvailRing));
622 virtio_reg_common_write_u32(virtio, VIRTIO_COMMON_REG_Q_DRIVER + 4, 0);
623
624 virtio_reg_common_write_u32(virtio, VIRTIO_COMMON_REG_Q_DEVICE, virtio_addr_to_phys(&virtio->Queue.UsedRing));
625 virtio_reg_common_write_u32(virtio, VIRTIO_COMMON_REG_Q_DEVICE + 4, 0);
626
627 virtio_reg_dev_cfg_write_u32(virtio, VIRTIO_DEV_CFG_REG_CDB_SZ, VIRTIO_SCSI_CDB_SZ);
628 virtio_reg_dev_cfg_write_u32(virtio, VIRTIO_DEV_CFG_REG_SENSE_SZ, VIRTIO_SCSI_SENSE_SZ);
629
630 DBG_VIRTIO("VirtIO: Q notify offset 0x%x\n", virtio_reg_common_read_u16(virtio, VIRTIO_COMMON_REG_Q_NOTIFY_OFF));
631 virtio->Queue.offNotify = virtio_reg_common_read_u16(virtio, VIRTIO_COMMON_REG_Q_NOTIFY_OFF) * virtio->u32NotifyOffMult;
632
633 /* Bring the device into operational mode. */
634 u8DevStat |= VIRTIO_CMN_REG_DEV_STS_F_DRV_OK;
635 virtio_reg_common_write_u8(virtio, VIRTIO_COMMON_REG_DEV_STS, u8DevStat);
636
637 return 0;
638}
639
640/**
641 * Init the VirtIO SCSI driver and detect attached disks.
642 */
643int virtio_scsi_init(void __far *pvHba, uint8_t u8Bus, uint8_t u8DevFn)
644{
645 virtio_t __far *virtio = (virtio_t __far *)pvHba;
646 uint8_t u8PciCapOff;
647 uint8_t u8PciCapOffVirtIo = VBOX_VIRTIO_NIL_CFG;
648 uint8_t u8PciCapVirtioSeen = 0;
649
650 /* Examine the capability list and search for the VirtIO specific capabilities. */
651 u8PciCapOff = pci_read_config_byte(u8Bus, u8DevFn, PCI_CONFIG_CAP);
652
653 while (u8PciCapOff != 0)
654 {
655 uint8_t u8PciCapId = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff);
656 uint8_t cbPciCap = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff + 2); /* Capability length. */
657
658 DBG_VIRTIO("Capability ID 0x%x at 0x%x\n", u8PciCapId, u8PciCapOff);
659
660 if ( u8PciCapId == PCI_CAP_ID_VNDR
661 && cbPciCap >= sizeof(virtio_pci_cap_t))
662 {
663 /* Read in the config type and see what we got. */
664 uint8_t u8PciVirtioCfg = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff + 3);
665
666 if (u8PciCapOffVirtIo == VBOX_VIRTIO_NIL_CFG)
667 u8PciCapOffVirtIo = u8PciCapOff;
668
669 DBG_VIRTIO("VirtIO: CFG ID 0x%x\n", u8PciVirtioCfg);
670 switch (u8PciVirtioCfg)
671 {
672 case VIRTIO_PCI_CAP_COMMON_CFG:
673 case VIRTIO_PCI_CAP_NOTIFY_CFG:
674 case VIRTIO_PCI_CAP_ISR_CFG:
675 case VIRTIO_PCI_CAP_DEVICE_CFG:
676 case VIRTIO_PCI_CAP_PCI_CFG:
677 u8PciCapVirtioSeen |= 1 << (u8PciVirtioCfg - 1);
678 break;
679 default:
680 DBG_VIRTIO("VirtIO SCSI HBA with unknown PCI capability type 0x%x\n", u8PciVirtioCfg);
681 }
682 }
683
684 u8PciCapOff = pci_read_config_byte(u8Bus, u8DevFn, u8PciCapOff + 1);
685 }
686
687 /* Initialize the controller if all required PCI capabilities where found. */
688 if ( u8PciCapOffVirtIo != VBOX_VIRTIO_NIL_CFG
689 && u8PciCapVirtioSeen == 0x1f)
690 {
691 DBG_VIRTIO("VirtIO SCSI HBA with all required capabilities at 0x%x\n", u8PciCapOffVirtIo);
692
693 /* Enable PCI memory, I/O, bus mastering access in command register. */
694 pci_write_config_word(u8Bus, u8DevFn, 4, 0x7);
695 return virtio_scsi_hba_init(virtio, u8Bus, u8DevFn, u8PciCapOffVirtIo);
696 }
697 else
698 DBG_VIRTIO("VirtIO SCSI HBA with no usable PCI config access!\n");
699
700 return 1;
701}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use