VirtualBox

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

Last change on this file since 56428 was 56428, checked in by vboxsync, 9 years ago

VBoxSCSI: Remove needless code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.6 KB
Line 
1/* $Id: VBoxSCSI.cpp 56428 2015-06-15 11:19:08Z vboxsync $ */
2/** @file
3 * VBox storage devices - Simple SCSI interface for BIOS access.
4 */
5
6/*
7 * Copyright (C) 2006-2015 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, bool fEverything)
42{
43 if (fEverything)
44 {
45 pVBoxSCSI->regIdentify = 0;
46 pVBoxSCSI->fBusy = false;
47 }
48 pVBoxSCSI->cbCDB = 0;
49 RT_ZERO(pVBoxSCSI->abCDB);
50 pVBoxSCSI->iCDB = 0;
51 pVBoxSCSI->rcCompletion = 0;
52 pVBoxSCSI->uTargetDevice = 0;
53 pVBoxSCSI->cbBuf = 0;
54 pVBoxSCSI->iBuf = 0;
55 if (pVBoxSCSI->pbBuf)
56 RTMemFree(pVBoxSCSI->pbBuf);
57 pVBoxSCSI->pbBuf = NULL;
58 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
59}
60
61/**
62 * Initializes the state for the SCSI interface.
63 *
64 * @returns VBox status code.
65 * @param pVBoxSCSI Pointer to the unitialized SCSI state.
66 */
67int vboxscsiInitialize(PVBOXSCSI pVBoxSCSI)
68{
69 pVBoxSCSI->pbBuf = NULL;
70 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
71
72 return VINF_SUCCESS;
73}
74
75/**
76 * Reads a register value.
77 *
78 * @returns VBox status code.
79 * @param pVBoxSCSI Pointer to the SCSI state.
80 * @param iRegister Index of the register to read.
81 * @param pu32Value Where to store the content of the register.
82 */
83int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32Value)
84{
85 uint8_t uVal = 0;
86
87 switch (iRegister)
88 {
89 case 0:
90 {
91 if (ASMAtomicReadBool(&pVBoxSCSI->fBusy) == true)
92 {
93 uVal |= VBOX_SCSI_BUSY;
94 /* There is an I/O operation in progress.
95 * Yield the execution thread to let the I/O thread make progress.
96 */
97 RTThreadYield();
98 }
99 if (pVBoxSCSI->rcCompletion)
100 uVal |= VBOX_SCSI_ERROR;
101 break;
102 }
103 case 1:
104 {
105 /* If we're not in the 'command ready' state, there may not even be a buffer yet. */
106 if ( pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY
107 && pVBoxSCSI->cbBufLeft > 0)
108 {
109 AssertMsg(pVBoxSCSI->pbBuf, ("pBuf is NULL\n"));
110 Assert(!pVBoxSCSI->fBusy);
111 uVal = pVBoxSCSI->pbBuf[pVBoxSCSI->iBuf];
112 pVBoxSCSI->iBuf++;
113 pVBoxSCSI->cbBufLeft--;
114
115 /* When the guest reads the last byte from the data in buffer, clear
116 everything and reset command buffer. */
117 if (pVBoxSCSI->cbBufLeft == 0)
118 vboxscsiReset(pVBoxSCSI, false /*fEverything*/);
119 }
120 break;
121 }
122 case 2:
123 {
124 uVal = pVBoxSCSI->regIdentify;
125 break;
126 }
127 case 3:
128 {
129 uVal = pVBoxSCSI->rcCompletion;
130 break;
131 }
132 default:
133 AssertMsgFailed(("Invalid register to read from %u\n", iRegister));
134 }
135
136 *pu32Value = uVal;
137
138 return VINF_SUCCESS;
139}
140
141/**
142 * Writes to a register.
143 *
144 * @returns VBox status code.
145 * @retval VERR_MORE_DATA if a command is ready to be sent to the SCSI driver.
146 * @param pVBoxSCSI Pointer to the SCSI state.
147 * @param iRegister Index of the register to write to.
148 * @param uVal Value to write.
149 */
150int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
151{
152 int rc = VINF_SUCCESS;
153
154 switch (iRegister)
155 {
156 case 0:
157 {
158 if (pVBoxSCSI->enmState == VBOXSCSISTATE_NO_COMMAND)
159 {
160 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_TXDIR;
161 pVBoxSCSI->uTargetDevice = uVal;
162 }
163 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_TXDIR)
164 {
165 if (uVal != VBOXSCSI_TXDIR_FROM_DEVICE && uVal != VBOXSCSI_TXDIR_TO_DEVICE)
166 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
167 else
168 {
169 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_CDB_SIZE_BUFHI;
170 pVBoxSCSI->uTxDir = uVal;
171 }
172 }
173 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_CDB_SIZE_BUFHI)
174 {
175 uint8_t cbCDB = uVal & 0x0F;
176 if (cbCDB > VBOXSCSI_CDB_SIZE_MAX)
177 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
178 else
179 {
180 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_LSB;
181 pVBoxSCSI->cbCDB = cbCDB;
182 pVBoxSCSI->cbBuf = (uVal & 0xF0) << 12; /* Bits 16-19 of buffer size. */
183 }
184 }
185 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_LSB)
186 {
187 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_MID;
188 pVBoxSCSI->cbBuf |= uVal; /* Bits 0-7 of buffer size. */
189 }
190 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_MID)
191 {
192 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_COMMAND;
193 pVBoxSCSI->cbBuf |= (((uint16_t)uVal) << 8); /* Bits 8-15 of buffer size. */
194 }
195 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_COMMAND)
196 {
197 pVBoxSCSI->abCDB[pVBoxSCSI->iCDB] = uVal;
198 pVBoxSCSI->iCDB++;
199
200 /* Check if we have all necessary command data. */
201 if (pVBoxSCSI->iCDB == pVBoxSCSI->cbCDB)
202 {
203 Log(("%s: Command ready for processing\n", __FUNCTION__));
204 pVBoxSCSI->enmState = VBOXSCSISTATE_COMMAND_READY;
205 pVBoxSCSI->cbBufLeft = pVBoxSCSI->cbBuf;
206 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
207 {
208 /* This is a write allocate buffer. */
209 pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
210 if (!pVBoxSCSI->pbBuf)
211 return VERR_NO_MEMORY;
212 }
213 else
214 {
215 /* This is a read from the device. */
216 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
217 rc = VERR_MORE_DATA; /** @todo Better return value to indicate ready command? */
218 }
219 }
220 }
221 else
222 AssertMsgFailed(("Invalid state %d\n", pVBoxSCSI->enmState));
223 break;
224 }
225
226 case 1:
227 {
228 if ( pVBoxSCSI->enmState != VBOXSCSISTATE_COMMAND_READY
229 || pVBoxSCSI->uTxDir != VBOXSCSI_TXDIR_TO_DEVICE)
230 {
231 /* Reset the state */
232 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
233 }
234 else if (pVBoxSCSI->cbBufLeft > 0)
235 {
236 pVBoxSCSI->pbBuf[pVBoxSCSI->iBuf++] = uVal;
237 pVBoxSCSI->cbBufLeft--;
238 if (pVBoxSCSI->cbBufLeft == 0)
239 {
240 rc = VERR_MORE_DATA;
241 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
242 }
243 }
244 /* else: Ignore extra data, request pending or something. */
245 break;
246 }
247
248 case 2:
249 {
250 pVBoxSCSI->regIdentify = uVal;
251 break;
252 }
253
254 case 3:
255 {
256 /* Reset */
257 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
258 break;
259 }
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->pbBuf)
290 RTMemFree(pVBoxSCSI->pbBuf);
291
292 pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
293 if (!pVBoxSCSI->pbBuf)
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->pbBuf);
302 pVBoxSCSI->pbBuf = 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->abCDB;
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->pbBuf;
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 vboxscsiReset(pVBoxSCSI, false /*fEverything*/);
336
337 pVBoxSCSI->rcCompletion = rcCompletion;
338
339 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, false);
340
341 return VINF_SUCCESS;
342}
343
344int vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
345 uint8_t *pbDst, uint32_t *pcTransfers, unsigned cb)
346{
347 LogFlowFunc(("pDevIns=%#p pVBoxSCSI=%#p iRegister=%d cTransfers=%u cb=%u\n",
348 pDevIns, pVBoxSCSI, iRegister, *pcTransfers, cb));
349
350 /*
351 * Check preconditions, fall back to non-string I/O handler.
352 */
353 Assert(*pcTransfers > 0);
354
355 /* Read string only valid for data in register. */
356 AssertMsgReturn(iRegister == 1, ("Hey! Only register 1 can be read from with string!\n"), VINF_SUCCESS);
357
358 /* Accesses without a valid buffer will be ignored. */
359 AssertReturn(pVBoxSCSI->pbBuf, VINF_SUCCESS);
360
361 /* Check state. */
362 AssertReturn(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, VINF_SUCCESS);
363 Assert(!pVBoxSCSI->fBusy);
364
365 /*
366 * Also ignore attempts to read more data than is available.
367 */
368 int rc = VINF_SUCCESS;
369 uint32_t cbTransfer = *pcTransfers * cb;
370 if (pVBoxSCSI->cbBufLeft > 0)
371 {
372 Assert(cbTransfer <= pVBoxSCSI->cbBuf);
373 if (cbTransfer > pVBoxSCSI->cbBuf)
374 {
375 memset(pbDst + pVBoxSCSI->cbBuf, 0xff, cbTransfer - pVBoxSCSI->cbBuf);
376 cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
377 }
378
379 /* Copy the data and adance the buffer position. */
380 memcpy(pbDst, pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, cbTransfer);
381
382 /* Advance current buffer position. */
383 pVBoxSCSI->iBuf += cbTransfer;
384 pVBoxSCSI->cbBufLeft -= cbTransfer;
385
386 /* When the guest reads the last byte from the data in buffer, clear
387 everything and reset command buffer. */
388 if (pVBoxSCSI->cbBufLeft == 0)
389 vboxscsiReset(pVBoxSCSI, false /*fEverything*/);
390 }
391 else
392 {
393 AssertFailed();
394 memset(pbDst, 0, cbTransfer);
395 }
396 *pcTransfers = 0;
397
398 return rc;
399}
400
401int vboxscsiWriteString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
402 uint8_t const *pbSrc, uint32_t *pcTransfers, unsigned cb)
403{
404 /*
405 * Check preconditions, fall back to non-string I/O handler.
406 */
407 Assert(*pcTransfers > 0);
408 /* Write string only valid for data in/out register. */
409 AssertMsgReturn(iRegister == 1, ("Hey! Only register 1 can be written to with string!\n"), VINF_SUCCESS);
410
411 /* Accesses without a valid buffer will be ignored. */
412 AssertReturn(pVBoxSCSI->pbBuf, VINF_SUCCESS);
413
414 /* State machine assumptions. */
415 AssertReturn(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, VINF_SUCCESS);
416 AssertReturn(pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE, VINF_SUCCESS);
417
418 /*
419 * Ignore excess data (not supposed to happen).
420 */
421 int rc = VINF_SUCCESS;
422 if (pVBoxSCSI->cbBufLeft > 0)
423 {
424 uint32_t cbTransfer = RT_MIN(*pcTransfers * cb, pVBoxSCSI->cbBufLeft);
425
426 /* Copy the data and adance the buffer position. */
427 memcpy(pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, pbSrc, cbTransfer);
428 pVBoxSCSI->iBuf += cbTransfer;
429 pVBoxSCSI->cbBufLeft -= cbTransfer;
430
431 /* If we've reached the end, tell the caller to submit the command. */
432 if (pVBoxSCSI->cbBufLeft == 0)
433 {
434 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
435 rc = VERR_MORE_DATA;
436 }
437 }
438 else
439 AssertFailed();
440 *pcTransfers = 0;
441
442 return rc;
443}
444
445void vboxscsiSetRequestRedo(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
446{
447 AssertMsg(pVBoxSCSI->fBusy, ("No request to redo\n"));
448
449 RTMemFree(pScsiRequest->paScatterGatherHead);
450 RTMemFree(pScsiRequest->pbSenseBuffer);
451
452 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
453 {
454 AssertPtr(pVBoxSCSI->pbBuf);
455 }
456}
457
458DECLHIDDEN(int) vboxscsiR3LoadExec(PVBOXSCSI pVBoxSCSI, PSSMHANDLE pSSM)
459{
460 SSMR3GetU8 (pSSM, &pVBoxSCSI->regIdentify);
461 SSMR3GetU8 (pSSM, &pVBoxSCSI->uTargetDevice);
462 SSMR3GetU8 (pSSM, &pVBoxSCSI->uTxDir);
463 SSMR3GetU8 (pSSM, &pVBoxSCSI->cbCDB);
464 SSMR3GetMem (pSSM, &pVBoxSCSI->abCDB[0], sizeof(pVBoxSCSI->abCDB));
465 SSMR3GetU8 (pSSM, &pVBoxSCSI->iCDB);
466 SSMR3GetU32 (pSSM, &pVBoxSCSI->cbBufLeft);
467 SSMR3GetU32 (pSSM, &pVBoxSCSI->iBuf);
468 SSMR3GetBool(pSSM, (bool *)&pVBoxSCSI->fBusy);
469 SSMR3GetU8 (pSSM, (uint8_t *)&pVBoxSCSI->enmState);
470
471 /*
472 * Old saved states only save the size of the buffer left to read/write.
473 * To avoid changing the saved state version we can just calculate the original
474 * buffer size from the offset and remaining size.
475 */
476 pVBoxSCSI->cbBuf = pVBoxSCSI->cbBufLeft + pVBoxSCSI->iBuf;
477
478 if (pVBoxSCSI->cbBuf)
479 {
480 pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
481 if (!pVBoxSCSI->pbBuf)
482 return VERR_NO_MEMORY;
483
484 SSMR3GetMem(pSSM, pVBoxSCSI->pbBuf, pVBoxSCSI->cbBuf);
485 }
486
487 return VINF_SUCCESS;
488}
489
490DECLHIDDEN(int) vboxscsiR3SaveExec(PVBOXSCSI pVBoxSCSI, PSSMHANDLE pSSM)
491{
492 SSMR3PutU8 (pSSM, pVBoxSCSI->regIdentify);
493 SSMR3PutU8 (pSSM, pVBoxSCSI->uTargetDevice);
494 SSMR3PutU8 (pSSM, pVBoxSCSI->uTxDir);
495 SSMR3PutU8 (pSSM, pVBoxSCSI->cbCDB);
496 SSMR3PutMem (pSSM, pVBoxSCSI->abCDB, sizeof(pVBoxSCSI->abCDB));
497 SSMR3PutU8 (pSSM, pVBoxSCSI->iCDB);
498 SSMR3PutU32 (pSSM, pVBoxSCSI->cbBufLeft);
499 SSMR3PutU32 (pSSM, pVBoxSCSI->iBuf);
500 SSMR3PutBool (pSSM, pVBoxSCSI->fBusy);
501 SSMR3PutU8 (pSSM, pVBoxSCSI->enmState);
502
503 if (pVBoxSCSI->cbBuf)
504 SSMR3PutMem(pSSM, pVBoxSCSI->pbBuf, pVBoxSCSI->cbBuf);
505
506 return VINF_SUCCESS;
507}
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