VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VBoxSCSI.cpp@ 44107

Last change on this file since 44107 was 43659, checked in by vboxsync, 12 years ago

VBoxSCSI: Slightly extended interface to allow error reporting.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.6 KB
Line 
1/* $Id: VBoxSCSI.cpp 43659 2012-10-16 16:25:26Z vboxsync $ */
2/** @file
3 *
4 * VBox storage devices:
5 * Simple SCSI interface for BIOS access
6 */
7
8/*
9 * Copyright (C) 2006-2009 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23//#define DEBUG
24#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC /* @todo: Create extra group. */
25
26#if defined(IN_R0) || defined(IN_RC)
27# error This device has no R0 or GC components
28#endif
29
30#include <VBox/vmm/pdmdev.h>
31#include <VBox/vmm/pgm.h>
32#include <iprt/asm.h>
33#include <iprt/mem.h>
34#include <iprt/thread.h>
35#include <iprt/string.h>
36
37#include "VBoxSCSI.h"
38
39static void vboxscsiReset(PVBOXSCSI pVBoxSCSI)
40{
41 pVBoxSCSI->regIdentify = 0;
42 pVBoxSCSI->cbCDB = 0;
43 memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
44 pVBoxSCSI->iCDB = 0;
45 pVBoxSCSI->fBusy = false;
46 pVBoxSCSI->rcCompletion = 0;
47 pVBoxSCSI->uTargetDevice = 0;
48 pVBoxSCSI->cbBuf = 0;
49 pVBoxSCSI->iBuf = 0;
50 if (pVBoxSCSI->pBuf)
51 RTMemFree(pVBoxSCSI->pBuf);
52
53 pVBoxSCSI->pBuf = NULL;
54 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
55}
56
57/**
58 * Initializes the state for the SCSI interface.
59 *
60 * @returns VBox status code.
61 * @param pVBoxSCSI Pointer to the unitialized SCSI state.
62 */
63int vboxscsiInitialize(PVBOXSCSI pVBoxSCSI)
64{
65 pVBoxSCSI->pBuf = NULL;
66 vboxscsiReset(pVBoxSCSI);
67
68 return VINF_SUCCESS;
69}
70
71/**
72 * Reads a register value.
73 *
74 * @returns VBox status code.
75 * @param pVBoxSCSI Pointer to the SCSI state.
76 * @param iRegister Index of the register to read.
77 * @param pu32Value Where to store the content of the register.
78 */
79int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32Value)
80{
81 uint8_t uVal = 0;
82
83 switch (iRegister)
84 {
85 case 0:
86 {
87 if (ASMAtomicReadBool(&pVBoxSCSI->fBusy) == true)
88 {
89 uVal |= VBOX_SCSI_BUSY;
90 /* There is an I/O operation in progress.
91 * Yield the execution thread to let the I/O thread make progress.
92 */
93 RTThreadYield();
94 }
95 if (pVBoxSCSI->rcCompletion)
96 uVal |= VBOX_SCSI_ERROR;
97 break;
98 }
99 case 1:
100 {
101 /* If we're not in the 'command ready' state, there may not even be a buffer yet. */
102 if ((pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY) && pVBoxSCSI->cbBuf > 0)
103 {
104 AssertMsg(pVBoxSCSI->pBuf, ("pBuf is NULL\n"));
105 Assert(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY);
106 Assert(!pVBoxSCSI->fBusy);
107 uVal = pVBoxSCSI->pBuf[pVBoxSCSI->iBuf];
108 pVBoxSCSI->iBuf++;
109 pVBoxSCSI->cbBuf--;
110 if (pVBoxSCSI->cbBuf == 0)
111 {
112 /** The guest read the last byte from the data in buffer.
113 * Clear everything and reset command buffer.
114 */
115 RTMemFree(pVBoxSCSI->pBuf);
116 pVBoxSCSI->pBuf = NULL;
117 pVBoxSCSI->cbCDB = 0;
118 pVBoxSCSI->iCDB = 0;
119 pVBoxSCSI->iBuf = 0;
120 pVBoxSCSI->rcCompletion = 0;
121 pVBoxSCSI->uTargetDevice = 0;
122 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
123 memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
124 }
125 }
126 break;
127 }
128 case 2:
129 {
130 uVal = pVBoxSCSI->regIdentify;
131 break;
132 }
133 case 3:
134 {
135 uVal = pVBoxSCSI->rcCompletion;
136 break;
137 }
138 default:
139 AssertMsgFailed(("Invalid register to read from %u\n", iRegister));
140 }
141
142 *pu32Value = uVal;
143
144 return VINF_SUCCESS;
145}
146
147/**
148 * Writes to a register.
149 *
150 * @returns VBox status code.
151 * VERR_MORE_DATA if a command is ready to be sent to the SCSI driver.
152 * @param pVBoxSCSI Pointer to the SCSI state.
153 * @param iRegister Index of the register to write to.
154 * @param uVal Value to write.
155 */
156int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
157{
158 int rc = VINF_SUCCESS;
159
160 switch (iRegister)
161 {
162 case 0:
163 {
164 if (pVBoxSCSI->enmState == VBOXSCSISTATE_NO_COMMAND)
165 {
166 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_TXDIR;
167 pVBoxSCSI->uTargetDevice = uVal;
168 }
169 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_TXDIR)
170 {
171 if (uVal != VBOXSCSI_TXDIR_FROM_DEVICE && uVal != VBOXSCSI_TXDIR_TO_DEVICE)
172 vboxscsiReset(pVBoxSCSI);
173 else
174 {
175 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_CDB_SIZE_BUFHI;
176 pVBoxSCSI->uTxDir = uVal;
177 }
178 }
179 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_CDB_SIZE_BUFHI)
180 {
181 uint8_t cbCDB = uVal & 0x0F;
182
183 if (cbCDB > VBOXSCSI_CDB_SIZE_MAX)
184 vboxscsiReset(pVBoxSCSI);
185 else
186 {
187 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_LSB;
188 pVBoxSCSI->cbCDB = cbCDB;
189 pVBoxSCSI->cbBuf = (uVal & 0xF0) << 12; /* Bits 16-19 of buffer size. */
190 }
191 }
192 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_LSB)
193 {
194 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_MID;
195 pVBoxSCSI->cbBuf |= uVal; /* Bits 0-7 of buffer size. */
196 }
197 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_MID)
198 {
199 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_COMMAND;
200 pVBoxSCSI->cbBuf |= (((uint16_t)uVal) << 8); /* Bits 8-15 of buffer size. */
201 }
202 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_COMMAND)
203 {
204 pVBoxSCSI->aCDB[pVBoxSCSI->iCDB] = uVal;
205 pVBoxSCSI->iCDB++;
206
207 /* Check if we have all necessary command data. */
208 if (pVBoxSCSI->iCDB == pVBoxSCSI->cbCDB)
209 {
210 Log(("%s: Command ready for processing\n", __FUNCTION__));
211 pVBoxSCSI->enmState = VBOXSCSISTATE_COMMAND_READY;
212 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
213 {
214 /* This is a write allocate buffer. */
215 pVBoxSCSI->pBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
216 if (!pVBoxSCSI->pBuf)
217 return VERR_NO_MEMORY;
218 }
219 else
220 {
221 /* This is a read from the device. */
222 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
223 rc = VERR_MORE_DATA; /** @todo Better return value to indicate ready command? */
224 }
225 }
226 }
227 else
228 AssertMsgFailed(("Invalid state %d\n", pVBoxSCSI->enmState));
229 break;
230 }
231 case 1:
232 {
233 if ( pVBoxSCSI->enmState != VBOXSCSISTATE_COMMAND_READY
234 || pVBoxSCSI->uTxDir != VBOXSCSI_TXDIR_TO_DEVICE)
235 {
236 /* Reset the state */
237 vboxscsiReset(pVBoxSCSI);
238 }
239 else
240 {
241 pVBoxSCSI->pBuf[pVBoxSCSI->iBuf++] = uVal;
242 if (pVBoxSCSI->iBuf == pVBoxSCSI->cbBuf)
243 {
244 rc = VERR_MORE_DATA;
245 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
246 }
247 }
248 break;
249 }
250 case 2:
251 {
252 pVBoxSCSI->regIdentify = uVal;
253 break;
254 }
255 case 3:
256 {
257 /* Reset */
258 vboxscsiReset(pVBoxSCSI);
259 break;
260 }
261 default:
262 AssertMsgFailed(("Invalid register to write to %u\n", iRegister));
263 }
264
265 return rc;
266}
267
268/**
269 * Sets up a SCSI request which the owning SCSI device can process.
270 *
271 * @returns VBox status code.
272 * @param pVBoxSCSI Pointer to the SCSI state.
273 * @param pScsiRequest Pointer to a scsi request to setup.
274 * @param puTargetDevice Where to store the target device ID.
275 */
276int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint32_t *puTargetDevice)
277{
278 int rc = VINF_SUCCESS;
279
280 LogFlowFunc(("pVBoxSCSI=%#p pScsiRequest=%#p puTargetDevice=%#p\n", pVBoxSCSI, pScsiRequest, puTargetDevice));
281
282 AssertMsg(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, ("Invalid state %u\n", pVBoxSCSI->enmState));
283
284 /* Clear any errors from a previous request. */
285 pVBoxSCSI->rcCompletion = 0;
286
287 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
288 {
289 if (pVBoxSCSI->pBuf)
290 RTMemFree(pVBoxSCSI->pBuf);
291
292 pVBoxSCSI->pBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
293 if (!pVBoxSCSI->pBuf)
294 return VERR_NO_MEMORY;
295 }
296
297 /* Allocate scatter gather element. */
298 pScsiRequest->paScatterGatherHead = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * 1); /* Only one element. */
299 if (!pScsiRequest->paScatterGatherHead)
300 {
301 RTMemFree(pVBoxSCSI->pBuf);
302 pVBoxSCSI->pBuf = NULL;
303 return VERR_NO_MEMORY;
304 }
305
306 /* Allocate sense buffer. */
307 pScsiRequest->cbSenseBuffer = 18;
308 pScsiRequest->pbSenseBuffer = (uint8_t *)RTMemAllocZ(pScsiRequest->cbSenseBuffer);
309
310 pScsiRequest->cbCDB = pVBoxSCSI->cbCDB;
311 pScsiRequest->pbCDB = pVBoxSCSI->aCDB;
312 pScsiRequest->uLogicalUnit = 0;
313 pScsiRequest->cbScatterGather = pVBoxSCSI->cbBuf;
314 pScsiRequest->cScatterGatherEntries = 1;
315
316 pScsiRequest->paScatterGatherHead[0].cbSeg = pVBoxSCSI->cbBuf;
317 pScsiRequest->paScatterGatherHead[0].pvSeg = pVBoxSCSI->pBuf;
318
319 *puTargetDevice = pVBoxSCSI->uTargetDevice;
320
321 return rc;
322}
323
324/**
325 * Notifies the device that a request finished and the incoming data
326 * is ready at the incoming data port.
327 */
328int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, int rcCompletion)
329{
330 LogFlowFunc(("pVBoxSCSI=%#p pScsiRequest=%#p\n", pVBoxSCSI, pScsiRequest));
331 RTMemFree(pScsiRequest->paScatterGatherHead);
332 RTMemFree(pScsiRequest->pbSenseBuffer);
333
334 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
335 {
336 if (pVBoxSCSI->pBuf)
337 RTMemFree(pVBoxSCSI->pBuf);
338 pVBoxSCSI->pBuf = NULL;
339 pVBoxSCSI->cbBuf = 0;
340 pVBoxSCSI->cbCDB = 0;
341 pVBoxSCSI->iCDB = 0;
342 pVBoxSCSI->iBuf = 0;
343 pVBoxSCSI->uTargetDevice = 0;
344 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
345 memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
346 }
347
348 pVBoxSCSI->rcCompletion = rcCompletion;
349
350 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, false);
351
352 return VINF_SUCCESS;
353}
354
355int vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
356 RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
357{
358 RTGCPTR GCDst = *pGCPtrDst;
359 uint32_t cbTransfer = *pcTransfer * cb;
360
361 LogFlowFunc(("pDevIns=%#p pVBoxSCSI=%#p iRegister=%d cTransfer=%u cb=%u\n",
362 pDevIns, pVBoxSCSI, iRegister, *pcTransfer, cb));
363
364 /* Read string only valid for data in register. */
365 AssertMsg(iRegister == 1, ("Hey only register 1 can be read from with string\n"));
366
367 /* Accesses without a valid buffer will be ignored. */
368 if (!pVBoxSCSI->pBuf)
369 return VINF_SUCCESS;
370
371 /* Also ignore attempts to read more data than is available. */
372 Assert(cbTransfer <= pVBoxSCSI->cbBuf);
373 if (cbTransfer > pVBoxSCSI->cbBuf)
374 cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
375
376 int rc = PGMPhysSimpleDirtyWriteGCPtr(PDMDevHlpGetVMCPU(pDevIns), GCDst, pVBoxSCSI->pBuf + pVBoxSCSI->iBuf, cbTransfer);
377 AssertRC(rc);
378
379 *pGCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCDst + cbTransfer);
380 *pcTransfer = 0;
381
382 /* Advance current buffer position. */
383 pVBoxSCSI->iBuf += cbTransfer;
384 pVBoxSCSI->cbBuf -= cbTransfer;
385
386 if (pVBoxSCSI->cbBuf == 0)
387 {
388 /** The guest read the last byte from the data in buffer.
389 * Clear everything and reset command buffer.
390 */
391 RTMemFree(pVBoxSCSI->pBuf);
392 pVBoxSCSI->pBuf = NULL;
393 pVBoxSCSI->cbCDB = 0;
394 pVBoxSCSI->iCDB = 0;
395 pVBoxSCSI->iBuf = 0;
396 pVBoxSCSI->uTargetDevice = 0;
397 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
398 memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
399 }
400
401 return rc;
402}
403
404int vboxscsiWriteString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
405 RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
406{
407 RTGCPTR GCSrc = *pGCPtrSrc;
408 uint32_t cbTransfer = *pcTransfer * cb;
409
410 /* Write string only valid for data in/out register. */
411 AssertMsg(iRegister == 1, ("Hey only register 1 can be written to with string\n"));
412
413 /* Accesses without a valid buffer will be ignored. */
414 if (!pVBoxSCSI->pBuf)
415 return VINF_SUCCESS;
416
417 Assert(cbTransfer <= pVBoxSCSI->cbBuf);
418 if (cbTransfer > pVBoxSCSI->cbBuf)
419 cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
420
421 int rc = PDMDevHlpPhysReadGCVirt(pDevIns, pVBoxSCSI->pBuf + pVBoxSCSI->iBuf, GCSrc, cbTransfer);
422 AssertRC(rc);
423
424 /* Advance current buffer position. */
425 pVBoxSCSI->iBuf += cbTransfer;
426 pVBoxSCSI->cbBuf -= cbTransfer;
427
428 *pGCPtrSrc = (RTGCPTR)((RTGCUINTPTR)GCSrc + cbTransfer);
429 *pcTransfer = 0;
430
431 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
432 return VERR_MORE_DATA;
433}
434
435void vboxscsiSetRequestRedo(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
436{
437 AssertMsg(pVBoxSCSI->fBusy, ("No request to redo\n"));
438
439 RTMemFree(pScsiRequest->paScatterGatherHead);
440 RTMemFree(pScsiRequest->pbSenseBuffer);
441
442 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
443 {
444 AssertPtr(pVBoxSCSI->pBuf);
445 }
446}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette