VirtualBox

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

Last change on this file since 44760 was 44533, checked in by vboxsync, 12 years ago

VBoxSVSI.cpp: doxygen and hungarian adjustments.

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