VirtualBox

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

Last change on this file since 86075 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 KB
Line 
1/* $Id: VBoxSCSI.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * VBox storage devices - Simple SCSI interface for BIOS access.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22//#define DEBUG
23#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC /** @todo Create extra group. */
24
25#if defined(IN_R0) || defined(IN_RC)
26# error This device has no R0 or RC components
27#endif
28
29#include <VBox/vmm/pdmdev.h>
30#include <VBox/vmm/pgm.h>
31#include <VBox/version.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
39
40/**
41 * Resets the state.
42 */
43static void vboxscsiReset(PVBOXSCSI pVBoxSCSI, bool fEverything)
44{
45 if (fEverything)
46 {
47 pVBoxSCSI->regIdentify = 0;
48 pVBoxSCSI->fBusy = false;
49 }
50 pVBoxSCSI->cbCDB = 0;
51 RT_ZERO(pVBoxSCSI->abCDB);
52 pVBoxSCSI->iCDB = 0;
53 pVBoxSCSI->rcCompletion = 0;
54 pVBoxSCSI->uTargetDevice = 0;
55 pVBoxSCSI->cbBuf = 0;
56 pVBoxSCSI->cbBufLeft = 0;
57 pVBoxSCSI->iBuf = 0;
58 if (pVBoxSCSI->pbBuf)
59 RTMemFree(pVBoxSCSI->pbBuf);
60 pVBoxSCSI->pbBuf = NULL;
61 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
62}
63
64/**
65 * Initializes the state for the SCSI interface.
66 *
67 * @returns VBox status code.
68 * @param pVBoxSCSI Pointer to the unitialized SCSI state.
69 */
70int vboxscsiInitialize(PVBOXSCSI pVBoxSCSI)
71{
72 int rc = RTCritSectInit(&pVBoxSCSI->CritSect);
73 if (RT_SUCCESS(rc))
74 {
75 pVBoxSCSI->pbBuf = NULL;
76 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
77 }
78
79 return rc;
80}
81
82/**
83 * Frees all allocated resources.
84 *
85 * @returns nothing.
86 * @param pVBoxSCSI Pointer to the SCSI state,
87 */
88void vboxscsiDestroy(PVBOXSCSI pVBoxSCSI)
89{
90 if (RTCritSectIsInitialized(&pVBoxSCSI->CritSect))
91 RTCritSectDelete(&pVBoxSCSI->CritSect);
92}
93
94/**
95 * Performs a hardware reset.
96 *
97 * @returns nothing.
98 * @param pVBoxSCSI Pointer to the SCSI state,
99 */
100void vboxscsiHwReset(PVBOXSCSI pVBoxSCSI)
101{
102 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
103}
104
105/**
106 * Reads a register value.
107 *
108 * @retval VINF_SUCCESS
109 * @param pVBoxSCSI Pointer to the SCSI state.
110 * @param iRegister Index of the register to read.
111 * @param pu32Value Where to store the content of the register.
112 */
113int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32Value)
114{
115 uint8_t uVal = 0;
116
117 RTCritSectEnter(&pVBoxSCSI->CritSect);
118 switch (iRegister)
119 {
120 case 0:
121 {
122 if (ASMAtomicReadBool(&pVBoxSCSI->fBusy) == true)
123 {
124 uVal |= VBOX_SCSI_BUSY;
125 /* There is an I/O operation in progress.
126 * Yield the execution thread to let the I/O thread make progress.
127 */
128 RTThreadYield();
129 }
130 if (pVBoxSCSI->rcCompletion)
131 uVal |= VBOX_SCSI_ERROR;
132 break;
133 }
134 case 1:
135 {
136 /* If we're not in the 'command ready' state, there may not even be a buffer yet. */
137 if ( pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY
138 && pVBoxSCSI->cbBufLeft > 0)
139 {
140 AssertMsg(pVBoxSCSI->pbBuf, ("pBuf is NULL\n"));
141 Assert(!pVBoxSCSI->fBusy);
142 uVal = pVBoxSCSI->pbBuf[pVBoxSCSI->iBuf];
143 pVBoxSCSI->iBuf++;
144 pVBoxSCSI->cbBufLeft--;
145
146 /* When the guest reads the last byte from the data in buffer, clear
147 everything and reset command buffer. */
148 if (pVBoxSCSI->cbBufLeft == 0)
149 vboxscsiReset(pVBoxSCSI, false /*fEverything*/);
150 }
151 break;
152 }
153 case 2:
154 {
155 uVal = pVBoxSCSI->regIdentify;
156 break;
157 }
158 case 3:
159 {
160 uVal = pVBoxSCSI->rcCompletion;
161 break;
162 }
163 default:
164 AssertMsgFailed(("Invalid register to read from %u\n", iRegister));
165 }
166
167 *pu32Value = uVal;
168 RTCritSectLeave(&pVBoxSCSI->CritSect);
169
170 return VINF_SUCCESS;
171}
172
173/**
174 * Writes to a register.
175 *
176 * @retval VINF_SUCCESS on success.
177 * @retval VERR_MORE_DATA if a command is ready to be sent to the SCSI driver.
178 *
179 * @param pVBoxSCSI Pointer to the SCSI state.
180 * @param iRegister Index of the register to write to.
181 * @param uVal Value to write.
182 */
183int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
184{
185 int rc = VINF_SUCCESS;
186
187 RTCritSectEnter(&pVBoxSCSI->CritSect);
188 switch (iRegister)
189 {
190 case 0:
191 {
192 if (pVBoxSCSI->enmState == VBOXSCSISTATE_NO_COMMAND)
193 {
194 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_TXDIR;
195 pVBoxSCSI->uTargetDevice = uVal;
196 }
197 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_TXDIR)
198 {
199 if (uVal != VBOXSCSI_TXDIR_FROM_DEVICE && uVal != VBOXSCSI_TXDIR_TO_DEVICE)
200 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
201 else
202 {
203 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_CDB_SIZE_BUFHI;
204 pVBoxSCSI->uTxDir = uVal;
205 }
206 }
207 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_CDB_SIZE_BUFHI)
208 {
209 uint8_t cbCDB = uVal & 0x0F;
210
211 if (cbCDB == 0)
212 cbCDB = 16;
213 if (cbCDB > VBOXSCSI_CDB_SIZE_MAX)
214 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
215 else
216 {
217 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_LSB;
218 pVBoxSCSI->cbCDB = cbCDB;
219 pVBoxSCSI->cbBuf = (uVal & 0xF0) << 12; /* Bits 16-19 of buffer size. */
220 }
221 }
222 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_LSB)
223 {
224 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_MID;
225 pVBoxSCSI->cbBuf |= uVal; /* Bits 0-7 of buffer size. */
226 }
227 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_MID)
228 {
229 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_COMMAND;
230 pVBoxSCSI->cbBuf |= (((uint16_t)uVal) << 8); /* Bits 8-15 of buffer size. */
231 }
232 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_COMMAND)
233 {
234 pVBoxSCSI->abCDB[pVBoxSCSI->iCDB] = uVal;
235 pVBoxSCSI->iCDB++;
236
237 /* Check if we have all necessary command data. */
238 if (pVBoxSCSI->iCDB == pVBoxSCSI->cbCDB)
239 {
240 Log(("%s: Command ready for processing\n", __FUNCTION__));
241 pVBoxSCSI->enmState = VBOXSCSISTATE_COMMAND_READY;
242 pVBoxSCSI->cbBufLeft = pVBoxSCSI->cbBuf;
243 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
244 {
245 /* This is a write allocate buffer. */
246 pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
247 if (!pVBoxSCSI->pbBuf)
248 return VERR_NO_MEMORY;
249 }
250 else
251 {
252 /* This is a read from the device. */
253 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
254 rc = VERR_MORE_DATA; /** @todo Better return value to indicate ready command? */
255 }
256 }
257 }
258 else
259 AssertMsgFailed(("Invalid state %d\n", pVBoxSCSI->enmState));
260 break;
261 }
262
263 case 1:
264 {
265 if ( pVBoxSCSI->enmState != VBOXSCSISTATE_COMMAND_READY
266 || pVBoxSCSI->uTxDir != VBOXSCSI_TXDIR_TO_DEVICE)
267 {
268 /* Reset the state */
269 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
270 }
271 else if (pVBoxSCSI->cbBufLeft > 0)
272 {
273 pVBoxSCSI->pbBuf[pVBoxSCSI->iBuf++] = uVal;
274 pVBoxSCSI->cbBufLeft--;
275 if (pVBoxSCSI->cbBufLeft == 0)
276 {
277 rc = VERR_MORE_DATA;
278 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
279 }
280 }
281 /* else: Ignore extra data, request pending or something. */
282 break;
283 }
284
285 case 2:
286 {
287 pVBoxSCSI->regIdentify = uVal;
288 break;
289 }
290
291 case 3:
292 {
293 /* Reset */
294 vboxscsiReset(pVBoxSCSI, true /*fEverything*/);
295 break;
296 }
297
298 default:
299 AssertMsgFailed(("Invalid register to write to %u\n", iRegister));
300 }
301
302 RTCritSectLeave(&pVBoxSCSI->CritSect);
303 return rc;
304}
305
306/**
307 * Sets up a SCSI request which the owning SCSI device can process.
308 *
309 * @returns VBox status code.
310 * @param pVBoxSCSI Pointer to the SCSI state.
311 * @param puLun Where to store the LUN on success.
312 * @param ppbCdb Where to store the pointer to the CDB on success.
313 * @param pcbCdb Where to store the size of the CDB on success.
314 * @param pcbBuf Where to store th size of the data buffer on success.
315 * @param puTargetDevice Where to store the target device ID.
316 */
317int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, uint32_t *puLun, uint8_t **ppbCdb,
318 size_t *pcbCdb, size_t *pcbBuf, uint32_t *puTargetDevice)
319{
320 int rc = VINF_SUCCESS;
321
322 LogFlowFunc(("pVBoxSCSI=%#p puTargetDevice=%#p\n", pVBoxSCSI, puTargetDevice));
323
324 RTCritSectEnter(&pVBoxSCSI->CritSect);
325 AssertMsg(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, ("Invalid state %u\n", pVBoxSCSI->enmState));
326
327 /* Clear any errors from a previous request. */
328 pVBoxSCSI->rcCompletion = 0;
329
330 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
331 {
332 if (pVBoxSCSI->pbBuf)
333 RTMemFree(pVBoxSCSI->pbBuf);
334
335 pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
336 if (!pVBoxSCSI->pbBuf)
337 return VERR_NO_MEMORY;
338 }
339
340 *puLun = 0;
341 *ppbCdb = &pVBoxSCSI->abCDB[0];
342 *pcbCdb = pVBoxSCSI->cbCDB;
343 *pcbBuf = pVBoxSCSI->cbBuf;
344 *puTargetDevice = pVBoxSCSI->uTargetDevice;
345 RTCritSectLeave(&pVBoxSCSI->CritSect);
346
347 return rc;
348}
349
350/**
351 * Notifies the device that a request finished and the incoming data
352 * is ready at the incoming data port.
353 */
354int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, int rcCompletion)
355{
356 LogFlowFunc(("pVBoxSCSI=%#p\n", pVBoxSCSI));
357
358 RTCritSectEnter(&pVBoxSCSI->CritSect);
359 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
360 vboxscsiReset(pVBoxSCSI, false /*fEverything*/);
361
362 pVBoxSCSI->rcCompletion = rcCompletion;
363
364 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, false);
365 RTCritSectLeave(&pVBoxSCSI->CritSect);
366
367 return VINF_SUCCESS;
368}
369
370size_t vboxscsiCopyToBuf(PVBOXSCSI pVBoxSCSI, PRTSGBUF pSgBuf, size_t cbSkip, size_t cbCopy)
371{
372 AssertPtrReturn(pVBoxSCSI->pbBuf, 0);
373 AssertReturn(cbSkip + cbCopy <= pVBoxSCSI->cbBuf, 0);
374
375 RTCritSectEnter(&pVBoxSCSI->CritSect);
376 void *pvBuf = pVBoxSCSI->pbBuf + cbSkip;
377 size_t cbCopied = RTSgBufCopyToBuf(pSgBuf, pvBuf, cbCopy);
378 RTCritSectLeave(&pVBoxSCSI->CritSect);
379
380 return cbCopied;
381}
382
383size_t vboxscsiCopyFromBuf(PVBOXSCSI pVBoxSCSI, PRTSGBUF pSgBuf, size_t cbSkip, size_t cbCopy)
384{
385 AssertPtrReturn(pVBoxSCSI->pbBuf, 0);
386 AssertReturn(cbSkip + cbCopy <= pVBoxSCSI->cbBuf, 0);
387
388 RTCritSectEnter(&pVBoxSCSI->CritSect);
389 void *pvBuf = pVBoxSCSI->pbBuf + cbSkip;
390 size_t cbCopied = RTSgBufCopyFromBuf(pSgBuf, pvBuf, cbCopy);
391 RTCritSectLeave(&pVBoxSCSI->CritSect);
392
393 return cbCopied;
394}
395
396/**
397 * @retval VINF_SUCCESS
398 */
399int vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
400 uint8_t *pbDst, uint32_t *pcTransfers, unsigned cb)
401{
402 RT_NOREF(pDevIns);
403 LogFlowFunc(("pDevIns=%#p pVBoxSCSI=%#p iRegister=%d cTransfers=%u cb=%u\n",
404 pDevIns, pVBoxSCSI, iRegister, *pcTransfers, cb));
405
406 /*
407 * Check preconditions, fall back to non-string I/O handler.
408 */
409 Assert(*pcTransfers > 0);
410
411 /* Read string only valid for data in register. */
412 AssertMsgReturn(iRegister == 1, ("Hey! Only register 1 can be read from with string!\n"), VINF_SUCCESS);
413
414 /* Accesses without a valid buffer will be ignored. */
415 AssertReturn(pVBoxSCSI->pbBuf, VINF_SUCCESS);
416
417 /* Check state. */
418 AssertReturn(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, VINF_SUCCESS);
419 Assert(!pVBoxSCSI->fBusy);
420
421 RTCritSectEnter(&pVBoxSCSI->CritSect);
422 /*
423 * Also ignore attempts to read more data than is available.
424 */
425 uint32_t cbTransfer = *pcTransfers * cb;
426 if (pVBoxSCSI->cbBufLeft > 0)
427 {
428 Assert(cbTransfer <= pVBoxSCSI->cbBuf);
429 if (cbTransfer > pVBoxSCSI->cbBuf)
430 {
431 memset(pbDst + pVBoxSCSI->cbBuf, 0xff, cbTransfer - pVBoxSCSI->cbBuf);
432 cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
433 }
434
435 /* Copy the data and adance the buffer position. */
436 memcpy(pbDst, pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, cbTransfer);
437
438 /* Advance current buffer position. */
439 pVBoxSCSI->iBuf += cbTransfer;
440 pVBoxSCSI->cbBufLeft -= cbTransfer;
441
442 /* When the guest reads the last byte from the data in buffer, clear
443 everything and reset command buffer. */
444 if (pVBoxSCSI->cbBufLeft == 0)
445 vboxscsiReset(pVBoxSCSI, false /*fEverything*/);
446 }
447 else
448 {
449 AssertFailed();
450 memset(pbDst, 0, cbTransfer);
451 }
452 *pcTransfers = 0;
453 RTCritSectLeave(&pVBoxSCSI->CritSect);
454
455 return VINF_SUCCESS;
456}
457
458/**
459 * @retval VINF_SUCCESS
460 * @retval VERR_MORE_DATA
461 */
462int vboxscsiWriteString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
463 uint8_t const *pbSrc, uint32_t *pcTransfers, unsigned cb)
464{
465 RT_NOREF(pDevIns);
466
467 /*
468 * Check preconditions, fall back to non-string I/O handler.
469 */
470 Assert(*pcTransfers > 0);
471 /* Write string only valid for data in/out register. */
472 AssertMsgReturn(iRegister == 1, ("Hey! Only register 1 can be written to with string!\n"), VINF_SUCCESS);
473
474 /* Accesses without a valid buffer will be ignored. */
475 AssertReturn(pVBoxSCSI->pbBuf, VINF_SUCCESS);
476
477 /* State machine assumptions. */
478 AssertReturn(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, VINF_SUCCESS);
479 AssertReturn(pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE, VINF_SUCCESS);
480
481 RTCritSectEnter(&pVBoxSCSI->CritSect);
482 /*
483 * Ignore excess data (not supposed to happen).
484 */
485 int rc = VINF_SUCCESS;
486 if (pVBoxSCSI->cbBufLeft > 0)
487 {
488 uint32_t cbTransfer = RT_MIN(*pcTransfers * cb, pVBoxSCSI->cbBufLeft);
489
490 /* Copy the data and adance the buffer position. */
491 memcpy(pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, pbSrc, cbTransfer);
492 pVBoxSCSI->iBuf += cbTransfer;
493 pVBoxSCSI->cbBufLeft -= cbTransfer;
494
495 /* If we've reached the end, tell the caller to submit the command. */
496 if (pVBoxSCSI->cbBufLeft == 0)
497 {
498 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
499 rc = VERR_MORE_DATA;
500 }
501 }
502 else
503 AssertFailed();
504 *pcTransfers = 0;
505 RTCritSectLeave(&pVBoxSCSI->CritSect);
506
507 return rc;
508}
509
510void vboxscsiSetRequestRedo(PVBOXSCSI pVBoxSCSI)
511{
512 AssertMsg(pVBoxSCSI->fBusy, ("No request to redo\n"));
513
514 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
515 {
516 AssertPtr(pVBoxSCSI->pbBuf);
517 }
518}
519
520DECLHIDDEN(int) vboxscsiR3LoadExec(PCPDMDEVHLPR3 pHlp, PVBOXSCSI pVBoxSCSI, PSSMHANDLE pSSM)
521{
522 SSMR3GetU8 (pSSM, &pVBoxSCSI->regIdentify);
523 SSMR3GetU8 (pSSM, &pVBoxSCSI->uTargetDevice);
524 SSMR3GetU8 (pSSM, &pVBoxSCSI->uTxDir);
525 SSMR3GetU8 (pSSM, &pVBoxSCSI->cbCDB);
526
527 /*
528 * The CDB buffer was increased with r104155 in trunk (backported to 5.0
529 * in r104311) without bumping the SSM state versions which leaves us
530 * with broken saved state restoring for older VirtualBox releases
531 * (up to 5.0.10).
532 */
533 if ( ( SSMR3HandleRevision(pSSM) < 104311
534 && SSMR3HandleVersion(pSSM) < VBOX_FULL_VERSION_MAKE(5, 0, 12))
535 || ( SSMR3HandleRevision(pSSM) < 104155
536 && SSMR3HandleVersion(pSSM) >= VBOX_FULL_VERSION_MAKE(5, 0, 51)))
537 {
538 memset(&pVBoxSCSI->abCDB[0], 0, sizeof(pVBoxSCSI->abCDB));
539 SSMR3GetMem (pSSM, &pVBoxSCSI->abCDB[0], 12);
540 }
541 else
542 SSMR3GetMem (pSSM, &pVBoxSCSI->abCDB[0], sizeof(pVBoxSCSI->abCDB));
543
544 SSMR3GetU8 (pSSM, &pVBoxSCSI->iCDB);
545 SSMR3GetU32 (pSSM, &pVBoxSCSI->cbBufLeft);
546 SSMR3GetU32 (pSSM, &pVBoxSCSI->iBuf);
547 SSMR3GetBoolV(pSSM, &pVBoxSCSI->fBusy);
548 PDMDEVHLP_SSM_GET_ENUM8_RET(pHlp, pSSM, pVBoxSCSI->enmState, VBOXSCSISTATE);
549
550 /*
551 * Old saved states only save the size of the buffer left to read/write.
552 * To avoid changing the saved state version we can just calculate the original
553 * buffer size from the offset and remaining size.
554 */
555 pVBoxSCSI->cbBuf = pVBoxSCSI->cbBufLeft + pVBoxSCSI->iBuf;
556
557 if (pVBoxSCSI->cbBuf)
558 {
559 pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
560 if (!pVBoxSCSI->pbBuf)
561 return VERR_NO_MEMORY;
562
563 SSMR3GetMem(pSSM, pVBoxSCSI->pbBuf, pVBoxSCSI->cbBuf);
564 }
565
566 return VINF_SUCCESS;
567}
568
569DECLHIDDEN(int) vboxscsiR3SaveExec(PCPDMDEVHLPR3 pHlp, PVBOXSCSI pVBoxSCSI, PSSMHANDLE pSSM)
570{
571 RT_NOREF(pHlp);
572 SSMR3PutU8 (pSSM, pVBoxSCSI->regIdentify);
573 SSMR3PutU8 (pSSM, pVBoxSCSI->uTargetDevice);
574 SSMR3PutU8 (pSSM, pVBoxSCSI->uTxDir);
575 SSMR3PutU8 (pSSM, pVBoxSCSI->cbCDB);
576 SSMR3PutMem (pSSM, pVBoxSCSI->abCDB, sizeof(pVBoxSCSI->abCDB));
577 SSMR3PutU8 (pSSM, pVBoxSCSI->iCDB);
578 SSMR3PutU32 (pSSM, pVBoxSCSI->cbBufLeft);
579 SSMR3PutU32 (pSSM, pVBoxSCSI->iBuf);
580 SSMR3PutBool (pSSM, pVBoxSCSI->fBusy);
581 SSMR3PutU8 (pSSM, pVBoxSCSI->enmState);
582
583 if (pVBoxSCSI->cbBuf)
584 SSMR3PutMem(pSSM, pVBoxSCSI->pbBuf, pVBoxSCSI->cbBuf);
585
586 return VINF_SUCCESS;
587}
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