VirtualBox

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

Last change on this file since 28065 was 28065, checked in by vboxsync, 15 years ago

Storage: Convert from PDMDATASEG to RTSGSEG to avoid casting between those two in VBoxHDD and more async I/O updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.7 KB
Line 
1/* $Id: VBoxSCSI.cpp 28065 2010-04-07 20:54:34Z vboxsync $ */
2/** @file
3 *
4 * VBox storage devices:
5 * Simple SCSI interface for BIOS access
6 */
7
8/*
9 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27//#define DEBUG
28#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC /* @todo: Create extra group. */
29
30#if defined(IN_R0) || defined(IN_RC)
31# error This device has no R0 or GC components
32#endif
33
34#include <VBox/pdmdev.h>
35#include <VBox/pgm.h>
36#include <iprt/asm.h>
37#include <iprt/mem.h>
38#include <iprt/thread.h>
39#include <iprt/string.h>
40
41#include "VBoxSCSI.h"
42
43/**
44 * Initializes the state for the SCSI interface.
45 *
46 * @returns VBox status code.
47 * @param pVBoxSCSI Pointer to the unitialized SCSI state.
48 */
49int vboxscsiInitialize(PVBOXSCSI pVBoxSCSI)
50{
51 pVBoxSCSI->regIdentify = 0;
52 pVBoxSCSI->cbCDB = 0;
53 memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
54 pVBoxSCSI->iCDB = 0;
55 pVBoxSCSI->fBusy = false;
56 pVBoxSCSI->cbBuf = 0;
57 pVBoxSCSI->iBuf = 0;
58 pVBoxSCSI->pBuf = NULL;
59 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
60
61 return VINF_SUCCESS;
62}
63
64/**
65 * Reads a register value.
66 *
67 * @returns VBox status code.
68 * @param pVBoxSCSI Pointer to the SCSI state.
69 * @param iRegister Index of the register to read.
70 * @param pu32Value Where to store the content of the register.
71 */
72int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32Value)
73{
74 uint8_t uVal = 0;
75
76 switch (iRegister)
77 {
78 case 0:
79 {
80 if (ASMAtomicReadBool(&pVBoxSCSI->fBusy) == true)
81 {
82 uVal |= VBOX_SCSI_BUSY;
83 /* There is an I/O operation in progress.
84 * Yield the execution thread to let the I/O thread make progress.
85 */
86 RTThreadYield();
87 }
88 else
89 uVal &= ~VBOX_SCSI_BUSY;
90 break;
91 }
92 case 1:
93 {
94 if (pVBoxSCSI->cbBuf > 0)
95 {
96 AssertMsg(pVBoxSCSI->pBuf, ("pBuf is NULL\n"));
97 uVal = pVBoxSCSI->pBuf[pVBoxSCSI->iBuf];
98 pVBoxSCSI->iBuf++;
99 pVBoxSCSI->cbBuf--;
100 if (pVBoxSCSI->cbBuf == 0)
101 {
102 /** The guest read the last byte from the data in buffer.
103 * Clear everything and reset command buffer.
104 */
105 RTMemFree(pVBoxSCSI->pBuf);
106 pVBoxSCSI->pBuf = NULL;
107 pVBoxSCSI->cbCDB = 0;
108 pVBoxSCSI->iCDB = 0;
109 pVBoxSCSI->iBuf = 0;
110 pVBoxSCSI->uTargetDevice = 0;
111 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
112 memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
113 }
114 }
115 break;
116 }
117 case 2:
118 {
119 uVal = pVBoxSCSI->regIdentify;
120 break;
121 }
122 default:
123 AssertMsgFailed(("Invalid register to read from %u\n", iRegister));
124 }
125
126 *pu32Value = uVal;
127
128 return VINF_SUCCESS;
129}
130
131/**
132 * Writes to a register.
133 *
134 * @returns VBox status code.
135 * VERR_MORE_DATA if a command is ready to be sent to the SCSI driver.
136 * @param pVBoxSCSI Pointer to the SCSI state.
137 * @param iRegister Index of the register to write to.
138 * @param uVal Value to write.
139 */
140int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
141{
142 int rc = VINF_SUCCESS;
143
144 switch (iRegister)
145 {
146 case 0:
147 {
148 if (pVBoxSCSI->enmState == VBOXSCSISTATE_NO_COMMAND)
149 {
150 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_TXDIR;
151 pVBoxSCSI->uTargetDevice = uVal;
152 }
153 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_TXDIR)
154 {
155 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_CDB_SIZE;
156 pVBoxSCSI->uTxDir = uVal;
157 }
158 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_CDB_SIZE)
159 {
160 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_LOW;
161 pVBoxSCSI->cbCDB = uVal;
162 }
163 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_LOW)
164 {
165 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_HIGH;
166 pVBoxSCSI->cbBuf = uVal;
167 }
168 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_HIGH)
169 {
170 pVBoxSCSI->enmState = VBOXSCSISTATE_READ_COMMAND;
171 pVBoxSCSI->cbBuf |= (((uint16_t)uVal) << 8);
172 }
173 else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_COMMAND)
174 {
175 pVBoxSCSI->aCDB[pVBoxSCSI->iCDB] = uVal;
176 pVBoxSCSI->iCDB++;
177
178 /* Check if we have all neccessary command data. */
179 if (pVBoxSCSI->iCDB == pVBoxSCSI->cbCDB)
180 {
181 Log(("%s: Command ready for processing\n", __FUNCTION__));
182 pVBoxSCSI->enmState = VBOXSCSISTATE_COMMAND_READY;
183 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
184 {
185 /* This is a write allocate buffer. */
186 pVBoxSCSI->pBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
187 if (!pVBoxSCSI->pBuf)
188 return VERR_NO_MEMORY;
189 }
190 else
191 {
192 /* This is a read from the device. */
193 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
194 rc = VERR_MORE_DATA; /** @todo Better return value to indicate ready command? */
195 }
196 }
197 }
198 else
199 AssertMsgFailed(("Invalid state %d\n", pVBoxSCSI->enmState));
200 break;
201 }
202 case 1:
203 {
204 AssertMsg(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, ("Invalid state\n"));
205 pVBoxSCSI->pBuf[pVBoxSCSI->iBuf++] = uVal;
206 if (pVBoxSCSI->iBuf == pVBoxSCSI->cbBuf)
207 {
208 rc = VERR_MORE_DATA;
209 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
210 }
211 break;
212 }
213 case 2:
214 {
215 pVBoxSCSI->regIdentify = uVal;
216 break;
217 }
218 default:
219 AssertMsgFailed(("Invalid register to write to %u\n", iRegister));
220 }
221
222 return rc;
223}
224
225/**
226 * Sets up a SCSI request which the owning SCSI device can process.
227 *
228 * @returns VBox status code.
229 * @param pVBoxSCSI Pointer to the SCSI state.
230 * @param pScsiRequest Pointer to a scsi request to setup.
231 * @param puTargetDevice Where to store the target device ID.
232 */
233int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint32_t *puTargetDevice)
234{
235 int rc = VINF_SUCCESS;
236
237 LogFlowFunc(("pVBoxSCSI=%#p pScsiRequest=%#p puTargetDevice=%#p\n", pVBoxSCSI, pScsiRequest, puTargetDevice));
238
239 AssertMsg(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, ("Invalid state %u\n", pVBoxSCSI->enmState));
240
241 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
242 {
243 Assert(!pVBoxSCSI->pBuf);
244 pVBoxSCSI->pBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
245 if (!pVBoxSCSI->pBuf)
246 return VERR_NO_MEMORY;
247 }
248
249 /** Allocate scatter gather element. */
250 pScsiRequest->paScatterGatherHead = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * 1); /* Only one element. */
251 if (!pScsiRequest->paScatterGatherHead)
252 {
253 RTMemFree(pVBoxSCSI->pBuf);
254 pVBoxSCSI->pBuf = NULL;
255 return VERR_NO_MEMORY;
256 }
257
258 /** Allocate sense buffer. */
259 pScsiRequest->cbSenseBuffer = 18;
260 pScsiRequest->pbSenseBuffer = (uint8_t *)RTMemAllocZ(pScsiRequest->cbSenseBuffer);
261
262 pScsiRequest->cbCDB = pVBoxSCSI->cbCDB;
263 pScsiRequest->pbCDB = pVBoxSCSI->aCDB;
264 pScsiRequest->uLogicalUnit = 0;
265 pScsiRequest->cbScatterGather = pVBoxSCSI->cbBuf;
266 pScsiRequest->cScatterGatherEntries = 1;
267
268 pScsiRequest->paScatterGatherHead[0].cbSeg = pVBoxSCSI->cbBuf;
269 pScsiRequest->paScatterGatherHead[0].pvSeg = pVBoxSCSI->pBuf;
270
271 *puTargetDevice = pVBoxSCSI->uTargetDevice;
272
273 return rc;
274}
275
276/**
277 * Notifies the device that a request finished and the incoming data
278 * is ready at the incoming data port.
279 */
280int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
281{
282 LogFlowFunc(("pVBoxSCSI=%#p pScsiRequest=%#p\n", pVBoxSCSI, pScsiRequest));
283 RTMemFree(pScsiRequest->paScatterGatherHead);
284 RTMemFree(pScsiRequest->pbSenseBuffer);
285
286 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
287 {
288 if (pVBoxSCSI->pBuf)
289 RTMemFree(pVBoxSCSI->pBuf);
290 pVBoxSCSI->pBuf = NULL;
291 pVBoxSCSI->cbBuf = 0;
292 pVBoxSCSI->cbCDB = 0;
293 pVBoxSCSI->iCDB = 0;
294 pVBoxSCSI->iBuf = 0;
295 pVBoxSCSI->uTargetDevice = 0;
296 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
297 memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
298 }
299
300 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, false);
301
302 return VINF_SUCCESS;
303}
304
305int vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
306 RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
307{
308 RTGCPTR GCDst = *pGCPtrDst;
309 uint32_t cbTransfer = *pcTransfer * cb;
310
311 LogFlowFunc(("pDevIns=%#p pVBoxSCSI=%#p iRegister=%d cTransfer=%u cb=%u\n",
312 pDevIns, pVBoxSCSI, iRegister, *pcTransfer, cb));
313
314 /* Read string only valid for data in register. */
315 AssertMsg(iRegister == 1, ("Hey only register 1 can be read from with string\n"));
316 Assert(pVBoxSCSI->pBuf);
317
318 int rc = PGMPhysSimpleDirtyWriteGCPtr(PDMDevHlpGetVMCPU(pDevIns), GCDst, pVBoxSCSI->pBuf, cbTransfer);
319 AssertRC(rc);
320
321 *pGCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCDst + cbTransfer);
322 *pcTransfer = 0;
323
324 RTMemFree(pVBoxSCSI->pBuf);
325 pVBoxSCSI->pBuf = NULL;
326 pVBoxSCSI->cbBuf = 0;
327 pVBoxSCSI->cbCDB = 0;
328 pVBoxSCSI->iCDB = 0;
329 pVBoxSCSI->iBuf = 0;
330 pVBoxSCSI->uTargetDevice = 0;
331 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
332 memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
333
334 return rc;
335}
336
337int vboxscsiWriteString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
338 RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
339{
340 RTGCPTR GCSrc = *pGCPtrSrc;
341 uint32_t cbTransfer = *pcTransfer * cb;
342
343 /* Read string only valid for data in register. */
344 AssertMsg(iRegister == 1, ("Hey only register 1 can be read from with string\n"));
345 AssertMsg(cbTransfer == 512, ("Only 512 byte transfers are allowed\n"));
346
347
348 int rc = PDMDevHlpPhysReadGCVirt(pDevIns, pVBoxSCSI->pBuf, GCSrc, cbTransfer);
349 AssertRC(rc);
350
351 *pGCPtrSrc = (RTGCPTR)((RTGCUINTPTR)GCSrc + cbTransfer);
352 *pcTransfer = 0;
353
354 return VERR_MORE_DATA;
355}
356
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