VirtualBox

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

Last change on this file since 28800 was 28800, checked in by vboxsync, 14 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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

© 2023 Oracle
ContactPrivacy policyTerms of Use