VirtualBox

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

Last change on this file since 41983 was 40676, checked in by vboxsync, 13 years ago

VBoxSCSI: Don't crash if a string I/O port access happens without a valid data buffer

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.1 KB
Line 
1/* $Id: VBoxSCSI.cpp 40676 2012-03-28 12:42:10Z 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/vmm/pdmdev.h>
31#include <VBox/vmm/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 necessary 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 if (pVBoxSCSI->pBuf)
274 RTMemFree(pVBoxSCSI->pBuf);
275
276 pVBoxSCSI->pBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
277 if (!pVBoxSCSI->pBuf)
278 return VERR_NO_MEMORY;
279 }
280
281 /* Allocate scatter gather element. */
282 pScsiRequest->paScatterGatherHead = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * 1); /* Only one element. */
283 if (!pScsiRequest->paScatterGatherHead)
284 {
285 RTMemFree(pVBoxSCSI->pBuf);
286 pVBoxSCSI->pBuf = NULL;
287 return VERR_NO_MEMORY;
288 }
289
290 /* Allocate sense buffer. */
291 pScsiRequest->cbSenseBuffer = 18;
292 pScsiRequest->pbSenseBuffer = (uint8_t *)RTMemAllocZ(pScsiRequest->cbSenseBuffer);
293
294 pScsiRequest->cbCDB = pVBoxSCSI->cbCDB;
295 pScsiRequest->pbCDB = pVBoxSCSI->aCDB;
296 pScsiRequest->uLogicalUnit = 0;
297 pScsiRequest->cbScatterGather = pVBoxSCSI->cbBuf;
298 pScsiRequest->cScatterGatherEntries = 1;
299
300 pScsiRequest->paScatterGatherHead[0].cbSeg = pVBoxSCSI->cbBuf;
301 pScsiRequest->paScatterGatherHead[0].pvSeg = pVBoxSCSI->pBuf;
302
303 *puTargetDevice = pVBoxSCSI->uTargetDevice;
304
305 return rc;
306}
307
308/**
309 * Notifies the device that a request finished and the incoming data
310 * is ready at the incoming data port.
311 */
312int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
313{
314 LogFlowFunc(("pVBoxSCSI=%#p pScsiRequest=%#p\n", pVBoxSCSI, pScsiRequest));
315 RTMemFree(pScsiRequest->paScatterGatherHead);
316 RTMemFree(pScsiRequest->pbSenseBuffer);
317
318 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
319 {
320 if (pVBoxSCSI->pBuf)
321 RTMemFree(pVBoxSCSI->pBuf);
322 pVBoxSCSI->pBuf = NULL;
323 pVBoxSCSI->cbBuf = 0;
324 pVBoxSCSI->cbCDB = 0;
325 pVBoxSCSI->iCDB = 0;
326 pVBoxSCSI->iBuf = 0;
327 pVBoxSCSI->uTargetDevice = 0;
328 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
329 memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
330 }
331
332 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, false);
333
334 return VINF_SUCCESS;
335}
336
337int vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
338 RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
339{
340 RTGCPTR GCDst = *pGCPtrDst;
341 uint32_t cbTransfer = *pcTransfer * cb;
342
343 LogFlowFunc(("pDevIns=%#p pVBoxSCSI=%#p iRegister=%d cTransfer=%u cb=%u\n",
344 pDevIns, pVBoxSCSI, iRegister, *pcTransfer, cb));
345
346 /* Read string only valid for data in register. */
347 AssertMsg(iRegister == 1, ("Hey only register 1 can be read from with string\n"));
348
349 /* Accesses without a valid buffer will be ignored. */
350 if (!pVBoxSCSI->pBuf)
351 return VINF_SUCCESS;
352
353 int rc = PGMPhysSimpleDirtyWriteGCPtr(PDMDevHlpGetVMCPU(pDevIns), GCDst, pVBoxSCSI->pBuf, cbTransfer);
354 AssertRC(rc);
355
356 *pGCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCDst + cbTransfer);
357 *pcTransfer = 0;
358
359 RTMemFree(pVBoxSCSI->pBuf);
360 pVBoxSCSI->pBuf = NULL;
361 pVBoxSCSI->cbBuf = 0;
362 pVBoxSCSI->cbCDB = 0;
363 pVBoxSCSI->iCDB = 0;
364 pVBoxSCSI->iBuf = 0;
365 pVBoxSCSI->uTargetDevice = 0;
366 pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
367 memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
368
369 return rc;
370}
371
372int vboxscsiWriteString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
373 RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
374{
375 RTGCPTR GCSrc = *pGCPtrSrc;
376 uint32_t cbTransfer = *pcTransfer * cb;
377
378 /* Write string only valid for data in/out register. */
379 AssertMsg(iRegister == 1, ("Hey only register 1 can be written to with string\n"));
380
381 /* Accesses without a valid buffer will be ignored. */
382 if (!pVBoxSCSI->pBuf)
383 return VINF_SUCCESS;
384
385 Assert(cbTransfer == pVBoxSCSI->cbBuf);
386 if (cbTransfer > pVBoxSCSI->cbBuf)
387 cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
388
389 int rc = PDMDevHlpPhysReadGCVirt(pDevIns, pVBoxSCSI->pBuf, GCSrc, cbTransfer);
390 AssertRC(rc);
391
392 *pGCPtrSrc = (RTGCPTR)((RTGCUINTPTR)GCSrc + cbTransfer);
393 *pcTransfer = 0;
394
395 ASMAtomicXchgBool(&pVBoxSCSI->fBusy, true);
396 return VERR_MORE_DATA;
397}
398
399void vboxscsiSetRequestRedo(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
400{
401 AssertMsg(pVBoxSCSI->fBusy, ("No request to redo\n"));
402
403 RTMemFree(pScsiRequest->paScatterGatherHead);
404 RTMemFree(pScsiRequest->pbSenseBuffer);
405
406 if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
407 {
408 AssertPtr(pVBoxSCSI->pBuf);
409 }
410}
411
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