summaryrefslogtreecommitdiff
path: root/src/VBox/Devices/Storage
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Storage')
-rw-r--r--src/VBox/Devices/Storage/ATAController.cpp5789
-rw-r--r--src/VBox/Devices/Storage/ATAController.h563
-rw-r--r--src/VBox/Devices/Storage/ATAPIPassthrough.cpp632
-rw-r--r--src/VBox/Devices/Storage/ATAPIPassthrough.h74
-rw-r--r--src/VBox/Devices/Storage/Debug.cpp2
-rw-r--r--src/VBox/Devices/Storage/DevAHCI.cpp1893
-rw-r--r--src/VBox/Devices/Storage/DevATA.cpp1473
-rw-r--r--src/VBox/Devices/Storage/DevBusLogic.cpp2105
-rw-r--r--src/VBox/Devices/Storage/DevFdc.cpp (renamed from src/VBox/Devices/Storage/fdc.c)1056
-rw-r--r--src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp3498
-rw-r--r--src/VBox/Devices/Storage/DevLsiLogicSCSI.h98
-rw-r--r--src/VBox/Devices/Storage/DrvBlock.cpp103
-rw-r--r--src/VBox/Devices/Storage/DrvDiskIntegrity.cpp66
-rw-r--r--src/VBox/Devices/Storage/DrvHostBase.cpp11
-rw-r--r--src/VBox/Devices/Storage/DrvHostBase.h2
-rw-r--r--src/VBox/Devices/Storage/DrvHostDVD.cpp85
-rw-r--r--src/VBox/Devices/Storage/DrvHostFloppy.cpp37
-rw-r--r--src/VBox/Devices/Storage/DrvMediaISO.cpp9
-rw-r--r--src/VBox/Devices/Storage/DrvRawImage.cpp9
-rw-r--r--src/VBox/Devices/Storage/DrvSCSI.cpp137
-rw-r--r--src/VBox/Devices/Storage/DrvSCSIHost.cpp2
-rw-r--r--src/VBox/Devices/Storage/DrvVD.cpp161
-rw-r--r--src/VBox/Devices/Storage/PIIX3ATABmDma.h2
-rw-r--r--src/VBox/Devices/Storage/UsbMsd.cpp216
-rw-r--r--src/VBox/Devices/Storage/VBoxSCSI.cpp177
-rw-r--r--src/VBox/Devices/Storage/VBoxSCSI.h71
-rw-r--r--src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp10
-rw-r--r--src/VBox/Devices/Storage/VSCSI/VSCSIInternal.h34
-rw-r--r--src/VBox/Devices/Storage/VSCSI/VSCSILun.cpp44
-rw-r--r--src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp186
-rw-r--r--src/VBox/Devices/Storage/VSCSI/VSCSILunSbc.cpp65
-rw-r--r--src/VBox/Devices/Storage/VSCSI/VSCSISense.cpp8
-rw-r--r--src/VBox/Devices/Storage/swab.h2
33 files changed, 7712 insertions, 10908 deletions
diff --git a/src/VBox/Devices/Storage/ATAController.cpp b/src/VBox/Devices/Storage/ATAController.cpp
deleted file mode 100644
index ca1ee31b..00000000
--- a/src/VBox/Devices/Storage/ATAController.cpp
+++ /dev/null
@@ -1,5789 +0,0 @@
-/* $Id: ATAController.cpp $ */
-/** @file
- * DevATA, DevAHCI - Shared ATA/ATAPI controller code (disk and cdrom).
- *
- * @todo Actually share this code?
- */
-
-/*
- * Copyright (C) 2006-2011 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-/*******************************************************************************
-* Header Files *
-*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_DEV_IDE
-#include <VBox/vmm/pdmdev.h>
-#include <iprt/assert.h>
-#include <iprt/string.h>
-#ifdef IN_RING3
-# include <iprt/uuid.h>
-# include <iprt/semaphore.h>
-# include <iprt/thread.h>
-# include <iprt/time.h>
-# include <iprt/alloc.h>
-#endif /* IN_RING3 */
-#include <iprt/critsect.h>
-#include <iprt/asm.h>
-#include <VBox/vmm/stam.h>
-#include <VBox/vmm/mm.h>
-#include <VBox/vmm/pgm.h>
-
-#include <VBox/scsi.h>
-
-#include "ATAController.h"
-#include "PIIX3ATABmDma.h"
-#include "ide.h"
-
-/**
- * The SSM saved state version.
- */
-#define ATA_CTL_SAVED_STATE_VERSION 3
-#define ATA_CTL_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE 1
-#define ATA_CTL_SAVED_STATE_VERSION_WITHOUT_EVENT_STATUS 2
-
-#ifndef VBOX_DEVICE_STRUCT_TESTCASE
-
-DECLINLINE(void) ataSetStatusValue(AHCIATADevState *s, uint8_t stat)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
-
- /* Freeze status register contents while processing RESET. */
- if (!pCtl->fReset)
- {
- s->uATARegStatus = stat;
- Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, s->iLUN, s->uATARegStatus));
- }
-}
-
-
-DECLINLINE(void) ataSetStatus(AHCIATADevState *s, uint8_t stat)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
-
- /* Freeze status register contents while processing RESET. */
- if (!pCtl->fReset)
- {
- s->uATARegStatus |= stat;
- Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, s->iLUN, s->uATARegStatus));
- }
-}
-
-
-DECLINLINE(void) ataUnsetStatus(AHCIATADevState *s, uint8_t stat)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
-
- /* Freeze status register contents while processing RESET. */
- if (!pCtl->fReset)
- {
- s->uATARegStatus &= ~stat;
- Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, s->iLUN, s->uATARegStatus));
- }
-}
-
-#ifdef IN_RING3
-
-typedef void (*PBeginTransferFunc)(AHCIATADevState *);
-typedef bool (*PSourceSinkFunc)(AHCIATADevState *);
-
-static void ataReadWriteSectorsBT(AHCIATADevState *);
-static void ataPacketBT(AHCIATADevState *);
-static void atapiCmdBT(AHCIATADevState *);
-static void atapiPassthroughCmdBT(AHCIATADevState *);
-
-static bool ataIdentifySS(AHCIATADevState *);
-static bool ataFlushSS(AHCIATADevState *);
-static bool ataReadSectorsSS(AHCIATADevState *);
-static bool ataWriteSectorsSS(AHCIATADevState *);
-static bool ataExecuteDeviceDiagnosticSS(AHCIATADevState *);
-static bool ataTrimSS(AHCIATADevState *);
-static bool ataPacketSS(AHCIATADevState *);
-static bool atapiGetConfigurationSS(AHCIATADevState *);
-static bool atapiGetEventStatusNotificationSS(AHCIATADevState *);
-static bool atapiIdentifySS(AHCIATADevState *);
-static bool atapiInquirySS(AHCIATADevState *);
-static bool atapiMechanismStatusSS(AHCIATADevState *);
-static bool atapiModeSenseErrorRecoverySS(AHCIATADevState *);
-static bool atapiModeSenseCDStatusSS(AHCIATADevState *);
-static bool atapiReadSS(AHCIATADevState *);
-static bool atapiReadCapacitySS(AHCIATADevState *);
-static bool atapiReadDiscInformationSS(AHCIATADevState *);
-static bool atapiReadTOCNormalSS(AHCIATADevState *);
-static bool atapiReadTOCMultiSS(AHCIATADevState *);
-static bool atapiReadTOCRawSS(AHCIATADevState *);
-static bool atapiReadTrackInformationSS(AHCIATADevState *);
-static bool atapiRequestSenseSS(AHCIATADevState *);
-static bool atapiPassthroughSS(AHCIATADevState *);
-
-/**
- * Begin of transfer function indexes for g_apfnBeginTransFuncs.
- */
-typedef enum ATAFNBT
-{
- ATAFN_BT_NULL = 0,
- ATAFN_BT_READ_WRITE_SECTORS,
- ATAFN_BT_PACKET,
- ATAFN_BT_ATAPI_CMD,
- ATAFN_BT_ATAPI_PASSTHROUGH_CMD,
- ATAFN_BT_MAX
-} ATAFNBT;
-
-/**
- * Array of end transfer functions, the index is ATAFNET.
- * Make sure ATAFNET and this array match!
- */
-static const PBeginTransferFunc g_apfnBeginTransFuncs[ATAFN_BT_MAX] =
-{
- NULL,
- ataReadWriteSectorsBT,
- ataPacketBT,
- atapiCmdBT,
- atapiPassthroughCmdBT,
-};
-
-/**
- * Source/sink function indexes for g_apfnSourceSinkFuncs.
- */
-typedef enum ATAFNSS
-{
- ATAFN_SS_NULL = 0,
- ATAFN_SS_IDENTIFY,
- ATAFN_SS_FLUSH,
- ATAFN_SS_READ_SECTORS,
- ATAFN_SS_WRITE_SECTORS,
- ATAFN_SS_EXECUTE_DEVICE_DIAGNOSTIC,
- ATAFN_SS_TRIM,
- ATAFN_SS_PACKET,
- ATAFN_SS_ATAPI_GET_CONFIGURATION,
- ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION,
- ATAFN_SS_ATAPI_IDENTIFY,
- ATAFN_SS_ATAPI_INQUIRY,
- ATAFN_SS_ATAPI_MECHANISM_STATUS,
- ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY,
- ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS,
- ATAFN_SS_ATAPI_READ,
- ATAFN_SS_ATAPI_READ_CAPACITY,
- ATAFN_SS_ATAPI_READ_DISC_INFORMATION,
- ATAFN_SS_ATAPI_READ_TOC_NORMAL,
- ATAFN_SS_ATAPI_READ_TOC_MULTI,
- ATAFN_SS_ATAPI_READ_TOC_RAW,
- ATAFN_SS_ATAPI_READ_TRACK_INFORMATION,
- ATAFN_SS_ATAPI_REQUEST_SENSE,
- ATAFN_SS_ATAPI_PASSTHROUGH,
- ATAFN_SS_MAX
-} ATAFNSS;
-
-/**
- * Array of source/sink functions, the index is ATAFNSS.
- * Make sure ATAFNSS and this array match!
- */
-static const PSourceSinkFunc g_apfnSourceSinkFuncs[ATAFN_SS_MAX] =
-{
- NULL,
- ataIdentifySS,
- ataFlushSS,
- ataReadSectorsSS,
- ataWriteSectorsSS,
- ataExecuteDeviceDiagnosticSS,
- ataTrimSS,
- ataPacketSS,
- atapiGetConfigurationSS,
- atapiGetEventStatusNotificationSS,
- atapiIdentifySS,
- atapiInquirySS,
- atapiMechanismStatusSS,
- atapiModeSenseErrorRecoverySS,
- atapiModeSenseCDStatusSS,
- atapiReadSS,
- atapiReadCapacitySS,
- atapiReadDiscInformationSS,
- atapiReadTOCNormalSS,
- atapiReadTOCMultiSS,
- atapiReadTOCRawSS,
- atapiReadTrackInformationSS,
- atapiRequestSenseSS,
- atapiPassthroughSS
-};
-
-
-static const AHCIATARequest ataDMARequest = { AHCIATA_AIO_DMA, };
-static const AHCIATARequest ataPIORequest = { AHCIATA_AIO_PIO, };
-static const AHCIATARequest ataResetARequest = { AHCIATA_AIO_RESET_ASSERTED, };
-static const AHCIATARequest ataResetCRequest = { AHCIATA_AIO_RESET_CLEARED, };
-
-
-static void ataAsyncIOClearRequests(PAHCIATACONTROLLER pCtl)
-{
- int rc;
-
- rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
- AssertRC(rc);
- pCtl->AsyncIOReqHead = 0;
- pCtl->AsyncIOReqTail = 0;
- rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
- AssertRC(rc);
-}
-
-
-static void ataAsyncIOPutRequest(PAHCIATACONTROLLER pCtl, const AHCIATARequest *pReq)
-{
- int rc;
-
- rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
- AssertRC(rc);
- Assert((pCtl->AsyncIOReqHead + 1) % RT_ELEMENTS(pCtl->aAsyncIORequests) != pCtl->AsyncIOReqTail);
- memcpy(&pCtl->aAsyncIORequests[pCtl->AsyncIOReqHead], pReq, sizeof(*pReq));
- pCtl->AsyncIOReqHead++;
- pCtl->AsyncIOReqHead %= RT_ELEMENTS(pCtl->aAsyncIORequests);
- rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
- AssertRC(rc);
- rc = PDMR3CritSectScheduleExitEvent(&pCtl->lock, pCtl->AsyncIOSem);
- if (RT_FAILURE(rc))
- {
- rc = RTSemEventSignal(pCtl->AsyncIOSem);
- AssertRC(rc);
- }
-}
-
-
-static const AHCIATARequest *ataAsyncIOGetCurrentRequest(PAHCIATACONTROLLER pCtl)
-{
- int rc;
- const AHCIATARequest *pReq;
-
- rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
- AssertRC(rc);
- if (pCtl->AsyncIOReqHead != pCtl->AsyncIOReqTail)
- pReq = &pCtl->aAsyncIORequests[pCtl->AsyncIOReqTail];
- else
- pReq = NULL;
- rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
- AssertRC(rc);
- return pReq;
-}
-
-
-/**
- * Remove the request with the given type, as it's finished. The request
- * is not removed blindly, as this could mean a RESET request that is not
- * yet processed (but has cleared the request queue) is lost.
- *
- * @param pCtl Controller for which to remove the request.
- * @param ReqType Type of the request to remove.
- */
-static void ataAsyncIORemoveCurrentRequest(PAHCIATACONTROLLER pCtl, AHCIATAAIO ReqType)
-{
- int rc;
-
- rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
- AssertRC(rc);
- if (pCtl->AsyncIOReqHead != pCtl->AsyncIOReqTail && pCtl->aAsyncIORequests[pCtl->AsyncIOReqTail].ReqType == ReqType)
- {
- pCtl->AsyncIOReqTail++;
- pCtl->AsyncIOReqTail %= RT_ELEMENTS(pCtl->aAsyncIORequests);
- }
- rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
- AssertRC(rc);
-}
-
-
-/**
- * Dump the request queue for a particular controller. First dump the queue
- * contents, then the already processed entries, as long as they haven't been
- * overwritten.
- *
- * @param pCtl Controller for which to dump the queue.
- */
-static void ataAsyncIODumpRequests(PAHCIATACONTROLLER pCtl)
-{
- int rc;
- uint8_t curr;
-
- rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
- AssertRC(rc);
- LogRel(("AHCI ATA: Ctl: request queue dump (topmost is current):\n"));
- curr = pCtl->AsyncIOReqTail;
- do
- {
- if (curr == pCtl->AsyncIOReqHead)
- LogRel(("AHCI ATA: Ctl: processed requests (topmost is oldest):\n"));
- switch (pCtl->aAsyncIORequests[curr].ReqType)
- {
- case AHCIATA_AIO_NEW:
- LogRel(("new transfer request, iIf=%d iBeginTransfer=%d iSourceSink=%d cbTotalTransfer=%d uTxDir=%d\n", pCtl->aAsyncIORequests[curr].u.t.iIf, pCtl->aAsyncIORequests[curr].u.t.iBeginTransfer, pCtl->aAsyncIORequests[curr].u.t.iSourceSink, pCtl->aAsyncIORequests[curr].u.t.cbTotalTransfer, pCtl->aAsyncIORequests[curr].u.t.uTxDir));
- break;
- case AHCIATA_AIO_DMA:
- LogRel(("dma transfer finished\n"));
- break;
- case AHCIATA_AIO_PIO:
- LogRel(("pio transfer finished\n"));
- break;
- case AHCIATA_AIO_RESET_ASSERTED:
- LogRel(("reset asserted request\n"));
- break;
- case AHCIATA_AIO_RESET_CLEARED:
- LogRel(("reset cleared request\n"));
- break;
- case AHCIATA_AIO_ABORT:
- LogRel(("abort request, iIf=%d fResetDrive=%d\n", pCtl->aAsyncIORequests[curr].u.a.iIf, pCtl->aAsyncIORequests[curr].u.a.fResetDrive));
- break;
- default:
- LogRel(("unknown request %d\n", pCtl->aAsyncIORequests[curr].ReqType));
- }
- curr = (curr + 1) % RT_ELEMENTS(pCtl->aAsyncIORequests);
- } while (curr != pCtl->AsyncIOReqTail);
- rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
- AssertRC(rc);
-}
-
-
-/**
- * Checks whether the request queue for a particular controller is empty
- * or whether a particular controller is idle.
- *
- * @param pCtl Controller for which to check the queue.
- * @param fStrict If set then the controller is checked to be idle.
- */
-static bool ataAsyncIOIsIdle(PAHCIATACONTROLLER pCtl, bool fStrict)
-{
- int rc;
- bool fIdle;
-
- if (pCtl->AsyncIORequestMutex == NIL_RTSEMMUTEX)
- return true;
-
- rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
- AssertRC(rc);
- fIdle = pCtl->fRedoIdle;
- if (!fIdle)
- fIdle = (pCtl->AsyncIOReqHead == pCtl->AsyncIOReqTail);
- if (fStrict)
- fIdle &= (pCtl->uAsyncIOState == AHCIATA_AIO_NEW);
- rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
- AssertRC(rc);
- return fIdle;
-}
-
-
-/**
- * Send a transfer request to the async I/O thread.
- *
- * @param s Pointer to the ATA device state data.
- * @param cbTotalTransfer Data transfer size.
- * @param uTxDir Data transfer direction.
- * @param iBeginTransfer Index of BeginTransfer callback.
- * @param iSourceSink Index of SourceSink callback.
- * @param fChainedTransfer Whether this is a transfer that is part of the previous command/transfer.
- */
-static void ataStartTransfer(AHCIATADevState *s, uint32_t cbTotalTransfer, uint8_t uTxDir, ATAFNBT iBeginTransfer, ATAFNSS iSourceSink, bool fChainedTransfer)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- AHCIATARequest Req;
-
- Assert(PDMCritSectIsOwner(&pCtl->lock));
-
- /* Do not issue new requests while the RESET line is asserted. */
- if (pCtl->fReset)
- {
- Log2(("%s: Ctl: suppressed new request as RESET is active\n", __FUNCTION__));
- return;
- }
-
- /* If the controller is already doing something else right now, ignore
- * the command that is being submitted. Some broken guests issue commands
- * twice (e.g. the Linux kernel that comes with Acronis True Image 8). */
- if (!fChainedTransfer && !ataAsyncIOIsIdle(pCtl, true))
- {
- Log(("%s: Ctl: ignored command %#04x, controller state %d\n", __FUNCTION__, s->uATARegCommand, pCtl->uAsyncIOState));
- LogRel(("IDE: guest issued command %#04x while controller busy\n", s->uATARegCommand));
- return;
- }
-
- Req.ReqType = AHCIATA_AIO_NEW;
- if (fChainedTransfer)
- Req.u.t.iIf = pCtl->iAIOIf;
- else
- Req.u.t.iIf = pCtl->iSelectedIf;
- Req.u.t.cbTotalTransfer = cbTotalTransfer;
- Req.u.t.uTxDir = uTxDir;
- Req.u.t.iBeginTransfer = iBeginTransfer;
- Req.u.t.iSourceSink = iSourceSink;
- ataSetStatusValue(s, ATA_STAT_BUSY);
- pCtl->fChainedTransfer = fChainedTransfer;
-
- /*
- * Kick the worker thread into action.
- */
- Log2(("%s: Ctl: message to async I/O thread, new request\n", __FUNCTION__));
- ataAsyncIOPutRequest(pCtl, &Req);
-}
-
-
-/**
- * Send an abort command request to the async I/O thread.
- *
- * @param s Pointer to the ATA device state data.
- * @param fResetDrive Whether to reset the drive or just abort a command.
- */
-static void ataAbortCurrentCommand(AHCIATADevState *s, bool fResetDrive)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- AHCIATARequest Req;
-
- Assert(PDMCritSectIsOwner(&pCtl->lock));
-
- /* Do not issue new requests while the RESET line is asserted. */
- if (pCtl->fReset)
- {
- Log2(("%s: Ctl: suppressed aborting command as RESET is active\n", __FUNCTION__));
- return;
- }
-
- Req.ReqType = AHCIATA_AIO_ABORT;
- Req.u.a.iIf = pCtl->iSelectedIf;
- Req.u.a.fResetDrive = fResetDrive;
- ataSetStatus(s, ATA_STAT_BUSY);
- Log2(("%s: Ctl: message to async I/O thread, abort command on LUN#%d\n", __FUNCTION__, s->iLUN));
- ataAsyncIOPutRequest(pCtl, &Req);
-}
-
-
-static void ataSetIRQ(AHCIATADevState *s)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s);
-
- if (!(s->uATARegDevCtl & ATA_DEVCTL_DISABLE_IRQ))
- {
- Log2(("%s: LUN#%d asserting IRQ\n", __FUNCTION__, s->iLUN));
- /* The BMDMA unit unconditionally sets BM_STATUS_INT if the interrupt
- * line is asserted. It monitors the line for a rising edge. */
- if (!s->fIrqPending)
- pCtl->BmDma.u8Status |= BM_STATUS_INT;
- /* Only actually set the IRQ line if updating the currently selected drive. */
- if (s == &pCtl->aIfs[pCtl->iSelectedIf])
- PDMDevHlpISASetIrq(pDevIns, pCtl->irq, 1);
- }
- s->fIrqPending = true;
-}
-
-#endif /* IN_RING3 */
-
-static void ataUnsetIRQ(AHCIATADevState *s)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s);
-
- if (!(s->uATARegDevCtl & ATA_DEVCTL_DISABLE_IRQ))
- {
- Log2(("%s: LUN#%d deasserting IRQ\n", __FUNCTION__, s->iLUN));
- /* Only actually unset the IRQ line if updating the currently selected drive. */
- if (s == &pCtl->aIfs[pCtl->iSelectedIf])
- PDMDevHlpISASetIrq(pDevIns, pCtl->irq, 0);
- }
- s->fIrqPending = false;
-}
-
-#ifdef IN_RING3
-
-static void ataPIOTransferStart(AHCIATADevState *s, uint32_t start, uint32_t size)
-{
- Log2(("%s: LUN#%d start %d size %d\n", __FUNCTION__, s->iLUN, start, size));
- s->iIOBufferPIODataStart = start;
- s->iIOBufferPIODataEnd = start + size;
- ataSetStatus(s, ATA_STAT_DRQ);
-}
-
-
-static void ataPIOTransferStop(AHCIATADevState *s)
-{
- Log2(("%s: LUN#%d\n", __FUNCTION__, s->iLUN));
- if (s->fATAPITransfer)
- {
- s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
- Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector));
- ataSetIRQ(s);
- s->fATAPITransfer = false;
- }
- s->cbTotalTransfer = 0;
- s->cbElementaryTransfer = 0;
- s->iIOBufferPIODataStart = 0;
- s->iIOBufferPIODataEnd = 0;
- s->iBeginTransfer = ATAFN_BT_NULL;
- s->iSourceSink = ATAFN_SS_NULL;
-}
-
-
-static void ataPIOTransferLimitATAPI(AHCIATADevState *s)
-{
- uint32_t cbLimit, cbTransfer;
-
- cbLimit = s->uATARegLCyl | (s->uATARegHCyl << 8);
- /* Use maximum transfer size if the guest requested 0. Avoids a hang. */
- if (cbLimit == 0)
- cbLimit = 0xfffe;
- Log2(("%s: byte count limit=%d\n", __FUNCTION__, cbLimit));
- if (cbLimit == 0xffff)
- cbLimit--;
- cbTransfer = RT_MIN(s->cbTotalTransfer, s->iIOBufferEnd - s->iIOBufferCur);
- if (cbTransfer > cbLimit)
- {
- /* Byte count limit for clipping must be even in this case */
- if (cbLimit & 1)
- cbLimit--;
- cbTransfer = cbLimit;
- }
- s->uATARegLCyl = cbTransfer;
- s->uATARegHCyl = cbTransfer >> 8;
- s->cbElementaryTransfer = cbTransfer;
-}
-
-
-static uint32_t ataGetNSectors(AHCIATADevState *s)
-{
- /* 0 means either 256 (LBA28) or 65536 (LBA48) sectors. */
- if (s->fLBA48)
- {
- if (!s->uATARegNSector && !s->uATARegNSectorHOB)
- return 65536;
- else
- return s->uATARegNSectorHOB << 8 | s->uATARegNSector;
- }
- else
- {
- if (!s->uATARegNSector)
- return 256;
- else
- return s->uATARegNSector;
- }
-}
-
-
-static void ataPadString(uint8_t *pbDst, const char *pbSrc, uint32_t cbSize)
-{
- for (uint32_t i = 0; i < cbSize; i++)
- {
- if (*pbSrc)
- pbDst[i ^ 1] = *pbSrc++;
- else
- pbDst[i ^ 1] = ' ';
- }
-}
-
-
-static void ataSCSIPadStr(uint8_t *pbDst, const char *pbSrc, uint32_t cbSize)
-{
- for (uint32_t i = 0; i < cbSize; i++)
- {
- if (*pbSrc)
- pbDst[i] = *pbSrc++;
- else
- pbDst[i] = ' ';
- }
-}
-
-
-DECLINLINE(void) ataH2BE_U16(uint8_t *pbBuf, uint16_t val)
-{
- pbBuf[0] = val >> 8;
- pbBuf[1] = val;
-}
-
-
-DECLINLINE(void) ataH2BE_U24(uint8_t *pbBuf, uint32_t val)
-{
- pbBuf[0] = val >> 16;
- pbBuf[1] = val >> 8;
- pbBuf[2] = val;
-}
-
-
-DECLINLINE(void) ataH2BE_U32(uint8_t *pbBuf, uint32_t val)
-{
- pbBuf[0] = val >> 24;
- pbBuf[1] = val >> 16;
- pbBuf[2] = val >> 8;
- pbBuf[3] = val;
-}
-
-
-DECLINLINE(uint16_t) ataBE2H_U16(const uint8_t *pbBuf)
-{
- return (pbBuf[0] << 8) | pbBuf[1];
-}
-
-
-DECLINLINE(uint32_t) ataBE2H_U24(const uint8_t *pbBuf)
-{
- return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2];
-}
-
-
-DECLINLINE(uint32_t) ataBE2H_U32(const uint8_t *pbBuf)
-{
- return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3];
-}
-
-
-DECLINLINE(void) ataLBA2MSF(uint8_t *pbBuf, uint32_t iATAPILBA)
-{
- iATAPILBA += 150;
- pbBuf[0] = (iATAPILBA / 75) / 60;
- pbBuf[1] = (iATAPILBA / 75) % 60;
- pbBuf[2] = iATAPILBA % 75;
-}
-
-
-DECLINLINE(uint32_t) ataMSF2LBA(const uint8_t *pbBuf)
-{
- return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
-}
-
-static uint32_t ataChecksum(void *ptr, size_t count)
-{
- uint8_t u8Sum = 0xa5;
- uint8_t *p = (uint8_t*)ptr;
- size_t i;
-
- for (i = 0; i < count; i++)
- {
- u8Sum += *p++;
- }
-
- return (uint8_t)-(int32_t)u8Sum;
-}
-
-static void ataCmdOK(AHCIATADevState *s, uint8_t status)
-{
- s->uATARegError = 0; /* Not needed by ATA spec, but cannot hurt. */
- ataSetStatusValue(s, ATA_STAT_READY | status);
-}
-
-
-static void ataCmdError(AHCIATADevState *s, uint8_t uErrorCode)
-{
- Log(("%s: code=%#x\n", __FUNCTION__, uErrorCode));
- s->uATARegError = uErrorCode;
- ataSetStatusValue(s, ATA_STAT_READY | ATA_STAT_ERR);
- s->cbTotalTransfer = 0;
- s->cbElementaryTransfer = 0;
- s->iIOBufferCur = 0;
- s->iIOBufferEnd = 0;
- s->uTxDir = PDMBLOCKTXDIR_NONE;
- s->iBeginTransfer = ATAFN_BT_NULL;
- s->iSourceSink = ATAFN_SS_NULL;
-}
-
-
-static bool ataIdentifySS(AHCIATADevState *s)
-{
- uint16_t *p;
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer == 512);
-
- p = (uint16_t *)s->CTXALLSUFF(pbIOBuffer);
- memset(p, 0, 512);
- p[0] = RT_H2LE_U16(0x0040);
- p[1] = RT_H2LE_U16(RT_MIN(s->PCHSGeometry.cCylinders, 16383));
- p[3] = RT_H2LE_U16(s->PCHSGeometry.cHeads);
- /* Block size; obsolete, but required for the BIOS. */
- p[5] = RT_H2LE_U16(512);
- p[6] = RT_H2LE_U16(s->PCHSGeometry.cSectors);
- ataPadString((uint8_t *)(p + 10), s->pszSerialNumber, ATA_SERIAL_NUMBER_LENGTH); /* serial number */
- p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */
- p[21] = RT_H2LE_U16(512); /* XXX: retired, cache size in sectors */
- p[22] = RT_H2LE_U16(0); /* ECC bytes per sector */
- ataPadString((uint8_t *)(p + 23), s->pszFirmwareRevision, ATA_FIRMWARE_REVISION_LENGTH); /* firmware version */
- ataPadString((uint8_t *)(p + 27), s->pszModelNumber, ATA_MODEL_NUMBER_LENGTH); /* model */
-#if ATA_MAX_MULT_SECTORS > 1
- p[47] = RT_H2LE_U16(0x8000 | ATA_MAX_MULT_SECTORS);
-#endif
- p[48] = RT_H2LE_U16(1); /* dword I/O, used by the BIOS */
- p[49] = RT_H2LE_U16(1 << 11 | 1 << 9 | 1 << 8); /* DMA and LBA supported */
- p[50] = RT_H2LE_U16(1 << 14); /* No drive specific standby timer minimum */
- p[51] = RT_H2LE_U16(240); /* PIO transfer cycle */
- p[52] = RT_H2LE_U16(240); /* DMA transfer cycle */
- p[53] = RT_H2LE_U16(1 | 1 << 1 | 1 << 2); /* words 54-58,64-70,88 valid */
- p[54] = RT_H2LE_U16(RT_MIN(s->PCHSGeometry.cCylinders, 16383));
- p[55] = RT_H2LE_U16(s->PCHSGeometry.cHeads);
- p[56] = RT_H2LE_U16(s->PCHSGeometry.cSectors);
- p[57] = RT_H2LE_U16( RT_MIN(s->PCHSGeometry.cCylinders, 16383)
- * s->PCHSGeometry.cHeads
- * s->PCHSGeometry.cSectors);
- p[58] = RT_H2LE_U16( RT_MIN(s->PCHSGeometry.cCylinders, 16383)
- * s->PCHSGeometry.cHeads
- * s->PCHSGeometry.cSectors >> 16);
- if (s->cMultSectors)
- p[59] = RT_H2LE_U16(0x100 | s->cMultSectors);
- if (s->cTotalSectors <= (1 << 28) - 1)
- {
- p[60] = RT_H2LE_U16(s->cTotalSectors);
- p[61] = RT_H2LE_U16(s->cTotalSectors >> 16);
- }
- else
- {
- /* Report maximum number of sectors possible with LBA28 */
- p[60] = RT_H2LE_U16(((1 << 28) - 1) & 0xffff);
- p[61] = RT_H2LE_U16(((1 << 28) - 1) >> 16);
- }
- p[63] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_MDMA, ATA_MDMA_MODE_MAX, s->uATATransferMode)); /* MDMA modes supported / mode enabled */
- p[64] = RT_H2LE_U16(ATA_PIO_MODE_MAX > 2 ? (1 << (ATA_PIO_MODE_MAX - 2)) - 1 : 0); /* PIO modes beyond PIO2 supported */
- p[65] = RT_H2LE_U16(120); /* minimum DMA multiword tx cycle time */
- p[66] = RT_H2LE_U16(120); /* recommended DMA multiword tx cycle time */
- p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */
- p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
- if (s->pDrvBlock->pfnDiscard)
- {
- p[80] = RT_H2LE_U16(0x1f0); /* support everything up to ATA/ATAPI-8 ACS */
- p[81] = RT_H2LE_U16(0x28); /* conforms to ATA/ATAPI-8 ACS */
- }
- else
- {
- p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
- p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
- }
- p[82] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* supports power management, write cache and look-ahead */
- if (s->cTotalSectors <= (1 << 28) - 1)
- p[83] = RT_H2LE_U16(1 << 14 | 1 << 12); /* supports FLUSH CACHE */
- else
- p[83] = RT_H2LE_U16(1 << 14 | 1 << 10 | 1 << 12 | 1 << 13); /* supports LBA48, FLUSH CACHE and FLUSH CACHE EXT */
- p[84] = RT_H2LE_U16(1 << 14);
- p[85] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* enabled power management, write cache and look-ahead */
- if (s->cTotalSectors <= (1 << 28) - 1)
- p[86] = RT_H2LE_U16(1 << 12); /* enabled FLUSH CACHE */
- else
- p[86] = RT_H2LE_U16(1 << 10 | 1 << 12 | 1 << 13); /* enabled LBA48, FLUSH CACHE and FLUSH CACHE EXT */
- p[87] = RT_H2LE_U16(1 << 14);
- p[88] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_UDMA, ATA_UDMA_MODE_MAX, s->uATATransferMode)); /* UDMA modes supported / mode enabled */
- p[93] = RT_H2LE_U16((1 | 1 << 1) << ((s->iLUN & 1) == 0 ? 0 : 8) | 1 << 13 | 1 << 14);
- if (s->cTotalSectors > (1 << 28) - 1)
- {
- p[100] = RT_H2LE_U16(s->cTotalSectors);
- p[101] = RT_H2LE_U16(s->cTotalSectors >> 16);
- p[102] = RT_H2LE_U16(s->cTotalSectors >> 32);
- p[103] = RT_H2LE_U16(s->cTotalSectors >> 48);
- }
- if (s->pDrvBlock->pfnDiscard) /** @todo: Set bit 14 in word 69 too? (Deterministic read after TRIM). */
- p[169] = RT_H2LE_U16(1); /* DATA SET MANAGEMENT command supported. */
- if (s->fNonRotational)
- p[217] = RT_H2LE_U16(1); /* Non-rotational medium */
- uint32_t uCsum = ataChecksum(p, 510);
- p[255] = RT_H2LE_U16(0xa5 | (uCsum << 8)); /* Integrity word */
-
- s->iSourceSink = ATAFN_SS_NULL;
- ataCmdOK(s, ATA_STAT_SEEK);
- return false;
-}
-
-
-static bool ataFlushSS(AHCIATADevState *s)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- int rc;
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_NONE);
- Assert(!s->cbElementaryTransfer);
-
- PDMCritSectLeave(&pCtl->lock);
-
- STAM_PROFILE_START(&s->StatFlushes, f);
- rc = s->pDrvBlock->pfnFlush(s->pDrvBlock);
- AssertRC(rc);
- STAM_PROFILE_STOP(&s->StatFlushes, f);
-
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- ataCmdOK(s, 0);
- return false;
-}
-
-
-static bool atapiIdentifySS(AHCIATADevState *s)
-{
- uint16_t *p;
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer == 512);
-
- p = (uint16_t *)s->CTXALLSUFF(pbIOBuffer);
- memset(p, 0, 512);
- /* Removable CDROM, 50us response, 12 byte packets */
- p[0] = RT_H2LE_U16(2 << 14 | 5 << 8 | 1 << 7 | 2 << 5 | 0 << 0);
- ataPadString((uint8_t *)(p + 10), s->pszSerialNumber, ATA_SERIAL_NUMBER_LENGTH); /* serial number */
- p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */
- p[21] = RT_H2LE_U16(512); /* XXX: retired, cache size in sectors */
- ataPadString((uint8_t *)(p + 23), s->pszFirmwareRevision, ATA_FIRMWARE_REVISION_LENGTH); /* firmware version */
- ataPadString((uint8_t *)(p + 27), s->pszModelNumber, ATA_MODEL_NUMBER_LENGTH); /* model */
- p[49] = RT_H2LE_U16(1 << 11 | 1 << 9 | 1 << 8); /* DMA and LBA supported */
- p[50] = RT_H2LE_U16(1 << 14); /* No drive specific standby timer minimum */
- p[51] = RT_H2LE_U16(240); /* PIO transfer cycle */
- p[52] = RT_H2LE_U16(240); /* DMA transfer cycle */
- p[53] = RT_H2LE_U16(1 << 1 | 1 << 2); /* words 64-70,88 are valid */
- p[63] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_MDMA, ATA_MDMA_MODE_MAX, s->uATATransferMode)); /* MDMA modes supported / mode enabled */
- p[64] = RT_H2LE_U16(ATA_PIO_MODE_MAX > 2 ? (1 << (ATA_PIO_MODE_MAX - 2)) - 1 : 0); /* PIO modes beyond PIO2 supported */
- p[65] = RT_H2LE_U16(120); /* minimum DMA multiword tx cycle time */
- p[66] = RT_H2LE_U16(120); /* recommended DMA multiword tx cycle time */
- p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */
- p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
- p[73] = RT_H2LE_U16(0x003e); /* ATAPI CDROM major */
- p[74] = RT_H2LE_U16(9); /* ATAPI CDROM minor */
- p[75] = RT_H2LE_U16(1); /* queue depth 1 */
- p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
- p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
- p[82] = RT_H2LE_U16(1 << 4 | 1 << 9); /* supports packet command set and DEVICE RESET */
- p[83] = RT_H2LE_U16(1 << 14);
- p[84] = RT_H2LE_U16(1 << 14);
- p[85] = RT_H2LE_U16(1 << 4 | 1 << 9); /* enabled packet command set and DEVICE RESET */
- p[86] = RT_H2LE_U16(0);
- p[87] = RT_H2LE_U16(1 << 14);
- p[88] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_UDMA, ATA_UDMA_MODE_MAX, s->uATATransferMode)); /* UDMA modes supported / mode enabled */
- p[93] = RT_H2LE_U16((1 | 1 << 1) << ((s->iLUN & 1) == 0 ? 0 : 8) | 1 << 13 | 1 << 14);
- uint32_t uCsum = ataChecksum(p, 510);
- p[255] = RT_H2LE_U16(0xa5 | (uCsum << 8)); /* Integrity word */
- s->iSourceSink = ATAFN_SS_NULL;
- ataCmdOK(s, ATA_STAT_SEEK);
- return false;
-}
-
-
-static void ataSetSignature(AHCIATADevState *s)
-{
- s->uATARegSelect &= 0xf0; /* clear head */
- /* put signature */
- s->uATARegNSector = 1;
- s->uATARegSector = 1;
- if (s->fATAPI)
- {
- s->uATARegLCyl = 0x14;
- s->uATARegHCyl = 0xeb;
- }
- else if (s->pDrvBlock)
- {
- s->uATARegLCyl = 0;
- s->uATARegHCyl = 0;
- }
- else
- {
- s->uATARegLCyl = 0xff;
- s->uATARegHCyl = 0xff;
- }
-}
-
-
-static uint64_t ataGetSector(AHCIATADevState *s)
-{
- uint64_t iLBA;
- if (s->uATARegSelect & 0x40)
- {
- /* any LBA variant */
- if (s->fLBA48)
- {
- /* LBA48 */
- iLBA = ((uint64_t)s->uATARegHCylHOB << 40) |
- ((uint64_t)s->uATARegLCylHOB << 32) |
- ((uint64_t)s->uATARegSectorHOB << 24) |
- ((uint64_t)s->uATARegHCyl << 16) |
- ((uint64_t)s->uATARegLCyl << 8) |
- s->uATARegSector;
- }
- else
- {
- /* LBA */
- iLBA = ((s->uATARegSelect & 0x0f) << 24) | (s->uATARegHCyl << 16) |
- (s->uATARegLCyl << 8) | s->uATARegSector;
- }
- }
- else
- {
- /* CHS */
- iLBA = ((s->uATARegHCyl << 8) | s->uATARegLCyl) * s->PCHSGeometry.cHeads * s->PCHSGeometry.cSectors +
- (s->uATARegSelect & 0x0f) * s->PCHSGeometry.cSectors +
- (s->uATARegSector - 1);
- }
- return iLBA;
-}
-
-static void ataSetSector(AHCIATADevState *s, uint64_t iLBA)
-{
- uint32_t cyl, r;
- if (s->uATARegSelect & 0x40)
- {
- /* any LBA variant */
- if (s->fLBA48)
- {
- /* LBA48 */
- s->uATARegHCylHOB = iLBA >> 40;
- s->uATARegLCylHOB = iLBA >> 32;
- s->uATARegSectorHOB = iLBA >> 24;
- s->uATARegHCyl = iLBA >> 16;
- s->uATARegLCyl = iLBA >> 8;
- s->uATARegSector = iLBA;
- }
- else
- {
- /* LBA */
- s->uATARegSelect = (s->uATARegSelect & 0xf0) | (iLBA >> 24);
- s->uATARegHCyl = (iLBA >> 16);
- s->uATARegLCyl = (iLBA >> 8);
- s->uATARegSector = (iLBA);
- }
- }
- else
- {
- /* CHS */
- cyl = iLBA / (s->PCHSGeometry.cHeads * s->PCHSGeometry.cSectors);
- r = iLBA % (s->PCHSGeometry.cHeads * s->PCHSGeometry.cSectors);
- s->uATARegHCyl = cyl >> 8;
- s->uATARegLCyl = cyl;
- s->uATARegSelect = (s->uATARegSelect & 0xf0) | ((r / s->PCHSGeometry.cSectors) & 0x0f);
- s->uATARegSector = (r % s->PCHSGeometry.cSectors) + 1;
- }
-}
-
-
-static void ataWarningDiskFull(PPDMDEVINS pDevIns)
-{
- int rc;
- LogRel(("AHCI ATA: Host disk full\n"));
- rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevATA_DISKFULL",
- N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
- AssertRC(rc);
-}
-
-static void ataWarningFileTooBig(PPDMDEVINS pDevIns)
-{
- int rc;
- LogRel(("AHCI ATA: File too big\n"));
- rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevATA_FILETOOBIG",
- N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files"));
- AssertRC(rc);
-}
-
-static void ataWarningISCSI(PPDMDEVINS pDevIns)
-{
- int rc;
- LogRel(("AHCI ATA: iSCSI target unavailable\n"));
- rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevATA_ISCSIDOWN",
- N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
- AssertRC(rc);
-}
-
-static bool ataIsRedoSetWarning(AHCIATADevState *s, int rc)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- Assert(!PDMCritSectIsOwner(&pCtl->lock));
- if (rc == VERR_DISK_FULL)
- {
- pCtl->fRedoIdle = true;
- ataWarningDiskFull(ATADEVSTATE_2_DEVINS(s));
- return true;
- }
- if (rc == VERR_FILE_TOO_BIG)
- {
- pCtl->fRedoIdle = true;
- ataWarningFileTooBig(ATADEVSTATE_2_DEVINS(s));
- return true;
- }
- if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
- {
- pCtl->fRedoIdle = true;
- /* iSCSI connection abort (first error) or failure to reestablish
- * connection (second error). Pause VM. On resume we'll retry. */
- ataWarningISCSI(ATADEVSTATE_2_DEVINS(s));
- return true;
- }
- return false;
-}
-
-
-static int ataReadSectors(AHCIATADevState *s, uint64_t u64Sector, void *pvBuf,
- uint32_t cSectors, bool *pfRedo)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- int rc;
-
- PDMCritSectLeave(&pCtl->lock);
-
- STAM_PROFILE_ADV_START(&s->StatReads, r);
- s->pLed->Asserted.s.fReading = s->pLed->Actual.s.fReading = 1;
- rc = s->pDrvBlock->pfnRead(s->pDrvBlock, u64Sector * 512, pvBuf, cSectors * 512);
- s->pLed->Actual.s.fReading = 0;
- STAM_PROFILE_ADV_STOP(&s->StatReads, r);
-
- STAM_REL_COUNTER_ADD(s->pStatBytesRead, cSectors * 512);
-
- if (RT_SUCCESS(rc))
- *pfRedo = false;
- else
- *pfRedo = ataIsRedoSetWarning(s, rc);
-
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- return rc;
-}
-
-
-static int ataWriteSectors(AHCIATADevState *s, uint64_t u64Sector,
- const void *pvBuf, uint32_t cSectors, bool *pfRedo)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- int rc;
-
- PDMCritSectLeave(&pCtl->lock);
-
- STAM_PROFILE_ADV_START(&s->StatWrites, w);
- s->pLed->Asserted.s.fWriting = s->pLed->Actual.s.fWriting = 1;
- rc = s->pDrvBlock->pfnWrite(s->pDrvBlock, u64Sector * 512, pvBuf, cSectors * 512);
- s->pLed->Actual.s.fWriting = 0;
- STAM_PROFILE_ADV_STOP(&s->StatWrites, w);
-
- STAM_REL_COUNTER_ADD(s->pStatBytesWritten, cSectors * 512);
-
- if (RT_SUCCESS(rc))
- *pfRedo = false;
- else
- *pfRedo = ataIsRedoSetWarning(s, rc);
-
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- return rc;
-}
-
-
-static void ataReadWriteSectorsBT(AHCIATADevState *s)
-{
- uint32_t cSectors;
-
- cSectors = s->cbTotalTransfer / 512;
- if (cSectors > s->cSectorsPerIRQ)
- s->cbElementaryTransfer = s->cSectorsPerIRQ * 512;
- else
- s->cbElementaryTransfer = cSectors * 512;
- if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
- ataCmdOK(s, 0);
-}
-
-
-static bool ataReadSectorsSS(AHCIATADevState *s)
-{
- int rc;
- uint32_t cSectors;
- uint64_t iLBA;
- bool fRedo;
-
- cSectors = s->cbElementaryTransfer / 512;
- Assert(cSectors);
- iLBA = ataGetSector(s);
- Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iLBA));
- rc = ataReadSectors(s, iLBA, s->CTXALLSUFF(pbIOBuffer), cSectors, &fRedo);
- if (RT_SUCCESS(rc))
- {
- ataSetSector(s, iLBA + cSectors);
- if (s->cbElementaryTransfer == s->cbTotalTransfer)
- s->iSourceSink = ATAFN_SS_NULL;
- ataCmdOK(s, ATA_STAT_SEEK);
- }
- else
- {
- if (fRedo)
- return true;
- if (s->cErrors++ < MAX_LOG_REL_ERRORS)
- LogRel(("AHCI ATA: LUN#%d: disk read error (rc=%Rrc iSector=%#RX64 cSectors=%#RX32)\n",
- s->iLUN, rc, iLBA, cSectors));
- ataCmdError(s, ID_ERR);
- }
- return false;
-}
-
-
-static bool ataWriteSectorsSS(AHCIATADevState *s)
-{
- int rc;
- uint32_t cSectors;
- uint64_t iLBA;
- bool fRedo;
-
- cSectors = s->cbElementaryTransfer / 512;
- Assert(cSectors);
- iLBA = ataGetSector(s);
- Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iLBA));
- rc = ataWriteSectors(s, iLBA, s->CTXALLSUFF(pbIOBuffer), cSectors, &fRedo);
- if (RT_SUCCESS(rc))
- {
- ataSetSector(s, iLBA + cSectors);
- if (!s->cbTotalTransfer)
- s->iSourceSink = ATAFN_SS_NULL;
- ataCmdOK(s, ATA_STAT_SEEK);
- }
- else
- {
- if (fRedo)
- return true;
- if (s->cErrors++ < MAX_LOG_REL_ERRORS)
- LogRel(("AHCI ATA: LUN#%d: disk write error (rc=%Rrc iSector=%#RX64 cSectors=%#RX32)\n",
- s->iLUN, rc, iLBA, cSectors));
- ataCmdError(s, ID_ERR);
- }
- return false;
-}
-
-
-static void atapiCmdOK(AHCIATADevState *s)
-{
- s->uATARegError = 0;
- ataSetStatusValue(s, ATA_STAT_READY);
- s->uATARegNSector = (s->uATARegNSector & ~7)
- | ((s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE) ? ATAPI_INT_REASON_IO : 0)
- | (!s->cbTotalTransfer ? ATAPI_INT_REASON_CD : 0);
- Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector));
- memset(s->abATAPISense, '\0', sizeof(s->abATAPISense));
- s->abATAPISense[0] = 0x70 | (1 << 7);
- s->abATAPISense[7] = 10;
-}
-
-
-static void atapiCmdError(AHCIATADevState *s, const uint8_t *pabATAPISense, size_t cbATAPISense)
-{
- Log(("%s: sense=%#x (%s) asc=%#x ascq=%#x (%s)\n", __FUNCTION__, pabATAPISense[2] & 0x0f, SCSISenseText(pabATAPISense[2] & 0x0f),
- pabATAPISense[12], pabATAPISense[13], SCSISenseExtText(pabATAPISense[12], pabATAPISense[13])));
- s->uATARegError = pabATAPISense[2] << 4;
- ataSetStatusValue(s, ATA_STAT_READY | ATA_STAT_ERR);
- s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
- Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector));
- memset(s->abATAPISense, '\0', sizeof(s->abATAPISense));
- memcpy(s->abATAPISense, pabATAPISense, RT_MIN(cbATAPISense, sizeof(s->abATAPISense)));
- s->cbTotalTransfer = 0;
- s->cbElementaryTransfer = 0;
- s->iIOBufferCur = 0;
- s->iIOBufferEnd = 0;
- s->uTxDir = PDMBLOCKTXDIR_NONE;
- s->iBeginTransfer = ATAFN_BT_NULL;
- s->iSourceSink = ATAFN_SS_NULL;
-}
-
-
-/** @todo deprecated function - doesn't provide enough info. Replace by direct
- * calls to atapiCmdError() with full data. */
-static void atapiCmdErrorSimple(AHCIATADevState *s, uint8_t uATAPISenseKey, uint8_t uATAPIASC)
-{
- uint8_t abATAPISense[ATAPI_SENSE_SIZE];
- memset(abATAPISense, '\0', sizeof(abATAPISense));
- abATAPISense[0] = 0x70 | (1 << 7);
- abATAPISense[2] = uATAPISenseKey & 0x0f;
- abATAPISense[7] = 10;
- abATAPISense[12] = uATAPIASC;
- atapiCmdError(s, abATAPISense, sizeof(abATAPISense));
-}
-
-
-static void atapiCmdBT(AHCIATADevState *s)
-{
- s->fATAPITransfer = true;
- s->cbElementaryTransfer = s->cbTotalTransfer;
- if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
- atapiCmdOK(s);
-}
-
-
-static void atapiPassthroughCmdBT(AHCIATADevState *s)
-{
- /* @todo implement an algorithm for correctly determining the read and
- * write sector size without sending additional commands to the drive.
- * This should be doable by saving processing the configuration requests
- * and replies. */
-#if 0
- if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
- {
- uint8_t cmd = s->aATAPICmd[0];
- if (cmd == SCSI_WRITE_10 || cmd == SCSI_WRITE_12 || cmd == SCSI_WRITE_AND_VERIFY_10)
- {
- uint8_t aModeSenseCmd[10];
- uint8_t aModeSenseResult[16];
- uint8_t uDummySense;
- uint32_t cbTransfer;
- int rc;
-
- cbTransfer = sizeof(aModeSenseResult);
- aModeSenseCmd[0] = SCSI_MODE_SENSE_10;
- aModeSenseCmd[1] = 0x08; /* disable block descriptor = 1 */
- aModeSenseCmd[2] = (SCSI_PAGECONTROL_CURRENT << 6) | SCSI_MODEPAGE_WRITE_PARAMETER;
- aModeSenseCmd[3] = 0; /* subpage code */
- aModeSenseCmd[4] = 0; /* reserved */
- aModeSenseCmd[5] = 0; /* reserved */
- aModeSenseCmd[6] = 0; /* reserved */
- aModeSenseCmd[7] = cbTransfer >> 8;
- aModeSenseCmd[8] = cbTransfer & 0xff;
- aModeSenseCmd[9] = 0; /* control */
- rc = s->pDrvBlock->pfnSendCmd(s->pDrvBlock, aModeSenseCmd, PDMBLOCKTXDIR_FROM_DEVICE, aModeSenseResult, &cbTransfer, &uDummySense, 500);
- if (RT_FAILURE(rc))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_NONE);
- return;
- }
- /* Select sector size based on the current data block type. */
- switch (aModeSenseResult[12] & 0x0f)
- {
- case 0:
- s->cbATAPISector = 2352;
- break;
- case 1:
- s->cbATAPISector = 2368;
- break;
- case 2:
- case 3:
- s->cbATAPISector = 2448;
- break;
- case 8:
- case 10:
- s->cbATAPISector = 2048;
- break;
- case 9:
- s->cbATAPISector = 2336;
- break;
- case 11:
- s->cbATAPISector = 2056;
- break;
- case 12:
- s->cbATAPISector = 2324;
- break;
- case 13:
- s->cbATAPISector = 2332;
- break;
- default:
- s->cbATAPISector = 0;
- }
- Log2(("%s: sector size %d\n", __FUNCTION__, s->cbATAPISector));
- s->cbTotalTransfer *= s->cbATAPISector;
- if (s->cbTotalTransfer == 0)
- s->uTxDir = PDMBLOCKTXDIR_NONE;
- }
- }
-#endif
- atapiCmdBT(s);
-}
-
-
-static bool atapiReadSS(AHCIATADevState *s)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- int rc = VINF_SUCCESS;
- uint32_t cbTransfer, cSectors;
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- cbTransfer = RT_MIN(s->cbTotalTransfer, s->cbIOBuffer);
- cSectors = cbTransfer / s->cbATAPISector;
- Assert(cSectors * s->cbATAPISector <= cbTransfer);
- Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, s->iATAPILBA));
-
- PDMCritSectLeave(&pCtl->lock);
-
- STAM_PROFILE_ADV_START(&s->StatReads, r);
- s->pLed->Asserted.s.fReading = s->pLed->Actual.s.fReading = 1;
- switch (s->cbATAPISector)
- {
- case 2048:
- rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)s->iATAPILBA * s->cbATAPISector, s->CTXALLSUFF(pbIOBuffer), s->cbATAPISector * cSectors);
- break;
- case 2352:
- {
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
-
- for (uint32_t i = s->iATAPILBA; i < s->iATAPILBA + cSectors; i++)
- {
- /* sync bytes */
- *pbBuf++ = 0x00;
- memset(pbBuf, 0xff, 11);
- pbBuf += 11;
- /* MSF */
- ataLBA2MSF(pbBuf, i);
- pbBuf += 3;
- *pbBuf++ = 0x01; /* mode 1 data */
- /* data */
- rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)i * 2048, pbBuf, 2048);
- if (RT_FAILURE(rc))
- break;
- pbBuf += 2048;
- /* ECC */
- memset(pbBuf, 0, 288);
- pbBuf += 288;
- }
- }
- break;
- default:
- break;
- }
- STAM_PROFILE_ADV_STOP(&s->StatReads, r);
-
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
-
- if (RT_SUCCESS(rc))
- {
- s->pLed->Actual.s.fReading = 0;
- STAM_REL_COUNTER_ADD(s->pStatBytesRead, s->cbATAPISector * cSectors);
-
- /* The initial buffer end value has been set up based on the total
- * transfer size. But the I/O buffer size limits what can actually be
- * done in one transfer, so set the actual value of the buffer end. */
- s->cbElementaryTransfer = cbTransfer;
- if (cbTransfer >= s->cbTotalTransfer)
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- s->iATAPILBA += cSectors;
- }
- else
- {
- if (s->cErrors++ < MAX_LOG_REL_ERRORS)
- LogRel(("AHCI ATA: LUN#%d: CD-ROM read error, %d sectors at LBA %d\n", s->iLUN, cSectors, s->iATAPILBA));
- atapiCmdErrorSimple(s, SCSI_SENSE_MEDIUM_ERROR, SCSI_ASC_READ_ERROR);
- }
- return false;
-}
-
-
-static bool atapiPassthroughSS(AHCIATADevState *s)
-{
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- int rc = VINF_SUCCESS;
- uint8_t abATAPISense[ATAPI_SENSE_SIZE];
- uint32_t cbTransfer;
- PSTAMPROFILEADV pProf = NULL;
-
- cbTransfer = s->cbElementaryTransfer;
-
- if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
- Log3(("ATAPI PT data write (%d): %.*Rhxs\n", cbTransfer, cbTransfer, s->CTXALLSUFF(pbIOBuffer)));
-
- /* Simple heuristics: if there is at least one sector of data
- * to transfer, it's worth updating the LEDs. */
- if (cbTransfer >= 2048)
- {
- if (s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE)
- {
- s->pLed->Asserted.s.fReading = s->pLed->Actual.s.fReading = 1;
- pProf = &s->StatReads;
- }
- else
- {
- s->pLed->Asserted.s.fWriting = s->pLed->Actual.s.fWriting = 1;
- pProf = &s->StatWrites;
- }
- }
-
- PDMCritSectLeave(&pCtl->lock);
-
- memset(abATAPISense, '\0', sizeof(abATAPISense));
- if (pProf) { STAM_PROFILE_ADV_START(pProf, b); }
- if (cbTransfer > 100 * _1K)
- {
- /* Linux accepts commands with up to 100KB of data, but expects
- * us to handle commands with up to 128KB of data. The usual
- * imbalance of powers. */
- uint8_t aATAPICmd[ATAPI_PACKET_SIZE];
- uint32_t iATAPILBA, cSectors, cReqSectors, cbCurrTX;
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
-
- switch (s->aATAPICmd[0])
- {
- case SCSI_READ_10:
- case SCSI_WRITE_10:
- case SCSI_WRITE_AND_VERIFY_10:
- iATAPILBA = ataBE2H_U32(s->aATAPICmd + 2);
- cSectors = ataBE2H_U16(s->aATAPICmd + 7);
- break;
- case SCSI_READ_12:
- case SCSI_WRITE_12:
- iATAPILBA = ataBE2H_U32(s->aATAPICmd + 2);
- cSectors = ataBE2H_U32(s->aATAPICmd + 6);
- break;
- case SCSI_READ_CD:
- iATAPILBA = ataBE2H_U32(s->aATAPICmd + 2);
- cSectors = ataBE2H_U24(s->aATAPICmd + 6) / s->cbATAPISector;
- break;
- case SCSI_READ_CD_MSF:
- iATAPILBA = ataMSF2LBA(s->aATAPICmd + 3);
- cSectors = ataMSF2LBA(s->aATAPICmd + 6) - iATAPILBA;
- break;
- default:
- AssertMsgFailed(("Don't know how to split command %#04x\n", s->aATAPICmd[0]));
- if (s->cErrors++ < MAX_LOG_REL_ERRORS)
- LogRel(("AHCI ATA: LUN#%d: CD-ROM passthrough split error\n", s->iLUN));
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
- {
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- }
- return false;
- }
- memcpy(aATAPICmd, s->aATAPICmd, ATAPI_PACKET_SIZE);
- cReqSectors = 0;
- for (uint32_t i = cSectors; i > 0; i -= cReqSectors)
- {
- if (i * s->cbATAPISector > 100 * _1K)
- cReqSectors = (100 * _1K) / s->cbATAPISector;
- else
- cReqSectors = i;
- cbCurrTX = s->cbATAPISector * cReqSectors;
- switch (s->aATAPICmd[0])
- {
- case SCSI_READ_10:
- case SCSI_WRITE_10:
- case SCSI_WRITE_AND_VERIFY_10:
- ataH2BE_U32(aATAPICmd + 2, iATAPILBA);
- ataH2BE_U16(aATAPICmd + 7, cReqSectors);
- break;
- case SCSI_READ_12:
- case SCSI_WRITE_12:
- ataH2BE_U32(aATAPICmd + 2, iATAPILBA);
- ataH2BE_U32(aATAPICmd + 6, cReqSectors);
- break;
- case SCSI_READ_CD:
- ataH2BE_U32(s->aATAPICmd + 2, iATAPILBA);
- ataH2BE_U24(s->aATAPICmd + 6, cbCurrTX);
- break;
- case SCSI_READ_CD_MSF:
- ataLBA2MSF(aATAPICmd + 3, iATAPILBA);
- ataLBA2MSF(aATAPICmd + 6, iATAPILBA + cReqSectors);
- break;
- }
- rc = s->pDrvBlock->pfnSendCmd(s->pDrvBlock, aATAPICmd, (PDMBLOCKTXDIR)s->uTxDir, pbBuf, &cbCurrTX, abATAPISense, sizeof(abATAPISense), 30000 /**< @todo timeout */);
- if (rc != VINF_SUCCESS)
- break;
- iATAPILBA += cReqSectors;
- pbBuf += s->cbATAPISector * cReqSectors;
- }
- }
- else
- rc = s->pDrvBlock->pfnSendCmd(s->pDrvBlock, s->aATAPICmd, (PDMBLOCKTXDIR)s->uTxDir, s->CTXALLSUFF(pbIOBuffer), &cbTransfer, abATAPISense, sizeof(abATAPISense), 30000 /**< @todo timeout */);
- if (pProf) { STAM_PROFILE_ADV_STOP(pProf, b); }
-
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
-
- /* Update the LEDs and the read/write statistics. */
- if (cbTransfer >= 2048)
- {
- if (s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE)
- {
- s->pLed->Actual.s.fReading = 0;
- STAM_REL_COUNTER_ADD(s->pStatBytesRead, cbTransfer);
- }
- else
- {
- s->pLed->Actual.s.fWriting = 0;
- STAM_REL_COUNTER_ADD(s->pStatBytesWritten, cbTransfer);
- }
- }
-
- if (RT_SUCCESS(rc))
- {
- if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
- {
- Assert(cbTransfer <= s->cbTotalTransfer);
- /* Reply with the same amount of data as the real drive. */
- s->cbTotalTransfer = cbTransfer;
- /* The initial buffer end value has been set up based on the total
- * transfer size. But the I/O buffer size limits what can actually be
- * done in one transfer, so set the actual value of the buffer end. */
- s->cbElementaryTransfer = cbTransfer;
- if (s->aATAPICmd[0] == SCSI_INQUIRY)
- {
- /* Make sure that the real drive cannot be identified.
- * Motivation: changing the VM configuration should be as
- * invisible as possible to the guest. */
- Log3(("ATAPI PT inquiry data before (%d): %.*Rhxs\n", cbTransfer, cbTransfer, s->CTXALLSUFF(pbIOBuffer)));
- ataSCSIPadStr(s->CTXALLSUFF(pbIOBuffer) + 8, "VBOX", 8);
- ataSCSIPadStr(s->CTXALLSUFF(pbIOBuffer) + 16, "CD-ROM", 16);
- ataSCSIPadStr(s->CTXALLSUFF(pbIOBuffer) + 32, "1.0", 4);
- }
- if (cbTransfer)
- Log3(("ATAPI PT data read (%d): %.*Rhxs\n", cbTransfer, cbTransfer, s->CTXALLSUFF(pbIOBuffer)));
- }
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- }
- else
- {
- if (s->cErrors++ < MAX_LOG_REL_ERRORS)
- {
- uint8_t u8Cmd = s->aATAPICmd[0];
- do
- {
- /* don't log superfluous errors */
- if ( rc == VERR_DEV_IO_ERROR
- && ( u8Cmd == SCSI_TEST_UNIT_READY
- || u8Cmd == SCSI_READ_CAPACITY
- || u8Cmd == SCSI_READ_TOC_PMA_ATIP))
- break;
- LogRel(("AHCI ATA: LUN#%d: CD-ROM passthrough command (%#04x) error %d %Rrc\n", s->iLUN, u8Cmd, abATAPISense[0] & 0x0f, rc));
- } while (0);
- }
- atapiCmdError(s, abATAPISense, sizeof(abATAPISense));
- }
- return false;
-}
-
-
-static bool atapiReadSectors(AHCIATADevState *s, uint32_t iATAPILBA, uint32_t cSectors, uint32_t cbSector)
-{
- Assert(cSectors > 0);
- s->iATAPILBA = iATAPILBA;
- s->cbATAPISector = cbSector;
- ataStartTransfer(s, cSectors * cbSector, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ, true);
- return false;
-}
-
-
-static bool atapiReadCapacitySS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer <= 8);
- ataH2BE_U32(pbBuf, s->cTotalSectors - 1);
- ataH2BE_U32(pbBuf + 4, 2048);
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiReadDiscInformationSS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer <= 34);
- memset(pbBuf, '\0', 34);
- ataH2BE_U16(pbBuf, 32);
- pbBuf[2] = (0 << 4) | (3 << 2) | (2 << 0); /* not erasable, complete session, complete disc */
- pbBuf[3] = 1; /* number of first track */
- pbBuf[4] = 1; /* number of sessions (LSB) */
- pbBuf[5] = 1; /* first track number in last session (LSB) */
- pbBuf[6] = 1; /* last track number in last session (LSB) */
- pbBuf[7] = (0 << 7) | (0 << 6) | (1 << 5) | (0 << 2) | (0 << 0); /* disc id not valid, disc bar code not valid, unrestricted use, not dirty, not RW medium */
- pbBuf[8] = 0; /* disc type = CD-ROM */
- pbBuf[9] = 0; /* number of sessions (MSB) */
- pbBuf[10] = 0; /* number of sessions (MSB) */
- pbBuf[11] = 0; /* number of sessions (MSB) */
- ataH2BE_U32(pbBuf + 16, 0x00ffffff); /* last session lead-in start time is not available */
- ataH2BE_U32(pbBuf + 20, 0x00ffffff); /* last possible start time for lead-out is not available */
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiReadTrackInformationSS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer <= 36);
- /* Accept address/number type of 1 only, and only track 1 exists. */
- if ((s->aATAPICmd[1] & 0x03) != 1 || ataBE2H_U32(&s->aATAPICmd[2]) != 1)
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- return false;
- }
- memset(pbBuf, '\0', 36);
- ataH2BE_U16(pbBuf, 34);
- pbBuf[2] = 1; /* track number (LSB) */
- pbBuf[3] = 1; /* session number (LSB) */
- pbBuf[5] = (0 << 5) | (0 << 4) | (4 << 0); /* not damaged, primary copy, data track */
- pbBuf[6] = (0 << 7) | (0 << 6) | (0 << 5) | (0 << 6) | (1 << 0); /* not reserved track, not blank, not packet writing, not fixed packet, data mode 1 */
- pbBuf[7] = (0 << 1) | (0 << 0); /* last recorded address not valid, next recordable address not valid */
- ataH2BE_U32(pbBuf + 8, 0); /* track start address is 0 */
- ataH2BE_U32(pbBuf + 24, s->cTotalSectors); /* track size */
- pbBuf[32] = 0; /* track number (MSB) */
- pbBuf[33] = 0; /* session number (MSB) */
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-static size_t atapiGetConfigurationFillFeatureListProfiles(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf)
-{
- if (cbBuf < 3*4)
- return 0;
-
- ataH2BE_U16(pbBuf, 0x0); /* feature 0: list of profiles supported */
- pbBuf[2] = (0 << 2) | (1 << 1) | (1 || 0); /* version 0, persistent, current */
- pbBuf[3] = 8; /* additional bytes for profiles */
- /* The MMC-3 spec says that DVD-ROM read capability should be reported
- * before CD-ROM read capability. */
- ataH2BE_U16(pbBuf + 4, 0x10); /* profile: read-only DVD */
- pbBuf[6] = (0 << 0); /* NOT current profile */
- ataH2BE_U16(pbBuf + 8, 0x08); /* profile: read only CD */
- pbBuf[10] = (1 << 0); /* current profile */
-
- return 3*4; /* Header + 2 profiles entries */
-}
-
-static size_t atapiGetConfigurationFillFeatureCore(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf)
-{
- if (cbBuf < 12)
- return 0;
-
- ataH2BE_U16(pbBuf, 0x1); /* feature 0001h: Core Feature */
- pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
- pbBuf[3] = 8; /* Additional length */
- ataH2BE_U16(pbBuf + 4, 0x00000002); /* Physical interface ATAPI. */
- pbBuf[8] = RT_BIT(0); /* DBE */
- /* Rest is reserved. */
-
- return 12;
-}
-
-static size_t atapiGetConfigurationFillFeatureMorphing(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf)
-{
- if (cbBuf < 8)
- return 0;
-
- ataH2BE_U16(pbBuf, 0x2); /* feature 0002h: Morphing Feature */
- pbBuf[2] = (0x1 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
- pbBuf[3] = 4; /* Additional length */
- pbBuf[4] = RT_BIT(1) | 0x0; /* OCEvent | !ASYNC */
- /* Rest is reserved. */
-
- return 8;
-}
-
-static size_t atapiGetConfigurationFillFeatureRemovableMedium(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf)
-{
- if (cbBuf < 8)
- return 0;
-
- ataH2BE_U16(pbBuf, 0x3); /* feature 0003h: Removable Medium Feature */
- pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
- pbBuf[3] = 4; /* Additional length */
- /* Tray type loading | Load | Eject | !Pvnt Jmpr | !DBML | Lock */
- pbBuf[4] = (0x2 << 5) | RT_BIT(4) | RT_BIT(3) | (0x0 << 2) | (0x0 << 1) | RT_BIT(0);
- /* Rest is reserved. */
-
- return 8;
-}
-
-static size_t atapiGetConfigurationFillFeatureRandomReadable(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf)
-{
- if (cbBuf < 12)
- return 0;
-
- ataH2BE_U16(pbBuf, 0x10); /* feature 0010h: Random Readable Feature */
- pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
- pbBuf[3] = 8; /* Additional length */
- ataH2BE_U32(pbBuf + 4, 2048); /* Logical block size. */
- ataH2BE_U16(pbBuf + 8, 0x10); /* Blocking (0x10 for DVD, CD is not defined). */
- pbBuf[10] = 0; /* PP not present */
- /* Rest is reserved. */
-
- return 12;
-}
-
-static size_t atapiGetConfigurationFillFeatureCDRead(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf)
-{
- if (cbBuf < 8)
- return 0;
-
- ataH2BE_U16(pbBuf, 0x1e); /* feature 001Eh: CD Read Feature */
- pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
- pbBuf[3] = 0; /* Additional length */
- pbBuf[4] = (0x0 << 7) | (0x0 << 1) | 0x0; /* !DAP | !C2-Flags | !CD-Text. */
- /* Rest is reserved. */
-
- return 8;
-}
-
-static size_t atapiGetConfigurationFillFeaturePowerManagement(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf)
-{
- if (cbBuf < 4)
- return 0;
-
- ataH2BE_U16(pbBuf, 0x100); /* feature 0100h: Power Management Feature */
- pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
- pbBuf[3] = 0; /* Additional length */
-
- return 4;
-}
-
-static size_t atapiGetConfigurationFillFeatureTimeout(AHCIATADevState *s, uint8_t *pbBuf, size_t cbBuf)
-{
- if (cbBuf < 8)
- return 0;
-
- ataH2BE_U16(pbBuf, 0x105); /* feature 0105h: Timeout Feature */
- pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
- pbBuf[3] = 4; /* Additional length */
- pbBuf[4] = 0x0; /* !Group3 */
-
- return 8;
-}
-
-static bool atapiGetConfigurationSS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
- size_t cbBuf = s->cbIOBuffer;
- size_t cbCopied = 0;
- uint16_t u16Sfn = ataBE2H_U16(&s->aATAPICmd[2]);
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer <= 80);
- /* Accept valid request types only, and only starting feature 0. */
- if ((s->aATAPICmd[1] & 0x03) == 3 || u16Sfn != 0)
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- return false;
- }
- memset(pbBuf, '\0', cbBuf);
- /** @todo implement switching between CD-ROM and DVD-ROM profile (the only
- * way to differentiate them right now is based on the image size). */
- if (s->cTotalSectors)
- ataH2BE_U16(pbBuf + 6, 0x08); /* current profile: read-only CD */
- else
- ataH2BE_U16(pbBuf + 6, 0x00); /* current profile: none -> no media */
- cbBuf -= 8;
- pbBuf += 8;
-
- cbCopied = atapiGetConfigurationFillFeatureListProfiles(s, pbBuf, cbBuf);
- cbBuf -= cbCopied;
- pbBuf += cbCopied;
-
- cbCopied = atapiGetConfigurationFillFeatureCore(s, pbBuf, cbBuf);
- cbBuf -= cbCopied;
- pbBuf += cbCopied;
-
- cbCopied = atapiGetConfigurationFillFeatureMorphing(s, pbBuf, cbBuf);
- cbBuf -= cbCopied;
- pbBuf += cbCopied;
-
- cbCopied = atapiGetConfigurationFillFeatureRemovableMedium(s, pbBuf, cbBuf);
- cbBuf -= cbCopied;
- pbBuf += cbCopied;
-
- cbCopied = atapiGetConfigurationFillFeatureRandomReadable(s, pbBuf, cbBuf);
- cbBuf -= cbCopied;
- pbBuf += cbCopied;
-
- cbCopied = atapiGetConfigurationFillFeatureCDRead(s, pbBuf, cbBuf);
- cbBuf -= cbCopied;
- pbBuf += cbCopied;
-
- cbCopied = atapiGetConfigurationFillFeaturePowerManagement(s, pbBuf, cbBuf);
- cbBuf -= cbCopied;
- pbBuf += cbCopied;
-
- cbCopied = atapiGetConfigurationFillFeatureTimeout(s, pbBuf, cbBuf);
- cbBuf -= cbCopied;
- pbBuf += cbCopied;
-
- /* Set data length now. */
- ataH2BE_U32(s->CTX_SUFF(pbIOBuffer), s->cbIOBuffer - cbBuf);
-
- /* Other profiles we might want to add in the future: 0x40 (BD-ROM) and 0x50 (HDDVD-ROM) */
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiGetEventStatusNotificationSS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer <= 8);
-
- if (!(s->aATAPICmd[1] & 1))
- {
- /* no asynchronous operation supported */
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- return false;
- }
-
- uint32_t OldStatus, NewStatus;
- do
- {
- OldStatus = ASMAtomicReadU32(&s->MediaEventStatus);
- NewStatus = ATA_EVENT_STATUS_UNCHANGED;
- switch (OldStatus)
- {
- case ATA_EVENT_STATUS_MEDIA_NEW:
- /* mount */
- ataH2BE_U16(pbBuf + 0, 6);
- pbBuf[2] = 0x04; /* media */
- pbBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */
- pbBuf[4] = 0x02; /* new medium */
- pbBuf[5] = 0x02; /* medium present / door closed */
- pbBuf[6] = 0x00;
- pbBuf[7] = 0x00;
- break;
-
- case ATA_EVENT_STATUS_MEDIA_CHANGED:
- case ATA_EVENT_STATUS_MEDIA_REMOVED:
- /* umount */
- ataH2BE_U16(pbBuf + 0, 6);
- pbBuf[2] = 0x04; /* media */
- pbBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */
- pbBuf[4] = 0x03; /* media removal */
- pbBuf[5] = 0x00; /* medium absent / door closed */
- pbBuf[6] = 0x00;
- pbBuf[7] = 0x00;
- if (OldStatus == ATA_EVENT_STATUS_MEDIA_CHANGED)
- NewStatus = ATA_EVENT_STATUS_MEDIA_NEW;
- break;
-
- case ATA_EVENT_STATUS_MEDIA_EJECT_REQUESTED: /* currently unused */
- ataH2BE_U16(pbBuf + 0, 6);
- pbBuf[2] = 0x04; /* media */
- pbBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */
- pbBuf[4] = 0x01; /* eject requested (eject button pressed) */
- pbBuf[5] = 0x02; /* medium present / door closed */
- pbBuf[6] = 0x00;
- pbBuf[7] = 0x00;
- break;
-
- case ATA_EVENT_STATUS_UNCHANGED:
- default:
- ataH2BE_U16(pbBuf + 0, 6);
- pbBuf[2] = 0x01; /* operational change request / notification */
- pbBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */
- pbBuf[4] = 0x00;
- pbBuf[5] = 0x00;
- pbBuf[6] = 0x00;
- pbBuf[7] = 0x00;
- break;
- }
- } while (!ASMAtomicCmpXchgU32(&s->MediaEventStatus, NewStatus, OldStatus));
-
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiInquirySS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer <= 36);
- pbBuf[0] = 0x05; /* CD-ROM */
- pbBuf[1] = 0x80; /* removable */
-#if 1/*ndef VBOX*/ /** @todo implement MESN + AENC. (async notification on removal and stuff.) */
- pbBuf[2] = 0x00; /* ISO */
- pbBuf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
-#else
- pbBuf[2] = 0x00; /* ISO */
- pbBuf[3] = 0x91; /* format 1, MESN=1, AENC=9 ??? */
-#endif
- pbBuf[4] = 31; /* additional length */
- pbBuf[5] = 0; /* reserved */
- pbBuf[6] = 0; /* reserved */
- pbBuf[7] = 0; /* reserved */
- ataSCSIPadStr(pbBuf + 8, s->pszInquiryVendorId, 8);
- ataSCSIPadStr(pbBuf + 16, s->pszInquiryProductId, 16);
- ataSCSIPadStr(pbBuf + 32, s->pszInquiryRevision, 4);
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiModeSenseErrorRecoverySS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer <= 16);
- ataH2BE_U16(&pbBuf[0], 16 + 6);
- pbBuf[2] = 0x70;
- pbBuf[3] = 0;
- pbBuf[4] = 0;
- pbBuf[5] = 0;
- pbBuf[6] = 0;
- pbBuf[7] = 0;
-
- pbBuf[8] = 0x01;
- pbBuf[9] = 0x06;
- pbBuf[10] = 0x00;
- pbBuf[11] = 0x05;
- pbBuf[12] = 0x00;
- pbBuf[13] = 0x00;
- pbBuf[14] = 0x00;
- pbBuf[15] = 0x00;
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiModeSenseCDStatusSS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer <= 40);
- ataH2BE_U16(&pbBuf[0], 38);
- pbBuf[2] = 0x70;
- pbBuf[3] = 0;
- pbBuf[4] = 0;
- pbBuf[5] = 0;
- pbBuf[6] = 0;
- pbBuf[7] = 0;
-
- pbBuf[8] = 0x2a;
- pbBuf[9] = 30; /* page length */
- pbBuf[10] = 0x08; /* DVD-ROM read support */
- pbBuf[11] = 0x00; /* no write support */
- /* The following claims we support audio play. This is obviously false,
- * but the Linux generic CDROM support makes many features depend on this
- * capability. If it's not set, this causes many things to be disabled. */
- pbBuf[12] = 0x71; /* multisession support, mode 2 form 1/2 support, audio play */
- pbBuf[13] = 0x00; /* no subchannel reads supported */
- pbBuf[14] = (1 << 0) | (1 << 3) | (1 << 5); /* lock supported, eject supported, tray type loading mechanism */
- if (s->pDrvMount->pfnIsLocked(s->pDrvMount))
- pbBuf[14] |= 1 << 1; /* report lock state */
- pbBuf[15] = 0; /* no subchannel reads supported, no separate audio volume control, no changer etc. */
- ataH2BE_U16(&pbBuf[16], 5632); /* (obsolete) claim 32x speed support */
- ataH2BE_U16(&pbBuf[18], 2); /* number of audio volume levels */
- ataH2BE_U16(&pbBuf[20], s->cbIOBuffer / _1K); /* buffer size supported in Kbyte */
- ataH2BE_U16(&pbBuf[22], 5632); /* (obsolete) current read speed 32x */
- pbBuf[24] = 0; /* reserved */
- pbBuf[25] = 0; /* reserved for digital audio (see idx 15) */
- ataH2BE_U16(&pbBuf[26], 0); /* (obsolete) maximum write speed */
- ataH2BE_U16(&pbBuf[28], 0); /* (obsolete) current write speed */
- ataH2BE_U16(&pbBuf[30], 0); /* copy management revision supported 0=no CSS */
- pbBuf[32] = 0; /* reserved */
- pbBuf[33] = 0; /* reserved */
- pbBuf[34] = 0; /* reserved */
- pbBuf[35] = 1; /* rotation control CAV */
- ataH2BE_U16(&pbBuf[36], 0); /* current write speed */
- ataH2BE_U16(&pbBuf[38], 0); /* number of write speed performance descriptors */
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiRequestSenseSS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- memset(pbBuf, '\0', s->cbElementaryTransfer);
- memcpy(pbBuf, s->abATAPISense, RT_MIN(s->cbElementaryTransfer, sizeof(s->abATAPISense)));
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiMechanismStatusSS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer <= 8);
- ataH2BE_U16(pbBuf, 0);
- /* no current LBA */
- pbBuf[2] = 0;
- pbBuf[3] = 0;
- pbBuf[4] = 0;
- pbBuf[5] = 1;
- ataH2BE_U16(pbBuf + 6, 0);
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiReadTOCNormalSS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer), *q, iStartTrack;
- bool fMSF;
- uint32_t cbSize;
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- fMSF = (s->aATAPICmd[1] >> 1) & 1;
- iStartTrack = s->aATAPICmd[6];
- if (iStartTrack > 1 && iStartTrack != 0xaa)
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- return false;
- }
- q = pbBuf + 2;
- *q++ = 1; /* first session */
- *q++ = 1; /* last session */
- if (iStartTrack <= 1)
- {
- *q++ = 0; /* reserved */
- *q++ = 0x14; /* ADR, control */
- *q++ = 1; /* track number */
- *q++ = 0; /* reserved */
- if (fMSF)
- {
- *q++ = 0; /* reserved */
- ataLBA2MSF(q, 0);
- q += 3;
- }
- else
- {
- /* sector 0 */
- ataH2BE_U32(q, 0);
- q += 4;
- }
- }
- /* lead out track */
- *q++ = 0; /* reserved */
- *q++ = 0x14; /* ADR, control */
- *q++ = 0xaa; /* track number */
- *q++ = 0; /* reserved */
- if (fMSF)
- {
- *q++ = 0; /* reserved */
- ataLBA2MSF(q, s->cTotalSectors);
- q += 3;
- }
- else
- {
- ataH2BE_U32(q, s->cTotalSectors);
- q += 4;
- }
- cbSize = q - pbBuf;
- ataH2BE_U16(pbBuf, cbSize - 2);
- if (cbSize < s->cbTotalTransfer)
- s->cbTotalTransfer = cbSize;
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiReadTOCMultiSS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer);
- bool fMSF;
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- Assert(s->cbElementaryTransfer <= 12);
- fMSF = (s->aATAPICmd[1] >> 1) & 1;
- /* multi session: only a single session defined */
-/** @todo double-check this stuff against what a real drive says for a CD-ROM (not a CD-R) with only a single data session. Maybe solve the problem with "cdrdao read-toc" not being able to figure out whether numbers are in BCD or hex. */
- memset(pbBuf, 0, 12);
- pbBuf[1] = 0x0a;
- pbBuf[2] = 0x01;
- pbBuf[3] = 0x01;
- pbBuf[5] = 0x14; /* ADR, control */
- pbBuf[6] = 1; /* first track in last complete session */
- if (fMSF)
- {
- pbBuf[8] = 0; /* reserved */
- ataLBA2MSF(&pbBuf[9], 0);
- }
- else
- {
- /* sector 0 */
- ataH2BE_U32(pbBuf + 8, 0);
- }
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static bool atapiReadTOCRawSS(AHCIATADevState *s)
-{
- uint8_t *pbBuf = s->CTXALLSUFF(pbIOBuffer), *q, iStartTrack;
- bool fMSF;
- uint32_t cbSize;
-
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- fMSF = (s->aATAPICmd[1] >> 1) & 1;
- iStartTrack = s->aATAPICmd[6];
-
- q = pbBuf + 2;
- *q++ = 1; /* first session */
- *q++ = 1; /* last session */
-
- *q++ = 1; /* session number */
- *q++ = 0x14; /* data track */
- *q++ = 0; /* track number */
- *q++ = 0xa0; /* first track in program area */
- *q++ = 0; /* min */
- *q++ = 0; /* sec */
- *q++ = 0; /* frame */
- *q++ = 0;
- *q++ = 1; /* first track */
- *q++ = 0x00; /* disk type CD-DA or CD data */
- *q++ = 0;
-
- *q++ = 1; /* session number */
- *q++ = 0x14; /* data track */
- *q++ = 0; /* track number */
- *q++ = 0xa1; /* last track in program area */
- *q++ = 0; /* min */
- *q++ = 0; /* sec */
- *q++ = 0; /* frame */
- *q++ = 0;
- *q++ = 1; /* last track */
- *q++ = 0;
- *q++ = 0;
-
- *q++ = 1; /* session number */
- *q++ = 0x14; /* data track */
- *q++ = 0; /* track number */
- *q++ = 0xa2; /* lead-out */
- *q++ = 0; /* min */
- *q++ = 0; /* sec */
- *q++ = 0; /* frame */
- if (fMSF)
- {
- *q++ = 0; /* reserved */
- ataLBA2MSF(q, s->cTotalSectors);
- q += 3;
- }
- else
- {
- ataH2BE_U32(q, s->cTotalSectors);
- q += 4;
- }
-
- *q++ = 1; /* session number */
- *q++ = 0x14; /* ADR, control */
- *q++ = 0; /* track number */
- *q++ = 1; /* point */
- *q++ = 0; /* min */
- *q++ = 0; /* sec */
- *q++ = 0; /* frame */
- if (fMSF)
- {
- *q++ = 0; /* reserved */
- ataLBA2MSF(q, 0);
- q += 3;
- }
- else
- {
- /* sector 0 */
- ataH2BE_U32(q, 0);
- q += 4;
- }
-
- cbSize = q - pbBuf;
- ataH2BE_U16(pbBuf, cbSize - 2);
- if (cbSize < s->cbTotalTransfer)
- s->cbTotalTransfer = cbSize;
- s->iSourceSink = ATAFN_SS_NULL;
- atapiCmdOK(s);
- return false;
-}
-
-
-static void atapiParseCmdVirtualATAPI(AHCIATADevState *s)
-{
- const uint8_t *pbPacket;
- uint8_t *pbBuf;
- uint32_t cbMax;
-
- pbPacket = s->aATAPICmd;
- pbBuf = s->CTXALLSUFF(pbIOBuffer);
- switch (pbPacket[0])
- {
- case SCSI_TEST_UNIT_READY:
- if (s->cNotifiedMediaChange > 0)
- {
- if (s->cNotifiedMediaChange-- > 2)
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- else
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- }
- else if (s->pDrvMount->pfnIsMounted(s->pDrvMount))
- atapiCmdOK(s);
- else
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- case SCSI_GET_EVENT_STATUS_NOTIFICATION:
- cbMax = ataBE2H_U16(pbPacket + 7);
- ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION, true);
- break;
- case SCSI_MODE_SENSE_10:
- {
- uint8_t uPageControl, uPageCode;
- cbMax = ataBE2H_U16(pbPacket + 7);
- uPageControl = pbPacket[2] >> 6;
- uPageCode = pbPacket[2] & 0x3f;
- switch (uPageControl)
- {
- case SCSI_PAGECONTROL_CURRENT:
- switch (uPageCode)
- {
- case SCSI_MODEPAGE_ERROR_RECOVERY:
- ataStartTransfer(s, RT_MIN(cbMax, 16), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY, true);
- break;
- case SCSI_MODEPAGE_CD_STATUS:
- ataStartTransfer(s, RT_MIN(cbMax, 40), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS, true);
- break;
- default:
- goto error_cmd;
- }
- break;
- case SCSI_PAGECONTROL_CHANGEABLE:
- goto error_cmd;
- case SCSI_PAGECONTROL_DEFAULT:
- goto error_cmd;
- default:
- case SCSI_PAGECONTROL_SAVED:
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
- break;
- }
- }
- break;
- case SCSI_REQUEST_SENSE:
- cbMax = pbPacket[4];
- ataStartTransfer(s, RT_MIN(cbMax, 18), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_REQUEST_SENSE, true);
- break;
- case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
- if (s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- if (pbPacket[4] & 1)
- s->pDrvMount->pfnLock(s->pDrvMount);
- else
- s->pDrvMount->pfnUnlock(s->pDrvMount);
- atapiCmdOK(s);
- }
- else
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- case SCSI_READ_10:
- case SCSI_READ_12:
- {
- uint32_t cSectors, iATAPILBA;
-
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- if (pbPacket[0] == SCSI_READ_10)
- cSectors = ataBE2H_U16(pbPacket + 7);
- else
- cSectors = ataBE2H_U32(pbPacket + 6);
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- if (cSectors == 0)
- {
- atapiCmdOK(s);
- break;
- }
- if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors)
- {
- /* Rate limited logging, one log line per second. For
- * guests that insist on reading from places outside the
- * valid area this often generates too many release log
- * entries otherwise. */
- static uint64_t uLastLogTS = 0;
- if (RTTimeMilliTS() >= uLastLogTS + 1000)
- {
- LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors));
- uLastLogTS = RTTimeMilliTS();
- }
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
- break;
- }
- atapiReadSectors(s, iATAPILBA, cSectors, 2048);
- }
- break;
- case SCSI_READ_CD:
- {
- uint32_t cSectors, iATAPILBA;
-
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8];
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- if (cSectors == 0)
- {
- atapiCmdOK(s);
- break;
- }
- if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors)
- {
- /* Rate limited logging, one log line per second. For
- * guests that insist on reading from places outside the
- * valid area this often generates too many release log
- * entries otherwise. */
- static uint64_t uLastLogTS = 0;
- if (RTTimeMilliTS() >= uLastLogTS + 1000)
- {
- LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors));
- uLastLogTS = RTTimeMilliTS();
- }
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
- break;
- }
- switch (pbPacket[9] & 0xf8)
- {
- case 0x00:
- /* nothing */
- atapiCmdOK(s);
- break;
- case 0x10:
- /* normal read */
- atapiReadSectors(s, iATAPILBA, cSectors, 2048);
- break;
- case 0xf8:
- /* read all data */
- atapiReadSectors(s, iATAPILBA, cSectors, 2352);
- break;
- default:
- LogRel(("AHCI ATA: LUN#%d: CD-ROM sector format not supported\n", s->iLUN));
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- break;
- }
- }
- break;
- case SCSI_SEEK_10:
- {
- uint32_t iATAPILBA;
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- if (iATAPILBA > s->cTotalSectors)
- {
- /* Rate limited logging, one log line per second. For
- * guests that insist on seeking to places outside the
- * valid area this often generates too many release log
- * entries otherwise. */
- static uint64_t uLastLogTS = 0;
- if (RTTimeMilliTS() >= uLastLogTS + 1000)
- {
- LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", s->iLUN, (uint64_t)iATAPILBA));
- uLastLogTS = RTTimeMilliTS();
- }
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
- break;
- }
- atapiCmdOK(s);
- ataSetStatus(s, ATA_STAT_SEEK); /* Linux expects this. */
- }
- break;
- case SCSI_START_STOP_UNIT:
- {
- int rc = VINF_SUCCESS;
- switch (pbPacket[4] & 3)
- {
- case 0: /* 00 - Stop motor */
- case 1: /* 01 - Start motor */
- break;
- case 2: /* 10 - Eject media */
- {
- /* This must be done from EMT. */
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s);
-
- PDMCritSectLeave(&pCtl->lock);
- rc = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
- (PFNRT)s->pDrvMount->pfnUnmount, 3, s->pDrvMount,
- false /*=fForce*/, true /*=fEeject*/);
- Assert(RT_SUCCESS(rc) || (rc == VERR_PDM_MEDIA_LOCKED) || (rc = VERR_PDM_MEDIA_NOT_MOUNTED));
- if (RT_SUCCESS(rc) && pCtl->pMediaNotify)
- {
- rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
- (PFNRT)pCtl->pMediaNotify->pfnEjected, 2,
- pCtl->pMediaNotify, s->iLUN);
- AssertRC(rc);
- }
- {
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- }
- break;
- }
- case 3: /* 11 - Load media */
- /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */
- break;
- }
- if (RT_SUCCESS(rc))
- atapiCmdOK(s);
- else
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED);
- }
- break;
- case SCSI_MECHANISM_STATUS:
- {
- cbMax = ataBE2H_U16(pbPacket + 8);
- ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MECHANISM_STATUS, true);
- }
- break;
- case SCSI_READ_TOC_PMA_ATIP:
- {
- uint8_t format;
-
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- cbMax = ataBE2H_U16(pbPacket + 7);
- /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits),
- * but Linux kernel uses offset 9 (topmost 2 bits). Hope that
- * the other field is clear... */
- format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6);
- switch (format)
- {
- case 0:
- ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_NORMAL, true);
- break;
- case 1:
- ataStartTransfer(s, RT_MIN(cbMax, 12), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_MULTI, true);
- break;
- case 2:
- ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_RAW, true);
- break;
- default:
- error_cmd:
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- break;
- }
- }
- break;
- case SCSI_READ_CAPACITY:
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- ataStartTransfer(s, 8, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_CAPACITY, true);
- break;
- case SCSI_READ_DISC_INFORMATION:
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- cbMax = ataBE2H_U16(pbPacket + 7);
- ataStartTransfer(s, RT_MIN(cbMax, 34), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_DISC_INFORMATION, true);
- break;
- case SCSI_READ_TRACK_INFORMATION:
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- cbMax = ataBE2H_U16(pbPacket + 7);
- ataStartTransfer(s, RT_MIN(cbMax, 36), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TRACK_INFORMATION, true);
- break;
- case SCSI_GET_CONFIGURATION:
- /* No media change stuff here, it can confuse Linux guests. */
- cbMax = ataBE2H_U16(pbPacket + 7);
- ataStartTransfer(s, RT_MIN(cbMax, 80), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_GET_CONFIGURATION, true);
- break;
- case SCSI_INQUIRY:
- cbMax = ataBE2H_U16(pbPacket + 3);
- ataStartTransfer(s, RT_MIN(cbMax, 36), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_INQUIRY, true);
- break;
- default:
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
- break;
- }
-}
-
-
-/*
- * Parse ATAPI commands, passing them directly to the CD/DVD drive.
- */
-static void atapiParseCmdPassthrough(AHCIATADevState *s)
-{
- const uint8_t *pbPacket;
- uint8_t *pbBuf;
- uint32_t cSectors, iATAPILBA;
- uint32_t cbTransfer = 0;
- PDMBLOCKTXDIR uTxDir = PDMBLOCKTXDIR_NONE;
-
- pbPacket = s->aATAPICmd;
- pbBuf = s->CTXALLSUFF(pbIOBuffer);
- switch (pbPacket[0])
- {
- case SCSI_BLANK:
- goto sendcmd;
- case SCSI_CLOSE_TRACK_SESSION:
- goto sendcmd;
- case SCSI_ERASE_10:
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- Log2(("ATAPI PT: lba %d\n", iATAPILBA));
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_FORMAT_UNIT:
- cbTransfer = s->uATARegLCyl | (s->uATARegHCyl << 8); /* use ATAPI transfer length */
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_GET_CONFIGURATION:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_GET_EVENT_STATUS_NOTIFICATION:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- if (ASMAtomicReadU32(&s->MediaEventStatus) != ATA_EVENT_STATUS_UNCHANGED)
- {
- ataStartTransfer(s, RT_MIN(cbTransfer, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION, true);
- break;
- }
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_GET_PERFORMANCE:
- cbTransfer = s->uATARegLCyl | (s->uATARegHCyl << 8); /* use ATAPI transfer length */
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_INQUIRY:
- cbTransfer = ataBE2H_U16(pbPacket + 3);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_LOAD_UNLOAD_MEDIUM:
- goto sendcmd;
- case SCSI_MECHANISM_STATUS:
- cbTransfer = ataBE2H_U16(pbPacket + 8);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_MODE_SELECT_10:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_MODE_SENSE_10:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_PAUSE_RESUME:
- goto sendcmd;
- case SCSI_PLAY_AUDIO_10:
- goto sendcmd;
- case SCSI_PLAY_AUDIO_12:
- goto sendcmd;
- case SCSI_PLAY_AUDIO_MSF:
- goto sendcmd;
- case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
- /** @todo do not forget to unlock when a VM is shut down */
- goto sendcmd;
- case SCSI_READ_10:
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- cSectors = ataBE2H_U16(pbPacket + 7);
- Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
- s->cbATAPISector = 2048; /**< @todo this size is not always correct */
- cbTransfer = cSectors * s->cbATAPISector;
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_12:
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- cSectors = ataBE2H_U32(pbPacket + 6);
- Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
- s->cbATAPISector = 2048; /**< @todo this size is not always correct */
- cbTransfer = cSectors * s->cbATAPISector;
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_BUFFER:
- cbTransfer = ataBE2H_U24(pbPacket + 6);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_BUFFER_CAPACITY:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_CAPACITY:
- cbTransfer = 8;
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_CD:
- s->cbATAPISector = 2048; /**< @todo this size is not always correct */
- cbTransfer = ataBE2H_U24(pbPacket + 6) / s->cbATAPISector * s->cbATAPISector;
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_CD_MSF:
- cSectors = ataMSF2LBA(pbPacket + 6) - ataMSF2LBA(pbPacket + 3);
- if (cSectors > 32)
- cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */
- s->cbATAPISector = 2048; /**< @todo this size is not always correct */
- cbTransfer = cSectors * s->cbATAPISector;
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_DISC_INFORMATION:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_DVD_STRUCTURE:
- cbTransfer = ataBE2H_U16(pbPacket + 8);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_FORMAT_CAPACITIES:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_SUBCHANNEL:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_TOC_PMA_ATIP:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_READ_TRACK_INFORMATION:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_REPAIR_TRACK:
- goto sendcmd;
- case SCSI_REPORT_KEY:
- cbTransfer = ataBE2H_U16(pbPacket + 8);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_REQUEST_SENSE:
- cbTransfer = pbPacket[4];
- if ((s->abATAPISense[2] & 0x0f) != SCSI_SENSE_NONE)
- {
- ataStartTransfer(s, RT_MIN(cbTransfer, 18), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_REQUEST_SENSE, true);
- break;
- }
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_RESERVE_TRACK:
- goto sendcmd;
- case SCSI_SCAN:
- goto sendcmd;
- case SCSI_SEEK_10:
- goto sendcmd;
- case SCSI_SEND_CUE_SHEET:
- cbTransfer = ataBE2H_U24(pbPacket + 6);
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_SEND_DVD_STRUCTURE:
- cbTransfer = ataBE2H_U16(pbPacket + 8);
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_SEND_EVENT:
- cbTransfer = ataBE2H_U16(pbPacket + 8);
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_SEND_KEY:
- cbTransfer = ataBE2H_U16(pbPacket + 8);
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_SEND_OPC_INFORMATION:
- cbTransfer = ataBE2H_U16(pbPacket + 7);
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_SET_CD_SPEED:
- goto sendcmd;
- case SCSI_SET_READ_AHEAD:
- goto sendcmd;
- case SCSI_SET_STREAMING:
- cbTransfer = ataBE2H_U16(pbPacket + 9);
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_START_STOP_UNIT:
- goto sendcmd;
- case SCSI_STOP_PLAY_SCAN:
- goto sendcmd;
- case SCSI_SYNCHRONIZE_CACHE:
- goto sendcmd;
- case SCSI_TEST_UNIT_READY:
- goto sendcmd;
- case SCSI_VERIFY_10:
- goto sendcmd;
- case SCSI_WRITE_10:
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- cSectors = ataBE2H_U16(pbPacket + 7);
- Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
-#if 0
- /* The sector size is determined by the async I/O thread. */
- s->cbATAPISector = 0;
- /* Preliminary, will be corrected once the sector size is known. */
- cbTransfer = cSectors;
-#else
- s->cbATAPISector = 2048; /**< @todo this size is not always correct */
- cbTransfer = cSectors * s->cbATAPISector;
-#endif
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_WRITE_12:
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- cSectors = ataBE2H_U32(pbPacket + 6);
- Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
-#if 0
- /* The sector size is determined by the async I/O thread. */
- s->cbATAPISector = 0;
- /* Preliminary, will be corrected once the sector size is known. */
- cbTransfer = cSectors;
-#else
- s->cbATAPISector = 2048; /**< @todo this size is not always correct */
- cbTransfer = cSectors * s->cbATAPISector;
-#endif
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_WRITE_AND_VERIFY_10:
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- cSectors = ataBE2H_U16(pbPacket + 7);
- Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
- /* The sector size is determined by the async I/O thread. */
- s->cbATAPISector = 0;
- /* Preliminary, will be corrected once the sector size is known. */
- cbTransfer = cSectors;
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- case SCSI_WRITE_BUFFER:
- switch (pbPacket[1] & 0x1f)
- {
- case 0x04: /* download microcode */
- case 0x05: /* download microcode and save */
- case 0x06: /* download microcode with offsets */
- case 0x07: /* download microcode with offsets and save */
- case 0x0e: /* download microcode with offsets and defer activation */
- case 0x0f: /* activate deferred microcode */
- LogRel(("AHCI ATA: LUN#%d: CD-ROM passthrough command attempted to update firmware, blocked\n", s->iLUN));
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- break;
- default:
- cbTransfer = ataBE2H_U16(pbPacket + 6);
- uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
- goto sendcmd;
- }
- break;
- case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */
- cbTransfer = ataBE2H_U32(pbPacket + 6);
- uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
- goto sendcmd;
- case SCSI_REZERO_UNIT:
- /* Obsolete command used by cdrecord. What else would one expect?
- * This command is not sent to the drive, it is handled internally,
- * as the Linux kernel doesn't like it (message "scsi: unknown
- * opcode 0x01" in syslog) and replies with a sense code of 0,
- * which sends cdrecord to an endless loop. */
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
- break;
- default:
- LogRel(("AHCI ATA: LUN#%d: passthrough unimplemented for command %#x\n", s->iLUN, pbPacket[0]));
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
- break;
- sendcmd:
- /* Send a command to the drive, passing data in/out as required. */
- Log2(("ATAPI PT: max size %d\n", cbTransfer));
- Assert(cbTransfer <= s->cbIOBuffer);
- if (cbTransfer == 0)
- uTxDir = PDMBLOCKTXDIR_NONE;
- ataStartTransfer(s, cbTransfer, uTxDir, ATAFN_BT_ATAPI_PASSTHROUGH_CMD, ATAFN_SS_ATAPI_PASSTHROUGH, true);
- }
-}
-
-
-static void atapiParseCmd(AHCIATADevState *s)
-{
- const uint8_t *pbPacket;
-
- pbPacket = s->aATAPICmd;
-#ifdef DEBUG
- Log(("%s: LUN#%d DMA=%d CMD=%#04x \"%s\"\n", __FUNCTION__, s->iLUN, s->fDMA, pbPacket[0], SCSICmdText(pbPacket[0])));
-#else /* !DEBUG */
- Log(("%s: LUN#%d DMA=%d CMD=%#04x\n", __FUNCTION__, s->iLUN, s->fDMA, pbPacket[0]));
-#endif /* !DEBUG */
- Log2(("%s: limit=%#x packet: %.*Rhxs\n", __FUNCTION__, s->uATARegLCyl | (s->uATARegHCyl << 8), ATAPI_PACKET_SIZE, pbPacket));
-
- if (s->fATAPIPassthrough)
- atapiParseCmdPassthrough(s);
- else
- atapiParseCmdVirtualATAPI(s);
-}
-
-
-static bool ataPacketSS(AHCIATADevState *s)
-{
- s->fDMA = !!(s->uATARegFeature & 1);
- memcpy(s->aATAPICmd, s->CTXALLSUFF(pbIOBuffer), ATAPI_PACKET_SIZE);
- s->uTxDir = PDMBLOCKTXDIR_NONE;
- s->cbTotalTransfer = 0;
- s->cbElementaryTransfer = 0;
- atapiParseCmd(s);
- return false;
-}
-
-#if 0
-
-/**
- * SCSI_GET_EVENT_STATUS_NOTIFICATION should return "medium removed" event
- * from now on, regardless if there was a medium inserted or not.
- */
-static void ataMediumRemoved(AHCIATADevState *s)
-{
- ASMAtomicWriteU32(&s->MediaEventStatus, ATA_EVENT_STATUS_MEDIA_REMOVED);
-}
-
-
-/**
- * SCSI_GET_EVENT_STATUS_NOTIFICATION should return "medium inserted". If
- * there was already a medium inserted, don't forget to send the "medium
- * removed" event first.
- */
-static void ataMediumInserted(AHCIATADevState *s)
-{
- uint32_t OldStatus, NewStatus;
- do
- {
- OldStatus = ASMAtomicReadU32(&s->MediaEventStatus);
- switch (OldStatus)
- {
- case ATA_EVENT_STATUS_MEDIA_CHANGED:
- case ATA_EVENT_STATUS_MEDIA_REMOVED:
- /* no change, we will send "medium removed" + "medium inserted" */
- NewStatus = ATA_EVENT_STATUS_MEDIA_CHANGED;
- break;
- default:
- NewStatus = ATA_EVENT_STATUS_MEDIA_NEW;
- break;
- }
- } while (!ASMAtomicCmpXchgU32(&s->MediaEventStatus, NewStatus, OldStatus));
-}
-
-
-/**
- * Called when a media is mounted.
- *
- * @param pInterface Pointer to the interface structure containing the called function pointer.
- */
-static DECLCALLBACK(void) ataMountNotify(PPDMIMOUNTNOTIFY pInterface)
-{
- AHCIATADevState *pIf = PDMIMOUNTNOTIFY_2_ATASTATE(pInterface);
- Log(("%s: changing LUN#%d\n", __FUNCTION__, pIf->iLUN));
-
- /* Ignore the call if we're called while being attached. */
- if (!pIf->pDrvBlock)
- return;
-
- LogRel(("AHCI ATA: LUN#%d: CD/DVD, total number of sectors %Ld, passthrough unchanged\n", pIf->iLUN, pIf->cTotalSectors));
-
- if (pIf->fATAPI)
- pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 2048;
- else
- pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 512;
-
- /* Report media changed in TEST UNIT and other (probably incorrect) places. */
- if (pIf->cNotifiedMediaChange < 2)
- pIf->cNotifiedMediaChange = 2;
- ataMediumInserted(pIf);
-}
-
-/**
- * Called when a media is unmounted
- * @param pInterface Pointer to the interface structure containing the called function pointer.
- */
-static DECLCALLBACK(void) ataUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
-{
- AHCIATADevState *pIf = PDMIMOUNTNOTIFY_2_ATASTATE(pInterface);
- Log(("%s:\n", __FUNCTION__));
- pIf->cTotalSectors = 0;
-
- /*
- * Whatever I do, XP will not use the GET MEDIA STATUS nor the EVENT stuff.
- * However, it will respond to TEST UNIT with a 0x6 0x28 (media changed) sense code.
- * So, we'll give it 4 TEST UNIT command to catch up, two which the media is not
- * present and 2 in which it is changed.
- */
- pIf->cNotifiedMediaChange = 4;
- ataMediumRemoved(pIf);
-}
-#endif
-
-static void ataPacketBT(AHCIATADevState *s)
-{
- s->cbElementaryTransfer = s->cbTotalTransfer;
- s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_CD;
- Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector));
- ataSetStatusValue(s, ATA_STAT_READY);
-}
-
-
-static void ataResetDevice(AHCIATADevState *s)
-{
- s->cMultSectors = ATA_MAX_MULT_SECTORS;
- s->cNotifiedMediaChange = 0;
- ASMAtomicWriteU32(&s->MediaEventStatus, ATA_EVENT_STATUS_UNCHANGED);
- ataUnsetIRQ(s);
-
- s->uATARegSelect = 0x20;
- ataSetStatusValue(s, ATA_STAT_READY);
- ataSetSignature(s);
- s->cbTotalTransfer = 0;
- s->cbElementaryTransfer = 0;
- s->iIOBufferPIODataStart = 0;
- s->iIOBufferPIODataEnd = 0;
- s->iBeginTransfer = ATAFN_BT_NULL;
- s->iSourceSink = ATAFN_SS_NULL;
- s->fATAPITransfer = false;
- s->uATATransferMode = ATA_MODE_UDMA | 2; /* AHCI supports only up to UDMA2 */
-
- s->uATARegFeature = 0;
-}
-
-
-static bool ataExecuteDeviceDiagnosticSS(AHCIATADevState *s)
-{
- ataSetSignature(s);
- if (s->fATAPI)
- ataSetStatusValue(s, 0); /* NOTE: READY is _not_ set */
- else
- ataSetStatusValue(s, ATA_STAT_READY);
- s->uATARegError = 0x01;
- return false;
-}
-
-
-static int ataTrimSectors(AHCIATADevState *s, uint64_t u64Sector, uint32_t cSectors,
- bool *pfRedo)
-{
- RTRANGE TrimRange;
- PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- int rc;
-
- PDMCritSectLeave(&pCtl->lock);
-
- TrimRange.offStart = u64Sector * 512;
- TrimRange.cbRange = cSectors * 512;
-
- s->pLed->Asserted.s.fWriting = s->pLed->Actual.s.fWriting = 1;
- rc = s->pDrvBlock->pfnDiscard(s->pDrvBlock, &TrimRange, 1);
- s->pLed->Actual.s.fWriting = 0;
-
- if (RT_SUCCESS(rc))
- *pfRedo = false;
- else
- *pfRedo = ataIsRedoSetWarning(s, rc);
-
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- return rc;
-}
-
-
-static bool ataTrimSS(AHCIATADevState *s)
-{
- int rc = VERR_GENERAL_FAILURE;
- uint32_t cRangesMax;
- uint64_t *pu64Range = (uint64_t *)s->CTX_SUFF(pbIOBuffer);
- bool fRedo = false;
-
- cRangesMax = s->cbElementaryTransfer / sizeof(uint64_t);
- Assert(cRangesMax);
-
- while (cRangesMax-- > 0)
- {
- if (ATA_RANGE_LENGTH_GET(*pu64Range) == 0)
- break;
-
- rc = ataTrimSectors(s, *pu64Range & ATA_RANGE_LBA_MASK,
- ATA_RANGE_LENGTH_GET(*pu64Range), &fRedo);
- if (RT_FAILURE(rc))
- break;
-
- pu64Range++;
- }
-
- if (RT_SUCCESS(rc))
- {
- s->iSourceSink = ATAFN_SS_NULL;
- ataCmdOK(s, ATA_STAT_SEEK);
- }
- else
- {
- if (fRedo)
- return fRedo;
- if (s->cErrors++ < MAX_LOG_REL_ERRORS)
- LogRel(("PIIX3 ATA: LUN#%d: disk trim error (rc=%Rrc iSector=%#RX64 cSectors=%#RX32)\n",
- s->iLUN, rc, *pu64Range & ATA_RANGE_LBA_MASK, ATA_RANGE_LENGTH_GET(*pu64Range)));
-
- /*
- * Check if we got interrupted. We don't need to set status variables
- * because the request was aborted.
- */
- if (rc != VERR_INTERRUPTED)
- ataCmdError(s, ID_ERR);
- }
-
- return false;
-}
-
-
-static void ataParseCmd(AHCIATADevState *s, uint8_t cmd)
-{
-#ifdef DEBUG
- Log(("%s: LUN#%d CMD=%#04x \"%s\"\n", __FUNCTION__, s->iLUN, cmd, ATACmdText(cmd)));
-#else /* !DEBUG */
- Log(("%s: LUN#%d CMD=%#04x\n", __FUNCTION__, s->iLUN, cmd));
-#endif /* !DEBUG */
- s->fLBA48 = false;
- s->fDMA = false;
- if (cmd == ATA_IDLE_IMMEDIATE)
- {
- /* Detect Linux timeout recovery, first tries IDLE IMMEDIATE (which
- * would overwrite the failing command unfortunately), then RESET. */
- int32_t uCmdWait = -1;
- uint64_t uNow = RTTimeNanoTS();
- if (s->u64CmdTS)
- uCmdWait = (uNow - s->u64CmdTS) / 1000;
- LogRel(("AHCI ATA: LUN#%d: IDLE IMMEDIATE, CmdIf=%#04x (%d usec ago)\n",
- s->iLUN, s->uATARegCommand, uCmdWait));
- }
- s->uATARegCommand = cmd;
- switch (cmd)
- {
- case ATA_IDENTIFY_DEVICE:
- if (s->pDrvBlock && !s->fATAPI)
- ataStartTransfer(s, 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_NULL, ATAFN_SS_IDENTIFY, false);
- else
- {
- if (s->fATAPI)
- ataSetSignature(s);
- ataCmdError(s, ABRT_ERR);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- }
- break;
- case ATA_INITIALIZE_DEVICE_PARAMETERS:
- case ATA_RECALIBRATE:
- ataCmdOK(s, ATA_STAT_SEEK);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case ATA_SET_MULTIPLE_MODE:
- if ( s->uATARegNSector != 0
- && ( s->uATARegNSector > ATA_MAX_MULT_SECTORS
- || (s->uATARegNSector & (s->uATARegNSector - 1)) != 0))
- {
- ataCmdError(s, ABRT_ERR);
- }
- else
- {
- Log2(("%s: set multi sector count to %d\n", __FUNCTION__, s->uATARegNSector));
- s->cMultSectors = s->uATARegNSector;
- ataCmdOK(s, 0);
- }
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case ATA_READ_VERIFY_SECTORS_EXT:
- s->fLBA48 = true;
- case ATA_READ_VERIFY_SECTORS:
- case ATA_READ_VERIFY_SECTORS_WITHOUT_RETRIES:
- /* do sector number check ? */
- ataCmdOK(s, 0);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case ATA_READ_SECTORS_EXT:
- s->fLBA48 = true;
- case ATA_READ_SECTORS:
- case ATA_READ_SECTORS_WITHOUT_RETRIES:
- if (!s->pDrvBlock || s->fATAPI)
- goto abort_cmd;
- s->cSectorsPerIRQ = 1;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
- break;
- case ATA_WRITE_SECTORS_EXT:
- s->fLBA48 = true;
- case ATA_WRITE_SECTORS:
- case ATA_WRITE_SECTORS_WITHOUT_RETRIES:
- if (!s->pDrvBlock || s->fATAPI)
- goto abort_cmd;
- s->cSectorsPerIRQ = 1;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
- break;
- case ATA_READ_MULTIPLE_EXT:
- s->fLBA48 = true;
- case ATA_READ_MULTIPLE:
- if (!s->pDrvBlock || !s->cMultSectors || s->fATAPI)
- goto abort_cmd;
- s->cSectorsPerIRQ = s->cMultSectors;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
- break;
- case ATA_WRITE_MULTIPLE_EXT:
- s->fLBA48 = true;
- case ATA_WRITE_MULTIPLE:
- if (!s->pDrvBlock || !s->cMultSectors || s->fATAPI)
- goto abort_cmd;
- s->cSectorsPerIRQ = s->cMultSectors;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
- break;
- case ATA_READ_DMA_EXT:
- s->fLBA48 = true;
- case ATA_READ_DMA:
- case ATA_READ_DMA_WITHOUT_RETRIES:
- if (!s->pDrvBlock || s->fATAPI)
- goto abort_cmd;
- s->cSectorsPerIRQ = ATA_MAX_MULT_SECTORS;
- s->fDMA = true;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
- break;
- case ATA_WRITE_DMA_EXT:
- s->fLBA48 = true;
- case ATA_WRITE_DMA:
- case ATA_WRITE_DMA_WITHOUT_RETRIES:
- if (!s->pDrvBlock || s->fATAPI)
- goto abort_cmd;
- s->cSectorsPerIRQ = ATA_MAX_MULT_SECTORS;
- s->fDMA = true;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
- break;
- case ATA_READ_NATIVE_MAX_ADDRESS_EXT:
- s->fLBA48 = true;
- ataSetSector(s, s->cTotalSectors - 1);
- ataCmdOK(s, 0);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case ATA_READ_NATIVE_MAX_ADDRESS:
- ataSetSector(s, RT_MIN(s->cTotalSectors, 1 << 28) - 1);
- ataCmdOK(s, 0);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case ATA_CHECK_POWER_MODE:
- s->uATARegNSector = 0xff; /* drive active or idle */
- ataCmdOK(s, 0);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case ATA_SET_FEATURES:
- Log2(("%s: feature=%#x\n", __FUNCTION__, s->uATARegFeature));
- if (!s->pDrvBlock)
- goto abort_cmd;
- switch (s->uATARegFeature)
- {
- case 0x02: /* write cache enable */
- Log2(("%s: write cache enable\n", __FUNCTION__));
- ataCmdOK(s, ATA_STAT_SEEK);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case 0xaa: /* read look-ahead enable */
- Log2(("%s: read look-ahead enable\n", __FUNCTION__));
- ataCmdOK(s, ATA_STAT_SEEK);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case 0x55: /* read look-ahead disable */
- Log2(("%s: read look-ahead disable\n", __FUNCTION__));
- ataCmdOK(s, ATA_STAT_SEEK);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case 0xcc: /* reverting to power-on defaults enable */
- Log2(("%s: revert to power-on defaults enable\n", __FUNCTION__));
- ataCmdOK(s, ATA_STAT_SEEK);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case 0x66: /* reverting to power-on defaults disable */
- Log2(("%s: revert to power-on defaults disable\n", __FUNCTION__));
- ataCmdOK(s, ATA_STAT_SEEK);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case 0x82: /* write cache disable */
- Log2(("%s: write cache disable\n", __FUNCTION__));
- /* As per the ATA/ATAPI-6 specs, a write cache disable
- * command MUST flush the write buffers to disc. */
- ataStartTransfer(s, 0, PDMBLOCKTXDIR_NONE, ATAFN_BT_NULL, ATAFN_SS_FLUSH, false);
- break;
- case 0x03: { /* set transfer mode */
- Log2(("%s: transfer mode %#04x\n", __FUNCTION__, s->uATARegNSector));
- switch (s->uATARegNSector & 0xf8)
- {
- case 0x00: /* PIO default */
- case 0x08: /* PIO mode */
- break;
- case ATA_MODE_MDMA: /* MDMA mode */
- s->uATATransferMode = (s->uATARegNSector & 0xf8) | RT_MIN(s->uATARegNSector & 0x07, ATA_MDMA_MODE_MAX);
- break;
- case ATA_MODE_UDMA: /* UDMA mode */
- s->uATATransferMode = (s->uATARegNSector & 0xf8) | RT_MIN(s->uATARegNSector & 0x07, ATA_UDMA_MODE_MAX);
- break;
- default:
- goto abort_cmd;
- }
- ataCmdOK(s, ATA_STAT_SEEK);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- }
- default:
- goto abort_cmd;
- }
- /*
- * OS/2 workarond:
- * The OS/2 IDE driver from MCP2 appears to rely on the feature register being
- * reset here. According to the specification, this is a driver bug as the register
- * contents are undefined after the call. This means we can just as well reset it.
- */
- s->uATARegFeature = 0;
- break;
- case ATA_FLUSH_CACHE_EXT:
- case ATA_FLUSH_CACHE:
- if (!s->pDrvBlock || s->fATAPI)
- goto abort_cmd;
- ataStartTransfer(s, 0, PDMBLOCKTXDIR_NONE, ATAFN_BT_NULL, ATAFN_SS_FLUSH, false);
- break;
- case ATA_STANDBY_IMMEDIATE:
- ataCmdOK(s, 0);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- case ATA_IDLE_IMMEDIATE:
- LogRel(("AHCI ATA: LUN#%d: aborting current command\n", s->iLUN));
- ataAbortCurrentCommand(s, false);
- break;
- /* ATAPI commands */
- case ATA_IDENTIFY_PACKET_DEVICE:
- if (s->fATAPI)
- ataStartTransfer(s, 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_NULL, ATAFN_SS_ATAPI_IDENTIFY, false);
- else
- {
- ataCmdError(s, ABRT_ERR);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- }
- break;
- case ATA_EXECUTE_DEVICE_DIAGNOSTIC:
- ataStartTransfer(s, 0, PDMBLOCKTXDIR_NONE, ATAFN_BT_NULL, ATAFN_SS_EXECUTE_DEVICE_DIAGNOSTIC, false);
- break;
- case ATA_DEVICE_RESET:
- if (!s->fATAPI)
- goto abort_cmd;
- LogRel(("AHCI ATA: LUN#%d: performing device RESET\n", s->iLUN));
- ataAbortCurrentCommand(s, true);
- break;
- case ATA_PACKET:
- if (!s->fATAPI)
- goto abort_cmd;
- /* overlapping commands not supported */
- if (s->uATARegFeature & 0x02)
- goto abort_cmd;
- ataStartTransfer(s, ATAPI_PACKET_SIZE, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_PACKET, ATAFN_SS_PACKET, false);
- break;
- case ATA_DATA_SET_MANAGEMENT:
- if (!s->pDrvBlock || !s->pDrvBlock->pfnDiscard)
- goto abort_cmd;
- if ( !(s->uATARegFeature & UINT8_C(0x01))
- || (s->uATARegFeature & ~UINT8_C(0x01)))
- goto abort_cmd;
- s->fDMA = true;
- ataStartTransfer(s, (s->uATARegNSectorHOB << 8 | s->uATARegNSector) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_NULL, ATAFN_SS_TRIM, false);
- break;
- default:
- abort_cmd:
- ataCmdError(s, ABRT_ERR);
- ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
- break;
- }
-}
-
-
-/**
- * Waits for a particular async I/O thread to complete whatever it
- * is doing at the moment.
- *
- * @returns true on success.
- * @returns false when the thread is still processing.
- * @param pThis Pointer to the controller data.
- * @param cMillies How long to wait (total).
- */
-static bool ataWaitForAsyncIOIsIdle(PAHCIATACONTROLLER pCtl, RTMSINTERVAL cMillies)
-{
- uint64_t u64Start;
- bool fRc;
-
- /* Hope for the simple way out... */
- if (ataAsyncIOIsIdle(pCtl, false /*fStrict*/))
- return true;
-
- /*
- * Have to wait. Do the setup while owning the mutex to avoid races.
- */
- RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
-
- RTThreadUserReset(pCtl->AsyncIOThread);
- ASMAtomicWriteBool(&pCtl->fSignalIdle, true);
-
- RTSemMutexRelease(pCtl->AsyncIORequestMutex);
-
- u64Start = RTTimeMilliTS();
- for (;;)
- {
- fRc = ataAsyncIOIsIdle(pCtl, false /*fStrict*/);
- if (fRc)
- break;
-
- if (RTTimeMilliTS() - u64Start >= cMillies)
- break;
-
- int rc = RTThreadUserWait(pCtl->AsyncIOThread, 100 /*ms*/);
- AssertMsg( ( RT_SUCCESS(rc)
- && ataAsyncIOIsIdle(pCtl, false /*fStrict*/))
- || rc == VERR_TIMEOUT,
- ("rc=%Rrc irq=%u\n", rc, pCtl->irq));
- }
-
- ASMAtomicWriteBool(&pCtl->fSignalIdle, false);
- return fRc;
-}
-
-#endif /* IN_RING3 */
-
-static int ataIOPortWriteU8(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val)
-{
- Log2(("%s: write addr=%#x val=%#04x\n", __FUNCTION__, addr, val));
- addr &= 7;
- switch (addr)
- {
- case 0:
- break;
- case 1: /* feature register */
- /* NOTE: data is written to the two drives */
- pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
- pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
- pCtl->aIfs[0].uATARegFeatureHOB = pCtl->aIfs[0].uATARegFeature;
- pCtl->aIfs[1].uATARegFeatureHOB = pCtl->aIfs[1].uATARegFeature;
- pCtl->aIfs[0].uATARegFeature = val;
- pCtl->aIfs[1].uATARegFeature = val;
- break;
- case 2: /* sector count */
- pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
- pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
- pCtl->aIfs[0].uATARegNSectorHOB = pCtl->aIfs[0].uATARegNSector;
- pCtl->aIfs[1].uATARegNSectorHOB = pCtl->aIfs[1].uATARegNSector;
- pCtl->aIfs[0].uATARegNSector = val;
- pCtl->aIfs[1].uATARegNSector = val;
- break;
- case 3: /* sector number */
- pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
- pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
- pCtl->aIfs[0].uATARegSectorHOB = pCtl->aIfs[0].uATARegSector;
- pCtl->aIfs[1].uATARegSectorHOB = pCtl->aIfs[1].uATARegSector;
- pCtl->aIfs[0].uATARegSector = val;
- pCtl->aIfs[1].uATARegSector = val;
- break;
- case 4: /* cylinder low */
- pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
- pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
- pCtl->aIfs[0].uATARegLCylHOB = pCtl->aIfs[0].uATARegLCyl;
- pCtl->aIfs[1].uATARegLCylHOB = pCtl->aIfs[1].uATARegLCyl;
- pCtl->aIfs[0].uATARegLCyl = val;
- pCtl->aIfs[1].uATARegLCyl = val;
- break;
- case 5: /* cylinder high */
- pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
- pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
- pCtl->aIfs[0].uATARegHCylHOB = pCtl->aIfs[0].uATARegHCyl;
- pCtl->aIfs[1].uATARegHCylHOB = pCtl->aIfs[1].uATARegHCyl;
- pCtl->aIfs[0].uATARegHCyl = val;
- pCtl->aIfs[1].uATARegHCyl = val;
- break;
- case 6: /* drive/head */
- pCtl->aIfs[0].uATARegSelect = (val & ~0x10) | 0xa0;
- pCtl->aIfs[1].uATARegSelect = (val | 0x10) | 0xa0;
- if (((val >> 4) & 1) != pCtl->iSelectedIf)
- {
- PPDMDEVINS pDevIns = CONTROLLER_2_DEVINS(pCtl);
-
- /* select another drive */
- pCtl->iSelectedIf = (val >> 4) & 1;
- /* The IRQ line is multiplexed between the two drives, so
- * update the state when switching to another drive. Only need
- * to update interrupt line if it is enabled and there is a
- * state change. */
- if ( !(pCtl->aIfs[pCtl->iSelectedIf].uATARegDevCtl & ATA_DEVCTL_DISABLE_IRQ)
- && ( pCtl->aIfs[pCtl->iSelectedIf].fIrqPending
- != pCtl->aIfs[pCtl->iSelectedIf ^ 1].fIrqPending))
- {
- if (pCtl->aIfs[pCtl->iSelectedIf].fIrqPending)
- {
- Log2(("%s: LUN#%d asserting IRQ (drive select change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN));
- /* The BMDMA unit unconditionally sets BM_STATUS_INT if
- * the interrupt line is asserted. It monitors the line
- * for a rising edge. */
- pCtl->BmDma.u8Status |= BM_STATUS_INT;
- if (pCtl->irq == 16)
- PDMDevHlpPCISetIrq(pDevIns, 0, 1);
- else
- PDMDevHlpISASetIrq(pDevIns, pCtl->irq, 1);
- }
- else
- {
- Log2(("%s: LUN#%d deasserting IRQ (drive select change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN));
- if (pCtl->irq == 16)
- PDMDevHlpPCISetIrq(pDevIns, 0, 0);
- else
- PDMDevHlpISASetIrq(pDevIns, pCtl->irq, 0);
- }
- }
- }
- break;
- default:
- case 7: /* command */
- /* ignore commands to non existant slave */
- if (pCtl->iSelectedIf && !pCtl->aIfs[pCtl->iSelectedIf].pDrvBlock)
- break;
-#ifndef IN_RING3
- /* Don't do anything complicated in GC */
- return VINF_IOM_R3_IOPORT_WRITE;
-#else /* IN_RING3 */
- ataParseCmd(&pCtl->aIfs[pCtl->iSelectedIf], val);
-#endif /* !IN_RING3 */
- }
- return VINF_SUCCESS;
-}
-
-
-static int ataIOPortReadU8(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t *pu32)
-{
- AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
- uint32_t val;
- bool fHOB;
-
- fHOB = !!(s->uATARegDevCtl & (1 << 7));
- switch (addr & 7)
- {
- case 0: /* data register */
- val = 0xff;
- break;
- case 1: /* error register */
- /* The ATA specification is very terse when it comes to specifying
- * the precise effects of reading back the error/feature register.
- * The error register (read-only) shares the register number with
- * the feature register (write-only), so it seems that it's not
- * necessary to support the usual HOB readback here. */
- if (!s->pDrvBlock)
- val = 0;
- else
- val = s->uATARegError;
- break;
- case 2: /* sector count */
- if (!s->pDrvBlock)
- val = 0;
- else if (fHOB)
- val = s->uATARegNSectorHOB;
- else
- val = s->uATARegNSector;
- break;
- case 3: /* sector number */
- if (!s->pDrvBlock)
- val = 0;
- else if (fHOB)
- val = s->uATARegSectorHOB;
- else
- val = s->uATARegSector;
- break;
- case 4: /* cylinder low */
- if (!s->pDrvBlock)
- val = 0;
- else if (fHOB)
- val = s->uATARegLCylHOB;
- else
- val = s->uATARegLCyl;
- break;
- case 5: /* cylinder high */
- if (!s->pDrvBlock)
- val = 0;
- else if (fHOB)
- val = s->uATARegHCylHOB;
- else
- val = s->uATARegHCyl;
- break;
- case 6: /* drive/head */
- /* This register must always work as long as there is at least
- * one drive attached to the controller. It is common between
- * both drives anyway (completely identical content). */
- if (!pCtl->aIfs[0].pDrvBlock && !pCtl->aIfs[1].pDrvBlock)
- val = 0;
- else
- val = s->uATARegSelect;
- break;
- default:
- case 7: /* primary status */
- {
- /* Counter for number of busy status seen in GC in a row. */
- static unsigned cBusy = 0;
-
- if (!s->pDrvBlock)
- val = 0;
- else
- val = s->uATARegStatus;
-
- /* Give the async I/O thread an opportunity to make progress,
- * don't let it starve by guests polling frequently. EMT has a
- * lower priority than the async I/O thread, but sometimes the
- * host OS doesn't care. With some guests we are only allowed to
- * be busy for about 5 milliseconds in some situations. Note that
- * this is no guarantee for any other VBox thread getting
- * scheduled, so this just lowers the CPU load a bit when drives
- * are busy. It cannot help with timing problems. */
- if (val & ATA_STAT_BUSY)
- {
-#ifdef IN_RING3
- cBusy = 0;
- PDMCritSectLeave(&pCtl->lock);
-
- RTThreadYield();
-
- {
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- }
-
- val = s->uATARegStatus;
-#else /* !IN_RING3 */
- /* Cannot yield CPU in guest context. And switching to host
- * context for each and every busy status is too costly,
- * especially on SMP systems where we don't gain much by
- * yielding the CPU to someone else. */
- if (++cBusy >= 20)
- {
- cBusy = 0;
- return VINF_IOM_R3_IOPORT_READ;
- }
-#endif /* !IN_RING3 */
- }
- else
- cBusy = 0;
- ataUnsetIRQ(s);
- break;
- }
- }
- Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val));
- *pu32 = val;
- return VINF_SUCCESS;
-}
-
-
-static uint32_t ataStatusRead(PAHCIATACONTROLLER pCtl, uint32_t addr)
-{
- AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
- uint32_t val;
-
- if ((!pCtl->aIfs[0].pDrvBlock && !pCtl->aIfs[1].pDrvBlock) ||
- (pCtl->iSelectedIf == 1 && !s->pDrvBlock))
- val = 0;
- else
- val = s->uATARegStatus;
- Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val));
- return val;
-}
-
-static int ataControlWrite(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val)
-{
-#ifndef IN_RING3
- if ((val ^ pCtl->aIfs[0].uATARegDevCtl) & ATA_DEVCTL_RESET)
- return VINF_IOM_R3_IOPORT_WRITE; /* The RESET stuff is too complicated for GC. */
-#endif /* !IN_RING3 */
-
- Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val));
- /* RESET is common for both drives attached to a controller. */
- if (!(pCtl->aIfs[0].uATARegDevCtl & ATA_DEVCTL_RESET) &&
- (val & ATA_DEVCTL_RESET))
- {
-#ifdef IN_RING3
- /* Software RESET low to high */
- int32_t uCmdWait0 = -1, uCmdWait1 = -1;
- uint64_t uNow = RTTimeNanoTS();
- if (pCtl->aIfs[0].u64CmdTS)
- uCmdWait0 = (uNow - pCtl->aIfs[0].u64CmdTS) / 1000;
- if (pCtl->aIfs[1].u64CmdTS)
- uCmdWait1 = (uNow - pCtl->aIfs[1].u64CmdTS) / 1000;
- LogRel(("AHCI ATA: Ctl: RESET, DevSel=%d AIOIf=%d CmdIf0=%#04x (%d usec ago) CmdIf1=%#04x (%d usec ago)\n",
- pCtl->iSelectedIf, pCtl->iAIOIf,
- pCtl->aIfs[0].uATARegCommand, uCmdWait0,
- pCtl->aIfs[1].uATARegCommand, uCmdWait1));
- pCtl->fReset = true;
- /* Everything must be done after the reset flag is set, otherwise
- * there are unavoidable races with the currently executing request
- * (which might just finish in the mean time). */
- pCtl->fChainedTransfer = false;
- for (uint32_t i = 0; i < RT_ELEMENTS(pCtl->aIfs); i++)
- {
- ataResetDevice(&pCtl->aIfs[i]);
- /* The following cannot be done using ataSetStatusValue() since the
- * reset flag is already set, which suppresses all status changes. */
- pCtl->aIfs[i].uATARegStatus = ATA_STAT_BUSY | ATA_STAT_SEEK;
- Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, pCtl->aIfs[i].iLUN, pCtl->aIfs[i].uATARegStatus));
- pCtl->aIfs[i].uATARegError = 0x01;
- }
- ataAsyncIOClearRequests(pCtl);
- Log2(("%s: Ctl: message to async I/O thread, resetA\n", __FUNCTION__));
- if (val & ATA_DEVCTL_HOB)
- {
- val &= ~ATA_DEVCTL_HOB;
- Log2(("%s: ignored setting HOB\n", __FUNCTION__));
- }
- ataAsyncIOPutRequest(pCtl, &ataResetARequest);
-#else /* !IN_RING3 */
- AssertMsgFailed(("RESET handling is too complicated for GC\n"));
-#endif /* IN_RING3 */
- }
- else if ((pCtl->aIfs[0].uATARegDevCtl & ATA_DEVCTL_RESET) &&
- !(val & ATA_DEVCTL_RESET))
- {
-#ifdef IN_RING3
- /* Software RESET high to low */
- Log(("%s: deasserting RESET\n", __FUNCTION__));
- Log2(("%s: Ctl: message to async I/O thread, resetC\n", __FUNCTION__));
- if (val & ATA_DEVCTL_HOB)
- {
- val &= ~ATA_DEVCTL_HOB;
- Log2(("%s: ignored setting HOB\n", __FUNCTION__));
- }
- ataAsyncIOPutRequest(pCtl, &ataResetCRequest);
-#else /* !IN_RING3 */
- AssertMsgFailed(("RESET handling is too complicated for GC\n"));
-#endif /* IN_RING3 */
- }
-
- /* Change of interrupt disable flag. Update interrupt line if interrupt
- * is pending on the current interface. */
- if ((val ^ pCtl->aIfs[0].uATARegDevCtl) & ATA_DEVCTL_DISABLE_IRQ
- && pCtl->aIfs[pCtl->iSelectedIf].fIrqPending)
- {
- if (!(val & ATA_DEVCTL_DISABLE_IRQ))
- {
- Log2(("%s: LUN#%d asserting IRQ (interrupt disable change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN));
- /* The BMDMA unit unconditionally sets BM_STATUS_INT if the
- * interrupt line is asserted. It monitors the line for a rising
- * edge. */
- pCtl->BmDma.u8Status |= BM_STATUS_INT;
- if (pCtl->irq == 16)
- PDMDevHlpPCISetIrq(CONTROLLER_2_DEVINS(pCtl), 0, 1);
- else
- PDMDevHlpISASetIrq(CONTROLLER_2_DEVINS(pCtl), pCtl->irq, 1);
- }
- else
- {
- Log2(("%s: LUN#%d deasserting IRQ (interrupt disable change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN));
- if (pCtl->irq == 16)
- PDMDevHlpPCISetIrq(CONTROLLER_2_DEVINS(pCtl), 0, 0);
- else
- PDMDevHlpISASetIrq(CONTROLLER_2_DEVINS(pCtl), pCtl->irq, 0);
- }
- }
-
- if (val & ATA_DEVCTL_HOB)
- Log2(("%s: set HOB\n", __FUNCTION__));
-
- pCtl->aIfs[0].uATARegDevCtl = val;
- pCtl->aIfs[1].uATARegDevCtl = val;
-
- return VINF_SUCCESS;
-}
-
-#ifdef IN_RING3
-
-static void ataPIOTransfer(PAHCIATACONTROLLER pCtl)
-{
- AHCIATADevState *s;
-
- s = &pCtl->aIfs[pCtl->iAIOIf];
- Log3(("%s: if=%p\n", __FUNCTION__, s));
-
- if (s->cbTotalTransfer && s->iIOBufferCur > s->iIOBufferEnd)
- {
- LogRel(("AHCI ATA: LUN#%d: %s data in the middle of a PIO transfer - VERY SLOW\n", s->iLUN, s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "loading" : "storing"));
- /* Any guest OS that triggers this case has a pathetic ATA driver.
- * In a real system it would block the CPU via IORDY, here we do it
- * very similarly by not continuing with the current instruction
- * until the transfer to/from the storage medium is completed. */
- if (s->iSourceSink != ATAFN_SS_NULL)
- {
- bool fRedo;
- uint8_t status = s->uATARegStatus;
- ataSetStatusValue(s, ATA_STAT_BUSY);
- Log2(("%s: calling source/sink function\n", __FUNCTION__));
- fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
- pCtl->fRedo = fRedo;
- if (RT_UNLIKELY(fRedo))
- return;
- ataSetStatusValue(s, status);
- s->iIOBufferCur = 0;
- s->iIOBufferEnd = s->cbElementaryTransfer;
- }
- }
- if (s->cbTotalTransfer)
- {
- if (s->fATAPITransfer)
- ataPIOTransferLimitATAPI(s);
-
- if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE && s->cbElementaryTransfer > s->cbTotalTransfer)
- s->cbElementaryTransfer = s->cbTotalTransfer;
-
- Log2(("%s: %s tx_size=%d elem_tx_size=%d index=%d end=%d\n",
- __FUNCTION__, s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "T2I" : "I2T",
- s->cbTotalTransfer, s->cbElementaryTransfer,
- s->iIOBufferCur, s->iIOBufferEnd));
- ataPIOTransferStart(s, s->iIOBufferCur, s->cbElementaryTransfer);
- s->cbTotalTransfer -= s->cbElementaryTransfer;
- s->iIOBufferCur += s->cbElementaryTransfer;
-
- if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE && s->cbElementaryTransfer > s->cbTotalTransfer)
- s->cbElementaryTransfer = s->cbTotalTransfer;
- }
- else
- ataPIOTransferStop(s);
-}
-
-
-DECLINLINE(void) ataPIOTransferFinish(PAHCIATACONTROLLER pCtl, AHCIATADevState *s)
-{
- /* Do not interfere with RESET processing if the PIO transfer finishes
- * while the RESET line is asserted. */
- if (pCtl->fReset)
- {
- Log2(("%s: Ctl: suppressed continuing PIO transfer as RESET is active\n", __FUNCTION__));
- return;
- }
-
- if ( s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE
- || ( s->iSourceSink != ATAFN_SS_NULL
- && s->iIOBufferCur >= s->iIOBufferEnd))
- {
- /* Need to continue the transfer in the async I/O thread. This is
- * the case for write operations or generally for not yet finished
- * transfers (some data might need to be read). */
- ataUnsetStatus(s, ATA_STAT_READY | ATA_STAT_DRQ);
- ataSetStatus(s, ATA_STAT_BUSY);
-
- Log2(("%s: Ctl: message to async I/O thread, continuing PIO transfer\n", __FUNCTION__));
- ataAsyncIOPutRequest(pCtl, &ataPIORequest);
- }
- else
- {
- /* Either everything finished (though some data might still be pending)
- * or some data is pending before the next read is due. */
-
- /* Continue a previously started transfer. */
- ataUnsetStatus(s, ATA_STAT_DRQ);
- ataSetStatus(s, ATA_STAT_READY);
-
- if (s->cbTotalTransfer)
- {
- /* There is more to transfer, happens usually for large ATAPI
- * reads - the protocol limits the chunk size to 65534 bytes. */
- ataPIOTransfer(pCtl);
- ataSetIRQ(s);
- }
- else
- {
- Log2(("%s: Ctl: skipping message to async I/O thread, ending PIO transfer\n", __FUNCTION__));
- /* Finish PIO transfer. */
- ataPIOTransfer(pCtl);
- Assert(!pCtl->fRedo);
- }
- }
-}
-
-#endif /* IN_RING3 */
-
-static int ataDataWrite(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t cbSize, const uint8_t *pbBuf)
-{
- AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
- uint8_t *p;
-
- if (s->iIOBufferPIODataStart < s->iIOBufferPIODataEnd)
- {
- Assert(s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE);
- p = s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart;
-#ifndef IN_RING3
- /* All but the last transfer unit is simple enough for GC, but
- * sending a request to the async IO thread is too complicated. */
- if (s->iIOBufferPIODataStart + cbSize < s->iIOBufferPIODataEnd)
- {
- memcpy(p, pbBuf, cbSize);
- s->iIOBufferPIODataStart += cbSize;
- }
- else
- return VINF_IOM_R3_IOPORT_WRITE;
-#else /* IN_RING3 */
- memcpy(p, pbBuf, cbSize);
- s->iIOBufferPIODataStart += cbSize;
- if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd)
- ataPIOTransferFinish(pCtl, s);
-#endif /* !IN_RING3 */
- }
- else
- Log2(("%s: DUMMY data\n", __FUNCTION__));
- Log3(("%s: addr=%#x val=%.*Rhxs\n", __FUNCTION__, addr, cbSize, pbBuf));
- return VINF_SUCCESS;
-}
-
-static int ataDataRead(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t cbSize, uint8_t *pbBuf)
-{
- AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
- uint8_t *p;
-
- if (s->iIOBufferPIODataStart < s->iIOBufferPIODataEnd)
- {
- Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
- p = s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart;
-#ifndef IN_RING3
- /* All but the last transfer unit is simple enough for GC, but
- * sending a request to the async IO thread is too complicated. */
- if (s->iIOBufferPIODataStart + cbSize < s->iIOBufferPIODataEnd)
- {
- memcpy(pbBuf, p, cbSize);
- s->iIOBufferPIODataStart += cbSize;
- }
- else
- return VINF_IOM_R3_IOPORT_READ;
-#else /* IN_RING3 */
- memcpy(pbBuf, p, cbSize);
- s->iIOBufferPIODataStart += cbSize;
- if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd)
- ataPIOTransferFinish(pCtl, s);
-#endif /* !IN_RING3 */
- }
- else
- {
- Log2(("%s: DUMMY data\n", __FUNCTION__));
- memset(pbBuf, '\xff', cbSize);
- }
- Log3(("%s: addr=%#x val=%.*Rhxs\n", __FUNCTION__, addr, cbSize, pbBuf));
- return VINF_SUCCESS;
-}
-
-#ifdef IN_RING3
-
-static void ataDMATransferStop(AHCIATADevState *s)
-{
- s->cbTotalTransfer = 0;
- s->cbElementaryTransfer = 0;
- s->iBeginTransfer = ATAFN_BT_NULL;
- s->iSourceSink = ATAFN_SS_NULL;
-}
-
-
-/**
- * Perform the entire DMA transfer in one go (unless a source/sink operation
- * has to be redone or a RESET comes in between). Unlike the PIO counterpart
- * this function cannot handle empty transfers.
- *
- * @param pCtl Controller for which to perform the transfer.
- */
-static void ataDMATransfer(PAHCIATACONTROLLER pCtl)
-{
- PPDMDEVINS pDevIns = CONTROLLER_2_DEVINS(pCtl);
- AHCIATADevState *s = &pCtl->aIfs[pCtl->iAIOIf];
- bool fRedo;
- RTGCPHYS32 pDesc;
- uint32_t cbTotalTransfer, cbElementaryTransfer;
- uint32_t iIOBufferCur, iIOBufferEnd;
- uint32_t dmalen;
- PDMBLOCKTXDIR uTxDir;
- bool fLastDesc = false;
-
- Assert(sizeof(BMDMADesc) == 8);
-
- fRedo = pCtl->fRedo;
- if (RT_LIKELY(!fRedo))
- Assert(s->cbTotalTransfer);
- uTxDir = (PDMBLOCKTXDIR)s->uTxDir;
- cbTotalTransfer = s->cbTotalTransfer;
- cbElementaryTransfer = s->cbElementaryTransfer;
- iIOBufferCur = s->iIOBufferCur;
- iIOBufferEnd = s->iIOBufferEnd;
-
- /* The DMA loop is designed to hold the lock only when absolutely
- * necessary. This avoids long freezes should the guest access the
- * ATA registers etc. for some reason. */
- PDMCritSectLeave(&pCtl->lock);
-
- Log2(("%s: %s tx_size=%d elem_tx_size=%d index=%d end=%d\n",
- __FUNCTION__, uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "T2I" : "I2T",
- cbTotalTransfer, cbElementaryTransfer,
- iIOBufferCur, iIOBufferEnd));
- for (pDesc = pCtl->pFirstDMADesc; pDesc <= pCtl->pLastDMADesc; pDesc += sizeof(BMDMADesc))
- {
- BMDMADesc DMADesc;
- RTGCPHYS32 pBuffer;
- uint32_t cbBuffer;
-
- if (RT_UNLIKELY(fRedo))
- {
- pBuffer = pCtl->pRedoDMABuffer;
- cbBuffer = pCtl->cbRedoDMABuffer;
- fLastDesc = pCtl->fRedoDMALastDesc;
- }
- else
- {
- PDMDevHlpPhysRead(pDevIns, pDesc, &DMADesc, sizeof(BMDMADesc));
- pBuffer = RT_LE2H_U32(DMADesc.pBuffer);
- cbBuffer = RT_LE2H_U32(DMADesc.cbBuffer);
- fLastDesc = !!(cbBuffer & 0x80000000);
- cbBuffer &= 0xfffe;
- if (cbBuffer == 0)
- cbBuffer = 0x10000;
- if (cbBuffer > cbTotalTransfer)
- cbBuffer = cbTotalTransfer;
- }
-
- while (RT_UNLIKELY(fRedo) || (cbBuffer && cbTotalTransfer))
- {
- if (RT_LIKELY(!fRedo))
- {
- dmalen = RT_MIN(cbBuffer, iIOBufferEnd - iIOBufferCur);
- Log2(("%s: DMA desc %#010x: addr=%#010x size=%#010x\n", __FUNCTION__,
- pDesc, pBuffer, cbBuffer));
- if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
- PDMDevHlpPhysWrite(pDevIns, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen);
- else
- PDMDevHlpPhysRead(pDevIns, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen);
- iIOBufferCur += dmalen;
- cbTotalTransfer -= dmalen;
- cbBuffer -= dmalen;
- pBuffer += dmalen;
- }
- if ( iIOBufferCur == iIOBufferEnd
- && (uTxDir == PDMBLOCKTXDIR_TO_DEVICE || cbTotalTransfer))
- {
- if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE && cbElementaryTransfer > cbTotalTransfer)
- cbElementaryTransfer = cbTotalTransfer;
-
- {
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- }
-
- /* The RESET handler could have cleared the DMA transfer
- * state (since we didn't hold the lock until just now
- * the guest can continue in parallel). If so, the state
- * is already set up so the loop is exited immediately. */
- if (s->iSourceSink != ATAFN_SS_NULL)
- {
- s->iIOBufferCur = iIOBufferCur;
- s->iIOBufferEnd = iIOBufferEnd;
- s->cbElementaryTransfer = cbElementaryTransfer;
- s->cbTotalTransfer = cbTotalTransfer;
- Log2(("%s: calling source/sink function\n", __FUNCTION__));
- fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
- if (RT_UNLIKELY(fRedo))
- {
- pCtl->pFirstDMADesc = pDesc;
- pCtl->pRedoDMABuffer = pBuffer;
- pCtl->cbRedoDMABuffer = cbBuffer;
- pCtl->fRedoDMALastDesc = fLastDesc;
- }
- else
- {
- cbTotalTransfer = s->cbTotalTransfer;
- cbElementaryTransfer = s->cbElementaryTransfer;
-
- if (uTxDir == PDMBLOCKTXDIR_TO_DEVICE && cbElementaryTransfer > cbTotalTransfer)
- cbElementaryTransfer = cbTotalTransfer;
- iIOBufferCur = 0;
- iIOBufferEnd = cbElementaryTransfer;
- }
- pCtl->fRedo = fRedo;
- }
- else
- {
- /* This forces the loop to exit immediately. */
- pDesc = pCtl->pLastDMADesc + 1;
- }
-
- PDMCritSectLeave(&pCtl->lock);
- if (RT_UNLIKELY(fRedo))
- break;
- }
- }
-
- if (RT_UNLIKELY(fRedo))
- break;
-
- /* end of transfer */
- if (!cbTotalTransfer || fLastDesc)
- break;
-
- {
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- }
-
- if (!(pCtl->BmDma.u8Cmd & BM_CMD_START) || pCtl->fReset)
- {
- LogRel(("AHCI ATA: Ctl: ABORT DMA%s\n", pCtl->fReset ? " due to RESET" : ""));
- if (!pCtl->fReset)
- ataDMATransferStop(s);
- /* This forces the loop to exit immediately. */
- pDesc = pCtl->pLastDMADesc + 1;
- }
-
- PDMCritSectLeave(&pCtl->lock);
- }
-
- {
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- }
-
- if (RT_UNLIKELY(fRedo))
- return;
-
- if (fLastDesc)
- pCtl->BmDma.u8Status &= ~BM_STATUS_DMAING;
- s->cbTotalTransfer = cbTotalTransfer;
- s->cbElementaryTransfer = cbElementaryTransfer;
- s->iIOBufferCur = iIOBufferCur;
- s->iIOBufferEnd = iIOBufferEnd;
-}
-
-
-/**
- * Signal ataWaitForAsyncIOIsIdle that we're idle (if we actually are).
- *
- * @param pCtl The controller.
- */
-static void ataAsyncSignalIdle(PAHCIATACONTROLLER pCtl)
-{
- /*
- * Take the mutex here and recheck the idle indicator as there might be
- * interesting races, like in the ataReset code.
- */
- int rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT); AssertRC(rc);
-
- if ( pCtl->fSignalIdle
- && ataAsyncIOIsIdle(pCtl, false /*fStrict*/))
- {
- PDMDevHlpAsyncNotificationCompleted(pCtl->pDevInsR3);
- RTThreadUserSignal(pCtl->AsyncIOThread);
- }
-
- rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex); AssertRC(rc);
-}
-
-
-/** Async I/O thread for an interface. Once upon a time this was readable
- * code with several loops and a different semaphore for each purpose. But
- * then came the "how can one save the state in the middle of a PIO transfer"
- * question. The solution was to use an ASM, which is what's there now. */
-static DECLCALLBACK(int) ataAsyncIOLoop(RTTHREAD ThreadSelf, void *pvUser)
-{
- const AHCIATARequest *pReq;
- uint64_t u64TS = 0; /* shut up gcc */
- uint64_t uWait;
- int rc = VINF_SUCCESS;
- PAHCIATACONTROLLER pCtl = (PAHCIATACONTROLLER)pvUser;
- AHCIATADevState *s;
-
- pReq = NULL;
- pCtl->fChainedTransfer = false;
- while (!pCtl->fShutdown)
- {
- /* Keep this thread from doing anything as long as EMT is suspended. */
- while (pCtl->fRedoIdle)
- {
- if (pCtl->fSignalIdle)
- ataAsyncSignalIdle(pCtl);
- rc = RTSemEventWait(pCtl->SuspendIOSem, RT_INDEFINITE_WAIT);
- if (RT_FAILURE(rc) || pCtl->fShutdown)
- break;
-
- pCtl->fRedoIdle = false;
- }
-
- /* Wait for work. */
- if (pReq == NULL)
- {
- if (pCtl->fSignalIdle)
- ataAsyncSignalIdle(pCtl);
- rc = RTSemEventWait(pCtl->AsyncIOSem, RT_INDEFINITE_WAIT);
- if (RT_FAILURE(rc) || pCtl->fShutdown)
- break;
-
- pReq = ataAsyncIOGetCurrentRequest(pCtl);
- }
-
- if (pReq == NULL)
- continue;
-
- AHCIATAAIO ReqType = pReq->ReqType;
-
- Log2(("%s: Ctl: state=%d, req=%d\n", __FUNCTION__, pCtl->uAsyncIOState, ReqType));
- if (pCtl->uAsyncIOState != ReqType)
- {
- /* The new state is not the state that was expected by the normal
- * state changes. This is either a RESET/ABORT or there's something
- * really strange going on. */
- if ( (pCtl->uAsyncIOState == AHCIATA_AIO_PIO || pCtl->uAsyncIOState == AHCIATA_AIO_DMA)
- && (ReqType == AHCIATA_AIO_PIO || ReqType == AHCIATA_AIO_DMA))
- {
- /* Incorrect sequence of PIO/DMA states. Dump request queue. */
- ataAsyncIODumpRequests(pCtl);
- }
- AssertReleaseMsg(ReqType == AHCIATA_AIO_RESET_ASSERTED || ReqType == AHCIATA_AIO_RESET_CLEARED || ReqType == AHCIATA_AIO_ABORT || pCtl->uAsyncIOState == ReqType, ("I/O state inconsistent: state=%d request=%d\n", pCtl->uAsyncIOState, ReqType));
- }
-
- /* Do our work. */
- {
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- }
-
- if (pCtl->uAsyncIOState == AHCIATA_AIO_NEW && !pCtl->fChainedTransfer)
- {
- u64TS = RTTimeNanoTS();
-#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
- STAM_PROFILE_ADV_START(&pCtl->StatAsyncTime, a);
-#endif /* DEBUG || VBOX_WITH_STATISTICS */
- }
-
- switch (ReqType)
- {
- case AHCIATA_AIO_NEW:
-
- pCtl->iAIOIf = pReq->u.t.iIf;
- s = &pCtl->aIfs[pCtl->iAIOIf];
- s->cbTotalTransfer = pReq->u.t.cbTotalTransfer;
- s->uTxDir = pReq->u.t.uTxDir;
- s->iBeginTransfer = pReq->u.t.iBeginTransfer;
- s->iSourceSink = pReq->u.t.iSourceSink;
- s->iIOBufferEnd = 0;
- s->u64CmdTS = u64TS;
-
- if (s->fATAPI)
- {
- if (pCtl->fChainedTransfer)
- {
- /* Only count the actual transfers, not the PIO
- * transfer of the ATAPI command bytes. */
- if (s->fDMA)
- STAM_REL_COUNTER_INC(&s->StatATAPIDMA);
- else
- STAM_REL_COUNTER_INC(&s->StatATAPIPIO);
- }
- }
- else
- {
- if (s->fDMA)
- STAM_REL_COUNTER_INC(&s->StatATADMA);
- else
- STAM_REL_COUNTER_INC(&s->StatATAPIO);
- }
-
- pCtl->fChainedTransfer = false;
-
- if (s->iBeginTransfer != ATAFN_BT_NULL)
- {
- Log2(("%s: Ctl: calling begin transfer function\n", __FUNCTION__));
- g_apfnBeginTransFuncs[s->iBeginTransfer](s);
- s->iBeginTransfer = ATAFN_BT_NULL;
- if (s->uTxDir != PDMBLOCKTXDIR_FROM_DEVICE)
- s->iIOBufferEnd = s->cbElementaryTransfer;
- }
- else
- {
- s->cbElementaryTransfer = s->cbTotalTransfer;
- s->iIOBufferEnd = s->cbTotalTransfer;
- }
- s->iIOBufferCur = 0;
-
- if (s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE)
- {
- if (s->iSourceSink != ATAFN_SS_NULL)
- {
- bool fRedo;
- Log2(("%s: Ctl: calling source/sink function\n", __FUNCTION__));
- fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
- pCtl->fRedo = fRedo;
- if (RT_UNLIKELY(fRedo))
- {
- /* Operation failed at the initial transfer, restart
- * everything from scratch by resending the current
- * request. Occurs very rarely, not worth optimizing. */
- LogRel(("%s: Ctl: redo entire operation\n", __FUNCTION__));
- ataAsyncIOPutRequest(pCtl, pReq);
- break;
- }
- }
- else
- ataCmdOK(s, 0);
- s->iIOBufferEnd = s->cbElementaryTransfer;
-
- }
-
- /* Do not go into the transfer phase if RESET is asserted.
- * The CritSect is released while waiting for the host OS
- * to finish the I/O, thus RESET is possible here. Most
- * important: do not change uAsyncIOState. */
- if (pCtl->fReset)
- break;
-
- if (s->fDMA)
- {
- if (s->cbTotalTransfer)
- {
- ataSetStatus(s, ATA_STAT_DRQ);
-
- pCtl->uAsyncIOState = AHCIATA_AIO_DMA;
- /* If BMDMA is already started, do the transfer now. */
- if (pCtl->BmDma.u8Cmd & BM_CMD_START)
- {
- Log2(("%s: Ctl: message to async I/O thread, continuing DMA transfer immediately\n", __FUNCTION__));
- ataAsyncIOPutRequest(pCtl, &ataDMARequest);
- }
- }
- else
- {
- Assert(s->uTxDir == PDMBLOCKTXDIR_NONE); /* Any transfer which has an initial transfer size of 0 must be marked as such. */
- /* Finish DMA transfer. */
- ataDMATransferStop(s);
- ataSetIRQ(s);
- pCtl->uAsyncIOState = AHCIATA_AIO_NEW;
- }
- }
- else
- {
- if (s->cbTotalTransfer)
- {
- ataPIOTransfer(pCtl);
- Assert(!pCtl->fRedo);
- if (s->fATAPITransfer || s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE)
- ataSetIRQ(s);
-
- if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE || s->iSourceSink != ATAFN_SS_NULL)
- {
- /* Write operations and not yet finished transfers
- * must be completed in the async I/O thread. */
- pCtl->uAsyncIOState = AHCIATA_AIO_PIO;
- }
- else
- {
- /* Finished read operation can be handled inline
- * in the end of PIO transfer handling code. Linux
- * depends on this, as it waits only briefly for
- * devices to become ready after incoming data
- * transfer. Cannot find anything in the ATA spec
- * that backs this assumption, but as all kernels
- * are affected (though most of the time it does
- * not cause any harm) this must work. */
- pCtl->uAsyncIOState = AHCIATA_AIO_NEW;
- }
- }
- else
- {
- Assert(s->uTxDir == PDMBLOCKTXDIR_NONE); /* Any transfer which has an initial transfer size of 0 must be marked as such. */
- /* Finish PIO transfer. */
- ataPIOTransfer(pCtl);
- Assert(!pCtl->fRedo);
- if (!s->fATAPITransfer)
- ataSetIRQ(s);
- pCtl->uAsyncIOState = AHCIATA_AIO_NEW;
- }
- }
- break;
-
- case AHCIATA_AIO_DMA:
- {
- BMDMAState *bm = &pCtl->BmDma;
- s = &pCtl->aIfs[pCtl->iAIOIf]; /* Do not remove or there's an instant crash after loading the saved state */
- ATAFNSS iOriginalSourceSink = (ATAFNSS)s->iSourceSink; /* Used by the hack below, but gets reset by then. */
-
- if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
- AssertRelease(bm->u8Cmd & BM_CMD_WRITE);
- else
- AssertRelease(!(bm->u8Cmd & BM_CMD_WRITE));
-
- if (RT_LIKELY(!pCtl->fRedo))
- {
- /* The specs say that the descriptor table must not cross a
- * 4K boundary. */
- pCtl->pFirstDMADesc = bm->pvAddr;
- pCtl->pLastDMADesc = RT_ALIGN_32(bm->pvAddr + 1, _4K) - sizeof(BMDMADesc);
- }
- ataDMATransfer(pCtl);
-
- if (RT_UNLIKELY(pCtl->fRedo))
- {
- LogRel(("AHCI ATA: Ctl: redo DMA operation\n"));
- ataAsyncIOPutRequest(pCtl, &ataDMARequest);
- break;
- }
-
- /* The infamous delay IRQ hack. */
- if ( iOriginalSourceSink == ATAFN_SS_WRITE_SECTORS
- && s->cbTotalTransfer == 0
- && pCtl->DelayIRQMillies)
- {
- /* Delay IRQ for writing. Required to get the Win2K
- * installation work reliably (otherwise it crashes,
- * usually during component install). So far no better
- * solution has been found. */
- Log(("%s: delay IRQ hack\n", __FUNCTION__));
- PDMCritSectLeave(&pCtl->lock);
- RTThreadSleep(pCtl->DelayIRQMillies);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- }
-
- ataUnsetStatus(s, ATA_STAT_DRQ);
- Assert(!pCtl->fChainedTransfer);
- Assert(s->iSourceSink == ATAFN_SS_NULL);
- if (s->fATAPITransfer)
- {
- s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
- Log2(("%s: Ctl: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector));
- s->fATAPITransfer = false;
- }
- ataSetIRQ(s);
- pCtl->uAsyncIOState = AHCIATA_AIO_NEW;
- break;
- }
-
- case AHCIATA_AIO_PIO:
- s = &pCtl->aIfs[pCtl->iAIOIf]; /* Do not remove or there's an instant crash after loading the saved state */
-
- if (s->iSourceSink != ATAFN_SS_NULL)
- {
- bool fRedo;
- Log2(("%s: Ctl: calling source/sink function\n", __FUNCTION__));
- fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
- pCtl->fRedo = fRedo;
- if (RT_UNLIKELY(fRedo))
- {
- LogRel(("AHCI ATA: Ctl: redo PIO operation\n"));
- ataAsyncIOPutRequest(pCtl, &ataPIORequest);
- break;
- }
- s->iIOBufferCur = 0;
- s->iIOBufferEnd = s->cbElementaryTransfer;
- }
- else
- {
- /* Continue a previously started transfer. */
- ataUnsetStatus(s, ATA_STAT_BUSY);
- ataSetStatus(s, ATA_STAT_READY);
- }
-
- /* It is possible that the drives on this controller get RESET
- * during the above call to the source/sink function. If that's
- * the case, don't restart the transfer and don't finish it the
- * usual way. RESET handling took care of all that already.
- * Most important: do not change uAsyncIOState. */
- if (pCtl->fReset)
- break;
-
- if (s->cbTotalTransfer)
- {
- ataPIOTransfer(pCtl);
- ataSetIRQ(s);
-
- if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE || s->iSourceSink != ATAFN_SS_NULL)
- {
- /* Write operations and not yet finished transfers
- * must be completed in the async I/O thread. */
- pCtl->uAsyncIOState = AHCIATA_AIO_PIO;
- }
- else
- {
- /* Finished read operation can be handled inline
- * in the end of PIO transfer handling code. Linux
- * depends on this, as it waits only briefly for
- * devices to become ready after incoming data
- * transfer. Cannot find anything in the ATA spec
- * that backs this assumption, but as all kernels
- * are affected (though most of the time it does
- * not cause any harm) this must work. */
- pCtl->uAsyncIOState = AHCIATA_AIO_NEW;
- }
- }
- else
- {
- /* Finish PIO transfer. */
- ataPIOTransfer(pCtl);
- if ( !pCtl->fChainedTransfer
- && !s->fATAPITransfer
- && s->uTxDir != PDMBLOCKTXDIR_FROM_DEVICE)
- {
- ataSetIRQ(s);
- }
- pCtl->uAsyncIOState = AHCIATA_AIO_NEW;
- }
- break;
-
- case AHCIATA_AIO_RESET_ASSERTED:
- pCtl->uAsyncIOState = AHCIATA_AIO_RESET_CLEARED;
- ataPIOTransferStop(&pCtl->aIfs[0]);
- ataPIOTransferStop(&pCtl->aIfs[1]);
- /* Do not change the DMA registers, they are not affected by the
- * ATA controller reset logic. It should be sufficient to issue a
- * new command, which is now possible as the state is cleared. */
- break;
-
- case AHCIATA_AIO_RESET_CLEARED:
- pCtl->uAsyncIOState = AHCIATA_AIO_NEW;
- pCtl->fReset = false;
- LogRel(("AHCI ATA: Ctl: finished processing RESET\n"));
- for (uint32_t i = 0; i < RT_ELEMENTS(pCtl->aIfs); i++)
- {
- if (pCtl->aIfs[i].fATAPI)
- ataSetStatusValue(&pCtl->aIfs[i], 0); /* NOTE: READY is _not_ set */
- else
- ataSetStatusValue(&pCtl->aIfs[i], ATA_STAT_READY | ATA_STAT_SEEK);
- ataSetSignature(&pCtl->aIfs[i]);
- }
- break;
-
- case AHCIATA_AIO_ABORT:
- /* Abort the current command no matter what. There cannot be
- * any command activity on the other drive otherwise using
- * one thread per controller wouldn't work at all. */
- s = &pCtl->aIfs[pReq->u.a.iIf];
-
- pCtl->uAsyncIOState = AHCIATA_AIO_NEW;
- /* Do not change the DMA registers, they are not affected by the
- * ATA controller reset logic. It should be sufficient to issue a
- * new command, which is now possible as the state is cleared. */
- if (pReq->u.a.fResetDrive)
- {
- ataResetDevice(s);
- ataExecuteDeviceDiagnosticSS(s);
- }
- else
- {
- ataPIOTransferStop(s);
- ataUnsetStatus(s, ATA_STAT_BUSY | ATA_STAT_DRQ | ATA_STAT_SEEK | ATA_STAT_ERR);
- ataSetStatus(s, ATA_STAT_READY);
- ataSetIRQ(s);
- }
- break;
-
- default:
- AssertMsgFailed(("Undefined async I/O state %d\n", pCtl->uAsyncIOState));
- }
-
- ataAsyncIORemoveCurrentRequest(pCtl, ReqType);
- pReq = ataAsyncIOGetCurrentRequest(pCtl);
-
- if (pCtl->uAsyncIOState == AHCIATA_AIO_NEW && !pCtl->fChainedTransfer)
- {
-#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
- STAM_PROFILE_ADV_STOP(&pCtl->StatAsyncTime, a);
-#endif /* DEBUG || VBOX_WITH_STATISTICS */
-
- u64TS = RTTimeNanoTS() - u64TS;
- uWait = u64TS / 1000;
- Log(("%s: Ctl: LUN#%d finished I/O transaction in %d microseconds\n", __FUNCTION__, pCtl->aIfs[pCtl->iAIOIf].iLUN, (uint32_t)(uWait)));
- /* Mark command as finished. */
- pCtl->aIfs[pCtl->iAIOIf].u64CmdTS = 0;
-
- /*
- * Release logging of command execution times depends on the
- * command type. ATAPI commands often take longer (due to CD/DVD
- * spin up time etc.) so the threshold is different.
- */
- if (pCtl->aIfs[pCtl->iAIOIf].uATARegCommand != ATA_PACKET)
- {
- if (uWait > 8 * 1000 * 1000)
- {
- /*
- * Command took longer than 8 seconds. This is close
- * enough or over the guest's command timeout, so place
- * an entry in the release log to allow tracking such
- * timing errors (which are often caused by the host).
- */
- LogRel(("AHCI ATA: execution time for ATA command %#04x was %d seconds\n", pCtl->aIfs[pCtl->iAIOIf].uATARegCommand, uWait / (1000 * 1000)));
- }
- }
- else
- {
- if (uWait > 20 * 1000 * 1000)
- {
- /*
- * Command took longer than 20 seconds. This is close
- * enough or over the guest's command timeout, so place
- * an entry in the release log to allow tracking such
- * timing errors (which are often caused by the host).
- */
- LogRel(("AHCI ATA: execution time for ATAPI command %#04x was %d seconds\n", pCtl->aIfs[pCtl->iAIOIf].aATAPICmd[0], uWait / (1000 * 1000)));
- }
- }
-
-#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
- if (uWait < pCtl->StatAsyncMinWait || !pCtl->StatAsyncMinWait)
- pCtl->StatAsyncMinWait = uWait;
- if (uWait > pCtl->StatAsyncMaxWait)
- pCtl->StatAsyncMaxWait = uWait;
-
- STAM_COUNTER_ADD(&pCtl->StatAsyncTimeUS, uWait);
- STAM_COUNTER_INC(&pCtl->StatAsyncOps);
-#endif /* DEBUG || VBOX_WITH_STATISTICS */
- }
-
- PDMCritSectLeave(&pCtl->lock);
- }
-
- /* Signal the ultimate idleness. */
- if (pCtl->fSignalIdle)
- PDMDevHlpAsyncNotificationCompleted(pCtl->pDevInsR3);
- RTThreadUserSignal(ThreadSelf);
-
- /* Do not destroy request mutex yet, still needed for proper shutdown. */
- pCtl->fShutdown = false;
-
- Log2(("%s: Ctl: return %Rrc\n", __FUNCTION__, rc));
- return rc;
-}
-
-#endif /* IN_RING3 */
-
-static uint32_t ataBMDMACmdReadB(PAHCIATACONTROLLER pCtl, uint32_t addr)
-{
- uint32_t val = pCtl->BmDma.u8Cmd;
- Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val));
- return val;
-}
-
-
-static void ataBMDMACmdWriteB(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val)
-{
- Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val));
- if (!(val & BM_CMD_START))
- {
- pCtl->BmDma.u8Status &= ~BM_STATUS_DMAING;
- pCtl->BmDma.u8Cmd = val & (BM_CMD_START | BM_CMD_WRITE);
- }
- else
- {
-#ifdef IN_RING3
- /* Check whether the guest OS wants to change DMA direction in
- * mid-flight. Not allowed, according to the AHCI specs. */
- Assert(!(pCtl->BmDma.u8Status & BM_STATUS_DMAING) || !((val ^ pCtl->BmDma.u8Cmd) & 0x04));
- pCtl->BmDma.u8Status |= BM_STATUS_DMAING;
- pCtl->BmDma.u8Cmd = val & (BM_CMD_START | BM_CMD_WRITE);
-
- /* Do not continue DMA transfers while the RESET line is asserted. */
- if (pCtl->fReset)
- {
- Log2(("%s: Ctl: suppressed continuing DMA transfer as RESET is active\n", __FUNCTION__));
- return;
- }
-
- /* Do not start DMA transfers if there's a PIO transfer going on. */
- if (!pCtl->aIfs[pCtl->iSelectedIf].fDMA)
- return;
-
- if (pCtl->aIfs[pCtl->iAIOIf].uATARegStatus & ATA_STAT_DRQ)
- {
- Log2(("%s: Ctl: message to async I/O thread, continuing DMA transfer\n", __FUNCTION__));
- ataAsyncIOPutRequest(pCtl, &ataDMARequest);
- }
-#else /* !IN_RING3 */
- AssertMsgFailed(("DMA START handling is too complicated for GC\n"));
-#endif /* IN_RING3 */
- }
-}
-
-static uint32_t ataBMDMAStatusReadB(PAHCIATACONTROLLER pCtl, uint32_t addr)
-{
- uint32_t val = pCtl->BmDma.u8Status;
- Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val));
- return val;
-}
-
-static void ataBMDMAStatusWriteB(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val)
-{
- Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val));
- pCtl->BmDma.u8Status = (val & (BM_STATUS_D0DMA | BM_STATUS_D1DMA))
- | (pCtl->BmDma.u8Status & BM_STATUS_DMAING)
- | (pCtl->BmDma.u8Status & ~val & (BM_STATUS_ERROR | BM_STATUS_INT));
-}
-
-static uint32_t ataBMDMAAddrReadL(PAHCIATACONTROLLER pCtl, uint32_t addr)
-{
- uint32_t val = (uint32_t)pCtl->BmDma.pvAddr;
- Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val));
- return val;
-}
-
-static void ataBMDMAAddrWriteL(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val)
-{
- Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val));
- pCtl->BmDma.pvAddr = val & ~3;
-}
-
-static void ataBMDMAAddrWriteLowWord(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val)
-{
- Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val));
- pCtl->BmDma.pvAddr = (pCtl->BmDma.pvAddr & 0xFFFF0000) | RT_LOWORD(val & ~3);
-
-}
-
-static void ataBMDMAAddrWriteHighWord(PAHCIATACONTROLLER pCtl, uint32_t addr, uint32_t val)
-{
- Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val));
- pCtl->BmDma.pvAddr = (RT_LOWORD(val) << 16) | RT_LOWORD(pCtl->BmDma.pvAddr);
-}
-
-#define VAL(port, size) ( ((port) & 7) | ((size) << 3) )
-
-/**
- * Port I/O Handler for bus master DMA IN operations.
- * @see FNIOMIOPORTIN for details.
- */
-int ataControllerBMDMAIOPortRead(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *pu32, unsigned cb)
-{
- int rc;
-
- rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_READ);
- if (rc != VINF_SUCCESS)
- return rc;
- switch (VAL(Port, cb))
- {
- case VAL(0, 1): *pu32 = ataBMDMACmdReadB(pCtl, Port); break;
- case VAL(0, 2): *pu32 = ataBMDMACmdReadB(pCtl, Port); break;
- case VAL(2, 1): *pu32 = ataBMDMAStatusReadB(pCtl, Port); break;
- case VAL(2, 2): *pu32 = ataBMDMAStatusReadB(pCtl, Port); break;
- case VAL(4, 4): *pu32 = ataBMDMAAddrReadL(pCtl, Port); break;
- default:
- AssertMsgFailed(("%s: Unsupported read from port %x size=%d\n", __FUNCTION__, Port, cb));
- PDMCritSectLeave(&pCtl->lock);
- return VERR_IOM_IOPORT_UNUSED;
- }
- PDMCritSectLeave(&pCtl->lock);
- return rc;
-}
-
-/**
- * Port I/O Handler for bus master DMA OUT operations.
- * @see FNIOMIOPORTOUT for details.
- */
-int ataControllerBMDMAIOPortWrite(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb)
-{
- int rc;
-
- rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_WRITE);
- if (rc != VINF_SUCCESS)
- return rc;
- switch (VAL(Port, cb))
- {
- case VAL(0, 1):
-#ifndef IN_RING3
- if (u32 & BM_CMD_START)
- {
- rc = VINF_IOM_R3_IOPORT_WRITE;
- break;
- }
-#endif /* !IN_RING3 */
- ataBMDMACmdWriteB(pCtl, Port, u32);
- break;
- case VAL(2, 1): ataBMDMAStatusWriteB(pCtl, Port, u32); break;
- case VAL(4, 4): ataBMDMAAddrWriteL(pCtl, Port, u32); break;
- case VAL(4, 2): ataBMDMAAddrWriteLowWord(pCtl, Port, u32); break;
- case VAL(6, 2): ataBMDMAAddrWriteHighWord(pCtl, Port, u32); break;
- default: AssertMsgFailed(("%s: Unsupported write to port %x size=%d val=%x\n", __FUNCTION__, Port, cb, u32)); break;
- }
- PDMCritSectLeave(&pCtl->lock);
- return rc;
-}
-
-#undef VAL
-
-
-#ifdef IN_RING3
-#if 0
-
-/**
- * Callback function for mapping an PCI I/O region.
- *
- * @return VBox status code.
- * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
- * @param iRegion The region number.
- * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
- * I/O port, else it's a physical address.
- * This address is *NOT* relative to pci_mem_base like earlier!
- * @param enmType One of the PCI_ADDRESS_SPACE_* values.
- */
-static DECLCALLBACK(int) ataBMDMAIORangeMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
-{
- PCIATAState *pThis = PCIDEV_2_PCIATASTATE(pPciDev);
- int rc = VINF_SUCCESS;
- Assert(enmType == PCI_ADDRESS_SPACE_IO);
- Assert(iRegion == 4);
- AssertMsg(RT_ALIGN(GCPhysAddress, 8) == GCPhysAddress, ("Expected 8 byte alignment. GCPhysAddress=%#x\n", GCPhysAddress));
-
- /* Register the port range. */
- for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
- {
- int rc2 = PDMDevHlpIOPortRegister(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress + i * 8, 8,
- (RTHCPTR)i, ataBMDMAIOPortWrite, ataBMDMAIOPortRead, NULL, NULL, "ATA Bus Master DMA");
- AssertRC(rc2);
- if (rc2 < rc)
- rc = rc2;
-
- if (pThis->fGCEnabled)
- {
- rc2 = PDMDevHlpIOPortRegisterGC(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress + i * 8, 8,
- (RTGCPTR)i, "ataBMDMAIOPortWrite", "ataBMDMAIOPortRead", NULL, NULL, "ATA Bus Master DMA");
- AssertRC(rc2);
- if (rc2 < rc)
- rc = rc2;
- }
- if (pThis->fR0Enabled)
- {
- rc2 = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress + i * 8, 8,
- (RTR0PTR)i, "ataBMDMAIOPortWrite", "ataBMDMAIOPortRead", NULL, NULL, "ATA Bus Master DMA");
- AssertRC(rc2);
- if (rc2 < rc)
- rc = rc2;
- }
- }
- return rc;
-}
-#endif
-
-/**
- * Reset the controller to an initial state.
- *
- * @returns VBox status.
- * @param pDevIns The device instance data.
- */
-void ataControllerReset(PAHCIATACONTROLLER pCtl)
-{
- pCtl->iSelectedIf = 0;
- pCtl->iAIOIf = 0;
- pCtl->BmDma.u8Cmd = 0;
- /* Report that both drives present on the bus are in DMA mode. This
- * pretends that there is a BIOS that has set it up. Normal reset
- * default is 0x00. */
- pCtl->BmDma.u8Status = (pCtl->aIfs[0].pDrvBase != NULL ? BM_STATUS_D0DMA : 0)
- | (pCtl->aIfs[1].pDrvBase != NULL ? BM_STATUS_D1DMA : 0);
- pCtl->BmDma.pvAddr = 0;
-
- pCtl->fReset = true;
- pCtl->fRedo = false;
- pCtl->fRedoIdle = false;
- ataAsyncIOClearRequests(pCtl);
- Log2(("%s: Ctl: message to async I/O thread, reset controller\n", __FUNCTION__));
- ataAsyncIOPutRequest(pCtl, &ataResetARequest);
- ataAsyncIOPutRequest(pCtl, &ataResetCRequest);
- if (!ataWaitForAsyncIOIsIdle(pCtl, 30000))
- AssertReleaseMsgFailed(("Async I/O thread busy after reset\n"));
-
- for (uint32_t i = 0; i < RT_ELEMENTS(pCtl->aIfs); i++)
- ataResetDevice(&pCtl->aIfs[i]);
-}
-
-#if 0
-/* -=-=-=-=-=- AHCIATADevState::IBase -=-=-=-=-=- */
-
-/**
- * @interface_method_impl{PDMIBASE,pfnQueryInterface}
- */
-static DECLCALLBACK(void *) ataQueryInterface(PPDMIBASE pInterface, const char *pszIID)
-{
- ATADevState *pIf = PDMIBASE_2_ATASTATE(pInterface);
- PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pIf->IBase);
- PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pIf->IPort);
- PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pIf->IMountNotify);
- return NULL;
-}
-#endif
-#endif /* IN_RING3 */
-
-
-/* -=-=-=-=-=- Wrappers -=-=-=-=-=- */
-
-/**
- * Port I/O Handler for primary port range OUT operations.
- * @see FNIOMIOPORTOUT for details.
- */
-int ataControllerIOPortWrite1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb)
-{
- int rc = VINF_SUCCESS;
-
- rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_WRITE);
- if (rc != VINF_SUCCESS)
- return rc;
- if (cb == 1)
- rc = ataIOPortWriteU8(pCtl, Port, u32);
- else if (Port == pCtl->IOPortBase1)
- {
- Assert(cb == 2 || cb == 4);
- rc = ataDataWrite(pCtl, Port, cb, (const uint8_t *)&u32);
- }
- else
- AssertMsgFailed(("ataIOPortWrite1: unsupported write to port %x val=%x size=%d\n", Port, u32, cb));
- PDMCritSectLeave(&pCtl->lock);
- return rc;
-}
-
-
-/**
- * Port I/O Handler for primary port range IN operations.
- * @see FNIOMIOPORTIN for details.
- */
-int ataControllerIOPortRead1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *pu32, unsigned cb)
-{
- int rc = VINF_SUCCESS;
-
- rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_READ);
- if (rc != VINF_SUCCESS)
- return rc;
- if (cb == 1)
- {
- rc = ataIOPortReadU8(pCtl, Port, pu32);
- }
- else if (Port == pCtl->IOPortBase1)
- {
- Assert(cb == 2 || cb == 4);
- rc = ataDataRead(pCtl, Port, cb, (uint8_t *)pu32);
- if (cb == 2)
- *pu32 &= 0xffff;
- }
- else
- {
- AssertMsgFailed(("ataIOPortRead1: unsupported read from port %x size=%d\n", Port, cb));
- rc = VERR_IOM_IOPORT_UNUSED;
- }
- PDMCritSectLeave(&pCtl->lock);
- return rc;
-}
-
-#ifndef IN_RING0 /** @todo do this in ring-0 as well. */
-/**
- * Port I/O Handler for primary port range IN string operations.
- * @see FNIOMIOPORTINSTRING for details.
- */
-int ataControllerIOPortReadStr1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
-{
- int rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_READ);
- if (rc != VINF_SUCCESS)
- return rc;
- if (Port == pCtl->IOPortBase1)
- {
- uint32_t cTransAvailable, cTransfer = *pcTransfer, cbTransfer;
- RTGCPTR GCDst = *pGCPtrDst;
- AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
- Assert(cb == 2 || cb == 4);
-
- cTransAvailable = (s->iIOBufferPIODataEnd - s->iIOBufferPIODataStart) / cb;
-#ifndef IN_RING3
- /* Deal with the unlikely case where no data (or not enough for the read length operation) is available; go back to ring 3. */
- if (!cTransAvailable)
- {
- PDMCritSectLeave(&pCtl->lock);
- return VINF_IOM_R3_IOPORT_READ;
- }
- /* The last transfer unit cannot be handled in GC, as it involves thread communication. */
- cTransAvailable--;
-#endif /* !IN_RING3 */
- /* Do not handle the dummy transfer stuff here, leave it to the single-word transfers.
- * They are not performance-critical and generally shouldn't occur at all. */
- if (cTransAvailable > cTransfer)
- cTransAvailable = cTransfer;
- cbTransfer = cTransAvailable * cb;
-
- PPDMDEVINS pDevIns = pCtl->CTX_SUFF(pDevIns);
- rc = PGMPhysSimpleDirtyWriteGCPtr(PDMDevHlpGetVMCPU(pDevIns), GCDst, s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart, cbTransfer);
-#ifndef IN_RING3
- /* Paranoia. */
- if (RT_FAILURE(rc))
- {
- PDMCritSectLeave(&pCtl->lock);
- AssertFailed();
- return VINF_IOM_R3_IOPORT_READ;
- }
-#else
- Assert(rc == VINF_SUCCESS);
-#endif
-
- if (cbTransfer)
- Log3(("%s: addr=%#x val=%.*Rhxs\n", __FUNCTION__, Port, cbTransfer, s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart));
- s->iIOBufferPIODataStart += cbTransfer;
- *pGCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCDst + cbTransfer);
- *pcTransfer = cTransfer - cTransAvailable;
-#ifdef IN_RING3
- if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd)
- ataPIOTransferFinish(pCtl, s);
-#endif /* IN_RING3 */
- }
- PDMCritSectLeave(&pCtl->lock);
- return rc;
-}
-
-
-/**
- * Port I/O Handler for primary port range OUT string operations.
- * @see FNIOMIOPORTOUTSTRING for details.
- */
-int ataControllerIOPortWriteStr1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
-{
- int rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_WRITE);
- if (rc != VINF_SUCCESS)
- return rc;
- if (Port == pCtl->IOPortBase1)
- {
- uint32_t cTransAvailable, cTransfer = *pcTransfer, cbTransfer;
- RTGCPTR GCSrc = *pGCPtrSrc;
- AHCIATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
- Assert(cb == 2 || cb == 4);
-
- cTransAvailable = (s->iIOBufferPIODataEnd - s->iIOBufferPIODataStart) / cb;
-#ifndef IN_RING3
- /* Deal with the unlikely case where no data (or not enough for the read length operation) is available; go back to ring 3. */
- if (!cTransAvailable)
- {
- PDMCritSectLeave(&pCtl->lock);
- return VINF_IOM_R3_IOPORT_WRITE;
- }
- /* The last transfer unit cannot be handled in GC, as it involves thread communication. */
- cTransAvailable--;
-#endif /* !IN_RING3 */
- /* Do not handle the dummy transfer stuff here, leave it to the single-word transfers.
- * They are not performance-critical and generally shouldn't occur at all. */
- if (cTransAvailable > cTransfer)
- cTransAvailable = cTransfer;
- cbTransfer = cTransAvailable * cb;
-
- PPDMDEVINS pDevIns = pCtl->CTX_SUFF(pDevIns);
- rc = PGMPhysSimpleReadGCPtr(PDMDevHlpGetVMCPU(pDevIns), s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart, GCSrc, cbTransfer);
-#ifndef IN_RING3
- /* Paranoia. */
- if (RT_FAILURE(rc))
- {
- PDMCritSectLeave(&pCtl->lock);
- AssertFailed();
- return VINF_IOM_R3_IOPORT_WRITE;
- }
-#else
- Assert(rc == VINF_SUCCESS);
-#endif
-
- if (cbTransfer)
- Log3(("%s: addr=%#x val=%.*Rhxs\n", __FUNCTION__, Port, cbTransfer, s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart));
- s->iIOBufferPIODataStart += cbTransfer;
- *pGCPtrSrc = (RTGCPTR)((RTGCUINTPTR)GCSrc + cbTransfer);
- *pcTransfer = cTransfer - cTransAvailable;
-#ifdef IN_RING3
- if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd)
- ataPIOTransferFinish(pCtl, s);
-#endif /* IN_RING3 */
- }
- PDMCritSectLeave(&pCtl->lock);
- return rc;
-}
-#endif /* !IN_RING0 */
-
-/**
- * Port I/O Handler for secondary port range OUT operations.
- * @see FNIOMIOPORTOUT for details.
- */
-int ataControllerIOPortWrite2(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb)
-{
- int rc;
-
- if (cb != 1)
- return VINF_SUCCESS;
- rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_WRITE);
- if (rc != VINF_SUCCESS)
- return rc;
- rc = ataControlWrite(pCtl, Port, u32);
- PDMCritSectLeave(&pCtl->lock);
- return rc;
-}
-
-
-/**
- * Port I/O Handler for secondary port range IN operations.
- * @see FNIOMIOPORTIN for details.
- */
-int ataControllerIOPortRead2(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *pu32, unsigned cb)
-{
- int rc;
-
- if (cb != 1)
- return VERR_IOM_IOPORT_UNUSED;
-
- rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_R3_IOPORT_READ);
- if (rc != VINF_SUCCESS)
- return rc;
- *pu32 = ataStatusRead(pCtl, Port);
- PDMCritSectLeave(&pCtl->lock);
- return VINF_SUCCESS;
-}
-
-#ifdef IN_RING3
-
-/**
- * Waits for all async I/O threads to complete whatever they
- * are doing at the moment.
- *
- * @returns true on success.
- * @returns false when one or more threads is still processing.
- * @param pThis Pointer to the instance data.
- * @param cMillies How long to wait (total).
- */
-static bool ataWaitForAllAsyncIOIsIdle(PAHCIATACONTROLLER pCtl, RTMSINTERVAL cMillies)
-{
- uint64_t u64Start;
- PPDMDEVINS pDevIns = pCtl->CTXALLSUFF(pDevIns);
- bool fAllIdle = false;
-
- /*
- * Wait for any pending async operation to finish
- */
- u64Start = RTTimeMilliTS();
- for (;;)
- {
- /* Check all async I/O threads. */
- fAllIdle = true;
-
- fAllIdle &= ataAsyncIOIsIdle(pCtl, false);
- if (!fAllIdle)
- break;
-
- if ( fAllIdle
- || RTTimeMilliTS() - u64Start >= cMillies)
- break;
-
- /* Sleep for a bit. */
- RTThreadSleep(100);
- }
-
- if (!fAllIdle)
- LogRel(("AHCI ATA: Ctl is still executing, DevSel=%d AIOIf=%d CmdIf0=%#04x CmdIf1=%#04x\n",
- pCtl->iSelectedIf, pCtl->iAIOIf,
- pCtl->aIfs[0].uATARegCommand, pCtl->aIfs[1].uATARegCommand));
-
- return fAllIdle;
-}
-
-
-DECLINLINE(void) ataRelocBuffer(PPDMDEVINS pDevIns, AHCIATADevState *s)
-{
- if (s->pbIOBufferR3)
- s->pbIOBufferRC = MMHyperR3ToRC(PDMDevHlpGetVM(pDevIns), s->pbIOBufferR3);
-}
-
-
-/**
- * @copydoc FNPDMDEVRELOCATE
- */
-void ataControllerRelocate(PAHCIATACONTROLLER pCtl, RTGCINTPTR offDelta)
-{
- PPDMDEVINS pDevIns = pCtl->CTXALLSUFF(pDevIns);
-
- pCtl->pDevInsRC += offDelta;
- pCtl->aIfs[0].pDevInsRC += offDelta;
- pCtl->aIfs[0].pControllerRC += offDelta;
- ataRelocBuffer(pDevIns, &pCtl->aIfs[0]);
- pCtl->aIfs[1].pDevInsRC += offDelta;
- pCtl->aIfs[1].pControllerRC += offDelta;
- ataRelocBuffer(pDevIns, &pCtl->aIfs[1]);
-}
-
-
-/**
- * Destroy a controller instance.
- *
- * Most VM resources are freed by the VM. This callback is provided so that any non-VM
- * resources can be freed correctly.
- *
- * @param pCtl The controller instance.
- */
-int ataControllerDestroy(PAHCIATACONTROLLER pCtl)
-{
- int rc;
-
- Log(("%s:\n", __FUNCTION__));
-
- /*
- * Terminate the async helper thread and wait for it to finish up.
- */
- if (pCtl->AsyncIOThread != NIL_RTTHREAD)
- {
- ASMAtomicWriteU32(&pCtl->fShutdown, true);
- rc = RTSemEventSignal(pCtl->AsyncIOSem);
- AssertRC(rc);
- rc = RTSemEventSignal(pCtl->SuspendIOSem);
- AssertRC(rc);
-
- rc = RTThreadWait(pCtl->AsyncIOThread, 30000 /* 30 s*/, NULL);
- if (RT_SUCCESS(rc))
- pCtl->AsyncIOThread = NIL_RTTHREAD;
- else
- LogRel(("PIIX3 ATA Dtor: Ctl/irq=%u is still executing, DevSel=%d AIOIf=%d CmdIf0=%#04x CmdIf1=%#04x rc=%Rrc\n",
- pCtl->irq, pCtl->iSelectedIf, pCtl->iAIOIf,
- pCtl->aIfs[0].uATARegCommand, pCtl->aIfs[1].uATARegCommand, rc));
- }
-
- /*
- * Now the request mutexes are no longer needed. Free resources.
- */
- if (pCtl->AsyncIORequestMutex != NIL_RTSEMMUTEX)
- {
- RTSemMutexDestroy(pCtl->AsyncIORequestMutex);
- pCtl->AsyncIORequestMutex = NIL_RTSEMMUTEX;
- }
- if (pCtl->AsyncIOSem != NIL_RTSEMEVENT)
- {
- RTSemEventDestroy(pCtl->AsyncIOSem);
- pCtl->AsyncIOSem = NIL_RTSEMEVENT;
- }
- if (pCtl->SuspendIOSem != NIL_RTSEMEVENT)
- {
- RTSemEventDestroy(pCtl->SuspendIOSem);
- pCtl->SuspendIOSem = NIL_RTSEMEVENT;
- }
-
- /* try one final time */
- if (pCtl->AsyncIOThread != NIL_RTTHREAD)
- {
- rc = RTThreadWait(pCtl->AsyncIOThread, 1 /*ms*/, NULL);
- if (RT_SUCCESS(rc))
- {
- pCtl->AsyncIOThread = NIL_RTTHREAD;
- LogRel(("AHCI ATA Dtor: Ctl/irq=%u actually completed.\n", pCtl->irq));
- }
- }
-
- return VINF_SUCCESS;
-}
-
-/**
- * Detach notification.
- *
- * The DVD drive has been unplugged.
- *
- * @param pDevIns The device instance.
- * @param fMaster True if the master is detached
- * false for the slave
- */
-void ataControllerDetach(PAHCIATACONTROLLER pCtl, bool fMaster)
-{
- AHCIATADevState *pIf;
-
- /*
- * Locate the controller and stuff.
- */
- pIf = &pCtl->aIfs[fMaster ? 0 : 1];
-
- /*
- * Zero some important members.
- */
- pIf->pDrvBase = NULL;
- pIf->pDrvBlock = NULL;
- pIf->pDrvBlockBios = NULL;
- pIf->pDrvMount = NULL;
-}
-
-/**
- * Configure a LUN.
- *
- * @returns VBox status code.
- * @param pDevIns The device instance.
- * @param pIf The ATA unit state.
- */
-static int ataConfigLun(PPDMDEVINS pDevIns, AHCIATADevState *pIf)
-{
- int rc;
- PDMBLOCKTYPE enmType;
-
- /*
- * Query Block, Bios and Mount interfaces.
- */
- pIf->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pIf->pDrvBase, PDMIBLOCK);
- if (!pIf->pDrvBlock)
- {
- AssertMsgFailed(("Configuration error: LUN#%d hasn't a block interface!\n", pIf->iLUN));
- return VERR_PDM_MISSING_INTERFACE;
- }
-
- /** @todo implement the BIOS invisible code path. */
- pIf->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pIf->pDrvBase, PDMIBLOCKBIOS);
- if (!pIf->pDrvBlockBios)
- {
- AssertMsgFailed(("Configuration error: LUN#%d hasn't a block BIOS interface!\n", pIf->iLUN));
- return VERR_PDM_MISSING_INTERFACE;
- }
- pIf->pDrvMount = PDMIBASE_QUERY_INTERFACE(pIf->pDrvBase, PDMIMOUNT);
-
- /*
- * Validate type.
- */
- enmType = pIf->pDrvBlock->pfnGetType(pIf->pDrvBlock);
- if ( enmType != PDMBLOCKTYPE_CDROM
- && enmType != PDMBLOCKTYPE_DVD
- && enmType != PDMBLOCKTYPE_HARD_DISK)
- {
- AssertMsgFailed(("Configuration error: LUN#%d isn't a disk or cd/dvd-rom. enmType=%d\n", pIf->iLUN, enmType));
- return VERR_PDM_UNSUPPORTED_BLOCK_TYPE;
- }
- if ( ( enmType == PDMBLOCKTYPE_DVD
- || enmType == PDMBLOCKTYPE_CDROM)
- && !pIf->pDrvMount)
- {
- AssertMsgFailed(("Internal error: cdrom without a mountable interface, WTF???!\n"));
- return VERR_INTERNAL_ERROR;
- }
- pIf->fATAPI = enmType == PDMBLOCKTYPE_DVD || enmType == PDMBLOCKTYPE_CDROM;
- pIf->fATAPIPassthrough = pIf->fATAPI ? (pIf->pDrvBlock->pfnSendCmd != NULL) : false;
-
- /*
- * Allocate I/O buffer.
- */
- PVM pVM = PDMDevHlpGetVM(pDevIns);
- if (pIf->cbIOBuffer)
- {
- /* Buffer is (probably) already allocated. Validate the fields,
- * because memory corruption can also overwrite pIf->cbIOBuffer. */
- if (pIf->fATAPI)
- AssertRelease(pIf->cbIOBuffer == _128K);
- else
- AssertRelease(pIf->cbIOBuffer == ATA_MAX_MULT_SECTORS * 512);
- Assert(pIf->pbIOBufferR3);
- Assert(pIf->pbIOBufferR0 == MMHyperR3ToR0(pVM, pIf->pbIOBufferR3));
- Assert(pIf->pbIOBufferRC == MMHyperR3ToRC(pVM, pIf->pbIOBufferR3));
- }
- else
- {
- if (pIf->fATAPI)
- pIf->cbIOBuffer = _128K;
- else
- pIf->cbIOBuffer = ATA_MAX_MULT_SECTORS * 512;
- Assert(!pIf->pbIOBufferR3);
- rc = MMR3HyperAllocOnceNoRel(pVM, pIf->cbIOBuffer, 0, MM_TAG_PDM_DEVICE_USER, (void **)&pIf->pbIOBufferR3);
- if (RT_FAILURE(rc))
- return VERR_NO_MEMORY;
- pIf->pbIOBufferR0 = MMHyperR3ToR0(pVM, pIf->pbIOBufferR3);
- pIf->pbIOBufferRC = MMHyperR3ToRC(pVM, pIf->pbIOBufferR3);
- }
-
- /*
- * Init geometry (only for non-CD/DVD media).
- */
- if (pIf->fATAPI)
- {
- pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 2048;
- pIf->PCHSGeometry.cCylinders = 0; /* dummy */
- pIf->PCHSGeometry.cHeads = 0; /* dummy */
- pIf->PCHSGeometry.cSectors = 0; /* dummy */
- LogRel(("AHCI ATA: LUN#%d: CD/DVD, total number of sectors %Ld, passthrough %s\n", pIf->iLUN, pIf->cTotalSectors, (pIf->fATAPIPassthrough ? "enabled" : "disabled")));
- }
- else
- {
- pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 512;
- rc = pIf->pDrvBlockBios->pfnGetPCHSGeometry(pIf->pDrvBlockBios,
- &pIf->PCHSGeometry);
- if (rc == VERR_PDM_MEDIA_NOT_MOUNTED)
- {
- pIf->PCHSGeometry.cCylinders = 0;
- pIf->PCHSGeometry.cHeads = 16; /*??*/
- pIf->PCHSGeometry.cSectors = 63; /*??*/
- }
- else if (rc == VERR_PDM_GEOMETRY_NOT_SET)
- {
- pIf->PCHSGeometry.cCylinders = 0; /* autodetect marker */
- rc = VINF_SUCCESS;
- }
- AssertRC(rc);
-
- if ( pIf->PCHSGeometry.cCylinders == 0
- || pIf->PCHSGeometry.cHeads == 0
- || pIf->PCHSGeometry.cSectors == 0
- )
- {
- uint64_t cCylinders = pIf->cTotalSectors / (16 * 63);
- pIf->PCHSGeometry.cCylinders = RT_MAX(RT_MIN(cCylinders, 16383), 1);
- pIf->PCHSGeometry.cHeads = 16;
- pIf->PCHSGeometry.cSectors = 63;
- /* Set the disk geometry information. */
- rc = pIf->pDrvBlockBios->pfnSetPCHSGeometry(pIf->pDrvBlockBios,
- &pIf->PCHSGeometry);
- }
- LogRel(("AHCI ATA: LUN#%d: disk, PCHS=%u/%u/%u, total number of sectors %Ld\n", pIf->iLUN, pIf->PCHSGeometry.cCylinders, pIf->PCHSGeometry.cHeads, pIf->PCHSGeometry.cSectors, pIf->cTotalSectors));
- }
- return VINF_SUCCESS;
-}
-
-/**
- * Attach command.
- *
- * This is called when we change block driver for the DVD drive.
- *
- * @returns VBox status code.
- * @param pDevIns The device instance.
- * @param iLUN The logical unit which is being detached.
- */
-int ataControllerAttach(PAHCIATACONTROLLER pCtl, PPDMIBASE pDrvBase, bool fMaster)
-{
- AHCIATADevState *pIf;
- int rc = VINF_SUCCESS;
-
- /*
- * Locate the controller and stuff.
- */
- pIf = &pCtl->aIfs[fMaster ? 0 : 1];
-
- /* the usual paranoia */
- AssertRelease(!pIf->pDrvBase);
- AssertRelease(!pIf->pDrvBlock);
- Assert(ATADEVSTATE_2_CONTROLLER(pIf) == pCtl);
-
- /*
- * Try attach the block device and get the interfaces,
- * required as well as optional.
- */
-
- pIf->pDrvBase = pDrvBase;
- if (pDrvBase)
- {
- rc = ataConfigLun(pCtl->pDevInsR3, pIf);
- AssertRC(rc);
-
- if (RT_FAILURE(rc))
- {
- pIf->pDrvBase = NULL;
- pIf->pDrvBlock = NULL;
- }
- }
-
- return rc;
-}
-
-
-/**
- * Resume notification.
- *
- * @returns VBox status.
- * @param pDevIns The device instance data.
- */
-void ataControllerResume(PAHCIATACONTROLLER pCtl)
-{
- int rc;
-
- Log(("%s:\n", __FUNCTION__));
- if (pCtl->fRedo && pCtl->fRedoIdle)
- {
- rc = RTSemEventSignal(pCtl->SuspendIOSem);
- AssertRC(rc);
- }
-
- return;
-}
-
-
-/**
- * Tests if the controller is idle, leaving the PDM notifications on if busy.
- *
- * @returns true if idle, false if idle.
- * @param pCtl the controller instance.
- */
-bool ataControllerIsIdle(PAHCIATACONTROLLER pCtl)
-{
- ASMAtomicWriteBool(&pCtl->fSignalIdle, true);
- if (ataAsyncIOIsIdle(pCtl, false /*fStrict*/))
- {
- ASMAtomicWriteBool(&pCtl->fSignalIdle, false);
- return true;
- }
- return false;
-}
-
-/**
- * Saves a state of the ATA device.
- *
- * @returns VBox status code.
- * @param pCtl Controller instance.
- * @param pSSM The handle to save the state to.
- */
-int ataControllerSaveExec(PAHCIATACONTROLLER pCtl, PSSMHANDLE pSSM)
-{
- SSMR3PutU32(pSSM, ATA_CTL_SAVED_STATE_VERSION);
- SSMR3PutU8(pSSM, pCtl->iSelectedIf);
- SSMR3PutU8(pSSM, pCtl->iAIOIf);
- SSMR3PutU8(pSSM, pCtl->uAsyncIOState);
- SSMR3PutBool(pSSM, pCtl->fChainedTransfer);
- SSMR3PutBool(pSSM, pCtl->fReset);
- SSMR3PutBool(pSSM, pCtl->fRedo);
- SSMR3PutBool(pSSM, pCtl->fRedoIdle);
- SSMR3PutBool(pSSM, pCtl->fRedoDMALastDesc);
- SSMR3PutMem(pSSM, &pCtl->BmDma, sizeof(pCtl->BmDma));
- SSMR3PutGCPhys32(pSSM, pCtl->pFirstDMADesc);
- SSMR3PutGCPhys32(pSSM, pCtl->pLastDMADesc);
- SSMR3PutGCPhys32(pSSM, pCtl->pRedoDMABuffer);
- SSMR3PutU32(pSSM, pCtl->cbRedoDMABuffer);
-
- for (uint32_t j = 0; j < RT_ELEMENTS(pCtl->aIfs); j++)
- {
- SSMR3PutBool(pSSM, pCtl->aIfs[j].fLBA48);
- SSMR3PutBool(pSSM, pCtl->aIfs[j].fATAPI);
- SSMR3PutBool(pSSM, pCtl->aIfs[j].fIrqPending);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].cMultSectors);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].PCHSGeometry.cCylinders);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].PCHSGeometry.cHeads);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].PCHSGeometry.cSectors);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].cSectorsPerIRQ);
- SSMR3PutU64(pSSM, pCtl->aIfs[j].cTotalSectors);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegFeature);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegFeatureHOB);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegError);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegNSector);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegNSectorHOB);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegSector);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegSectorHOB);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegLCyl);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegLCylHOB);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegHCyl);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegHCylHOB);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegSelect);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegStatus);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegCommand);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATARegDevCtl);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uATATransferMode);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].uTxDir);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].iBeginTransfer);
- SSMR3PutU8(pSSM, pCtl->aIfs[j].iSourceSink);
- SSMR3PutBool(pSSM, pCtl->aIfs[j].fDMA);
- SSMR3PutBool(pSSM, pCtl->aIfs[j].fATAPITransfer);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].cbTotalTransfer);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].cbElementaryTransfer);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].iIOBufferCur);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].iIOBufferEnd);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].iIOBufferPIODataStart);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].iIOBufferPIODataEnd);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].iATAPILBA);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].cbATAPISector);
- SSMR3PutMem(pSSM, &pCtl->aIfs[j].aATAPICmd, sizeof(pCtl->aIfs[j].aATAPICmd));
- SSMR3PutMem(pSSM, &pCtl->aIfs[j].abATAPISense, sizeof(pCtl->aIfs[j].abATAPISense));
- SSMR3PutU8(pSSM, pCtl->aIfs[j].cNotifiedMediaChange);
- SSMR3PutU32(pSSM, pCtl->aIfs[j].MediaEventStatus);
-
- PDMLED Led;
- memset(&Led, 0, sizeof(PDMLED));
- SSMR3PutMem(pSSM, &Led, sizeof(PDMLED));
- SSMR3PutU32(pSSM, pCtl->aIfs[j].cbIOBuffer);
- if (pCtl->aIfs[j].cbIOBuffer)
- SSMR3PutMem(pSSM, pCtl->aIfs[j].CTX_SUFF(pbIOBuffer), pCtl->aIfs[j].cbIOBuffer);
- else
- Assert(pCtl->aIfs[j].CTX_SUFF(pbIOBuffer) == NULL);
- }
-
- return SSMR3PutU32(pSSM, ~0); /* sanity/terminator */
-}
-
-
-/**
- * Loads a saved ATA device state.
- *
- * @returns VBox status code.
- * @param pDevIns The device instance.
- * @param pSSM The handle to the saved state.
- */
-int ataControllerLoadExec(PAHCIATACONTROLLER pCtl, PSSMHANDLE pSSM)
-{
- int rc;
- uint32_t u32Version;
- uint32_t u32;
-
- /* Test for correct version. */
- rc = SSMR3GetU32(pSSM, &u32Version);
- AssertRCReturn(rc, rc);
-
- if ( u32Version != ATA_CTL_SAVED_STATE_VERSION
- && u32Version != ATA_CTL_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE
- && u32Version != ATA_CTL_SAVED_STATE_VERSION_WITHOUT_EVENT_STATUS)
- {
- AssertMsgFailed(("u32Version=%d\n", u32Version));
- return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
- }
-
- /* integrity check */
- if (!ataAsyncIOIsIdle(pCtl, false))
- {
- AssertMsgFailed(("Async I/O for controller is active\n"));
- return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
- }
-
- SSMR3GetU8(pSSM, &pCtl->iSelectedIf);
- SSMR3GetU8(pSSM, &pCtl->iAIOIf);
- SSMR3GetU8(pSSM, &pCtl->uAsyncIOState);
- SSMR3GetBool(pSSM, &pCtl->fChainedTransfer);
- SSMR3GetBool(pSSM, (bool *)&pCtl->fReset);
- SSMR3GetBool(pSSM, (bool *)&pCtl->fRedo);
- SSMR3GetBool(pSSM, (bool *)&pCtl->fRedoIdle);
- SSMR3GetBool(pSSM, (bool *)&pCtl->fRedoDMALastDesc);
- SSMR3GetMem(pSSM, &pCtl->BmDma, sizeof(pCtl->BmDma));
- SSMR3GetGCPhys32(pSSM, &pCtl->pFirstDMADesc);
- SSMR3GetGCPhys32(pSSM, &pCtl->pLastDMADesc);
- SSMR3GetGCPhys32(pSSM, &pCtl->pRedoDMABuffer);
- SSMR3GetU32(pSSM, &pCtl->cbRedoDMABuffer);
-
- for (uint32_t j = 0; j < RT_ELEMENTS(pCtl->aIfs); j++)
- {
- SSMR3GetBool(pSSM, &pCtl->aIfs[j].fLBA48);
- SSMR3GetBool(pSSM, &pCtl->aIfs[j].fATAPI);
- SSMR3GetBool(pSSM, &pCtl->aIfs[j].fIrqPending);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].cMultSectors);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].PCHSGeometry.cCylinders);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].PCHSGeometry.cHeads);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].PCHSGeometry.cSectors);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].cSectorsPerIRQ);
- SSMR3GetU64(pSSM, &pCtl->aIfs[j].cTotalSectors);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegFeature);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegFeatureHOB);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegError);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegNSector);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegNSectorHOB);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegSector);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegSectorHOB);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegLCyl);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegLCylHOB);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegHCyl);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegHCylHOB);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegSelect);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegStatus);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegCommand);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATARegDevCtl);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uATATransferMode);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].uTxDir);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].iBeginTransfer);
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].iSourceSink);
- SSMR3GetBool(pSSM, &pCtl->aIfs[j].fDMA);
- SSMR3GetBool(pSSM, &pCtl->aIfs[j].fATAPITransfer);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].cbTotalTransfer);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].cbElementaryTransfer);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].iIOBufferCur);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].iIOBufferEnd);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].iIOBufferPIODataStart);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].iIOBufferPIODataEnd);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].iATAPILBA);
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].cbATAPISector);
- SSMR3GetMem(pSSM, &pCtl->aIfs[j].aATAPICmd, sizeof(pCtl->aIfs[j].aATAPICmd));
- if (u32Version > ATA_CTL_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE)
- {
- SSMR3GetMem(pSSM, &pCtl->aIfs[j].abATAPISense, sizeof(pCtl->aIfs[j].abATAPISense));
- }
- else
- {
- uint8_t uATAPISenseKey, uATAPIASC;
- memset(pCtl->aIfs[j].abATAPISense, '\0', sizeof(pCtl->aIfs[j].abATAPISense));
- pCtl->aIfs[j].abATAPISense[0] = 0x70 | (1 << 7);
- pCtl->aIfs[j].abATAPISense[7] = 10;
- SSMR3GetU8(pSSM, &uATAPISenseKey);
- SSMR3GetU8(pSSM, &uATAPIASC);
- pCtl->aIfs[j].abATAPISense[2] = uATAPISenseKey & 0x0f;
- pCtl->aIfs[j].abATAPISense[12] = uATAPIASC;
- }
- /** @todo triple-check this hack after passthrough is working */
- SSMR3GetU8(pSSM, &pCtl->aIfs[j].cNotifiedMediaChange);
- if (u32Version > ATA_CTL_SAVED_STATE_VERSION_WITHOUT_EVENT_STATUS)
- SSMR3GetU32(pSSM, (uint32_t*)&pCtl->aIfs[j].MediaEventStatus);
- else
- pCtl->aIfs[j].MediaEventStatus = ATA_EVENT_STATUS_UNCHANGED;
-
- PDMLED Led;
- SSMR3GetMem(pSSM, &Led, sizeof(PDMLED));
- SSMR3GetU32(pSSM, &pCtl->aIfs[j].cbIOBuffer);
- if (pCtl->aIfs[j].cbIOBuffer)
- {
- if (pCtl->aIfs[j].CTX_SUFF(pbIOBuffer))
- SSMR3GetMem(pSSM, pCtl->aIfs[j].CTX_SUFF(pbIOBuffer), pCtl->aIfs[j].cbIOBuffer);
- else
- {
- LogRel(("AHCI ATA: No buffer for %d\n", j));
- if (SSMR3HandleGetAfter(pSSM) != SSMAFTER_DEBUG_IT)
- return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("No buffer for %d"), j);
-
- /* skip the buffer if we're loading for the debugger / animator. */
- uint8_t u8Ignored;
- size_t cbLeft = pCtl->aIfs[j].cbIOBuffer;
- while (cbLeft-- > 0)
- SSMR3GetU8(pSSM, &u8Ignored);
- }
- }
- else
- Assert(pCtl->aIfs[j].CTX_SUFF(pbIOBuffer) == NULL);
- }
-
- rc = SSMR3GetU32(pSSM, &u32);
- if (RT_FAILURE(rc))
- return rc;
- if (u32 != ~0U)
- {
- AssertMsgFailed(("u32=%#x expected ~0\n", u32));
- rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
- return rc;
- }
-
- return VINF_SUCCESS;
-}
-
-int ataControllerInit(PPDMDEVINS pDevIns, PAHCIATACONTROLLER pCtl,
- PPDMIMEDIANOTIFY pMediaNotify,
- unsigned iLUNMaster, PPDMIBASE pDrvBaseMaster, PPDMLED pLedMaster,
- PSTAMCOUNTER pStatBytesReadMaster, PSTAMCOUNTER pStatBytesWrittenMaster,
- const char *pszSerialNumberMaster, const char *pszFirmwareRevisionMaster,
- const char *pszModelNumberMaster, const char *pszInquiryVendorIdMaster,
- const char *pszInquiryProductIdMaster, const char *pszInquiryRevisionMaster,
- bool fNonRotationalMaster,
- unsigned iLUNSlave, PPDMIBASE pDrvBaseSlave, PPDMLED pLedSlave,
- PSTAMCOUNTER pStatBytesReadSlave, PSTAMCOUNTER pStatBytesWrittenSlave,
- const char *pszSerialNumberSlave, const char *pszFirmwareRevisionSlave,
- const char *pszModelNumberSlave, const char *pszInquiryVendorIdSlave,
- const char *pszInquiryProductIdSlave, const char *pszInquiryRevisionSlave,
- bool fNonRotationalSlave,
- uint32_t *pcbSSMState, const char *szName)
-{
- int rc;
-
- AssertMsg(pcbSSMState, ("pcbSSMState is invalid\n"));
-
- pCtl->pDevInsR3 = pDevIns;
- pCtl->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
- pCtl->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
- pCtl->pMediaNotify = pMediaNotify;
- pCtl->AsyncIOSem = NIL_RTSEMEVENT;
- pCtl->SuspendIOSem = NIL_RTSEMEVENT;
- pCtl->AsyncIORequestMutex = NIL_RTSEMMUTEX;
- pCtl->AsyncIOThread = NIL_RTTHREAD;
-
- for (uint32_t j = 0; j < RT_ELEMENTS(pCtl->aIfs); j++)
- {
- pCtl->aIfs[j].iLUN = j == 0 ? iLUNMaster : iLUNSlave;
- pCtl->aIfs[j].pDevInsR3 = pDevIns;
- pCtl->aIfs[j].pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
- pCtl->aIfs[j].pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
- pCtl->aIfs[j].pControllerR3 = pCtl;
- pCtl->aIfs[j].pControllerR0 = MMHyperR3ToR0(PDMDevHlpGetVM(pDevIns), pCtl);
- pCtl->aIfs[j].pControllerRC = MMHyperR3ToRC(PDMDevHlpGetVM(pDevIns), pCtl);
- pCtl->aIfs[j].pLed = j == 0 ? pLedMaster : pLedSlave;
- pCtl->aIfs[j].pStatBytesRead = j == 0 ? pStatBytesReadMaster : pStatBytesReadSlave;
- pCtl->aIfs[j].pStatBytesWritten = j == 0 ? pStatBytesWrittenMaster : pStatBytesWrittenSlave;
- pCtl->aIfs[j].pszSerialNumber = j == 0 ? pszSerialNumberMaster : pszSerialNumberSlave;
- pCtl->aIfs[j].pszFirmwareRevision = j == 0 ? pszFirmwareRevisionMaster : pszFirmwareRevisionSlave;
- pCtl->aIfs[j].pszModelNumber = j == 0 ? pszModelNumberMaster : pszModelNumberSlave;
- pCtl->aIfs[j].pszInquiryVendorId = j == 0 ? pszInquiryVendorIdMaster : pszInquiryVendorIdSlave;
- pCtl->aIfs[j].pszInquiryProductId = j == 0 ? pszInquiryProductIdMaster : pszInquiryProductIdSlave;
- pCtl->aIfs[j].pszInquiryRevision = j == 0 ? pszInquiryRevisionMaster : pszInquiryRevisionSlave;
- pCtl->aIfs[j].fNonRotational = j == 0 ? fNonRotationalMaster : fNonRotationalSlave;
- }
-
- /* Initialize per-controller critical section */
- rc = PDMDevHlpCritSectInit(pDevIns, &pCtl->lock, RT_SRC_POS, "%s", szName);
- if (RT_FAILURE(rc))
- return PDMDEV_SET_ERROR(pDevIns, rc, N_("AHCI ATA: cannot initialize critical section"));
-
- /*
- * Attach the units.
- */
- uint32_t cbTotalBuffer = 0;
-
- /*
- * Start the worker thread.
- */
- pCtl->uAsyncIOState = AHCIATA_AIO_NEW;
- rc = RTSemEventCreate(&pCtl->AsyncIOSem);
- AssertRCReturn(rc, rc);
- rc = RTSemEventCreate(&pCtl->SuspendIOSem);
- AssertRCReturn(rc, rc);
- rc = RTSemMutexCreate(&pCtl->AsyncIORequestMutex);
- AssertRCReturn(rc, rc);
- ataAsyncIOClearRequests(pCtl);
- rc = RTThreadCreateF(&pCtl->AsyncIOThread, ataAsyncIOLoop, (void *)pCtl, 128*1024,
- RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "AHCI-ATA-%u", pCtl->irq);
- AssertRCReturn(rc, rc);
- Assert(pCtl->AsyncIOThread != NIL_RTTHREAD && pCtl->AsyncIOSem != NIL_RTSEMEVENT && pCtl->SuspendIOSem != NIL_RTSEMEVENT && pCtl->AsyncIORequestMutex != NIL_RTSEMMUTEX);
- Log(("%s: controller AIO thread id %#x; sem %p susp_sem %p mutex %p\n", __FUNCTION__, pCtl->AsyncIOThread, pCtl->AsyncIOSem, pCtl->SuspendIOSem, pCtl->AsyncIORequestMutex));
-
- for (uint32_t j = 0; j < RT_ELEMENTS(pCtl->aIfs); j++)
- {
- /*
- * Try attach the block device and get the interfaces,
- * required as well as optional.
- */
- AHCIATADevState *pIf = &pCtl->aIfs[j];
-
- pIf->pDrvBase = (j == 0) ? pDrvBaseMaster : pDrvBaseSlave;
-
-#if 0
- rc = PDMDevHlpDriverAttach(pDevIns, pIf->iLUN, &pIf->IBase, &pIf->pDrvBase, s_apszDescs[j]);
- if (RT_SUCCESS(rc))
-#endif
- if (pIf->pDrvBase)
- rc = ataConfigLun(pDevIns, pIf);
- else
- {
- pIf->pDrvBase = NULL;
- pIf->pDrvBlock = NULL;
- pIf->cbIOBuffer = 0;
- pIf->pbIOBufferR3 = NULL;
- pIf->pbIOBufferR0 = NIL_RTR0PTR;
- pIf->pbIOBufferRC = NIL_RTRCPTR;
- LogRel(("AHCI ATA: LUN#%d: no unit\n", pIf->iLUN));
- }
- cbTotalBuffer += pIf->cbIOBuffer;
- }
-
- *pcbSSMState = cbTotalBuffer;
-
- /*
- * Initialize the device state.
- */
- ataControllerReset(pCtl);
-
- return VINF_SUCCESS;
-}
-#endif /* IN_RING3 */
-#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
diff --git a/src/VBox/Devices/Storage/ATAController.h b/src/VBox/Devices/Storage/ATAController.h
deleted file mode 100644
index 12addeb5..00000000
--- a/src/VBox/Devices/Storage/ATAController.h
+++ /dev/null
@@ -1,563 +0,0 @@
-/* $Id: ATAController.h $ */
-/** @file
- * DevATA, DevAHCI - Shared ATA/ATAPI controller types.
- */
-
-/*
- * Copyright (C) 2006-2011 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- */
-
-#ifndef ___Storage_ATAController_h
-#define ___Storage_ATAController_h
-
-/*******************************************************************************
-* Header Files *
-*******************************************************************************/
-#include <VBox/vmm/pdmdev.h>
-#ifdef IN_RING3
-# include <iprt/semaphore.h>
-# include <iprt/thread.h>
-#endif /* IN_RING3 */
-#include <iprt/critsect.h>
-#include <VBox/vmm/stam.h>
-
-#include "PIIX3ATABmDma.h"
-#include "ide.h"
-
-
-/*******************************************************************************
-* Defined Constants And Macros *
-*******************************************************************************/
-/**
- * Maximum number of sectors to transfer in a READ/WRITE MULTIPLE request.
- * Set to 1 to disable multi-sector read support. According to the ATA
- * specification this must be a power of 2 and it must fit in an 8 bit
- * value. Thus the only valid values are 1, 2, 4, 8, 16, 32, 64 and 128.
- */
-#define ATA_MAX_MULT_SECTORS 128
-
-/**
- * Fastest PIO mode supported by the drive.
- */
-#define ATA_PIO_MODE_MAX 4
-/**
- * Fastest MDMA mode supported by the drive.
- */
-#define ATA_MDMA_MODE_MAX 2
-/**
- * Fastest UDMA mode supported by the drive.
- */
-#define ATA_UDMA_MODE_MAX 6
-
-/** ATAPI sense info size. */
-#define ATAPI_SENSE_SIZE 64
-
-/** The maximum number of release log entries per device. */
-#define MAX_LOG_REL_ERRORS 1024
-
-/* MediaEventStatus */
-#define ATA_EVENT_STATUS_UNCHANGED 0 /**< medium event status not changed */
-#define ATA_EVENT_STATUS_MEDIA_NEW 1 /**< new medium inserted */
-#define ATA_EVENT_STATUS_MEDIA_REMOVED 2 /**< medium removed */
-#define ATA_EVENT_STATUS_MEDIA_CHANGED 3 /**< medium was removed + new medium was inserted */
-#define ATA_EVENT_STATUS_MEDIA_EJECT_REQUESTED 4 /**< medium eject requested (eject button pressed) */
-
-
-/*******************************************************************************
-* Structures and Typedefs *
-*******************************************************************************/
-typedef struct AHCIATADevState {
- /** Flag indicating whether the current command uses LBA48 mode. */
- bool fLBA48;
- /** Flag indicating whether this drive implements the ATAPI command set. */
- bool fATAPI;
- /** Set if this interface has asserted the IRQ. */
- bool fIrqPending;
- /** Currently configured number of sectors in a multi-sector transfer. */
- uint8_t cMultSectors;
- /** PCHS disk geometry. */
- PDMMEDIAGEOMETRY PCHSGeometry;
- /** Total number of sectors on this disk. */
- uint64_t cTotalSectors;
- /** Number of sectors to transfer per IRQ. */
- uint32_t cSectorsPerIRQ;
-
- /** ATA/ATAPI register 1: feature (write-only). */
- uint8_t uATARegFeature;
- /** ATA/ATAPI register 1: feature, high order byte. */
- uint8_t uATARegFeatureHOB;
- /** ATA/ATAPI register 1: error (read-only). */
- uint8_t uATARegError;
- /** ATA/ATAPI register 2: sector count (read/write). */
- uint8_t uATARegNSector;
- /** ATA/ATAPI register 2: sector count, high order byte. */
- uint8_t uATARegNSectorHOB;
- /** ATA/ATAPI register 3: sector (read/write). */
- uint8_t uATARegSector;
- /** ATA/ATAPI register 3: sector, high order byte. */
- uint8_t uATARegSectorHOB;
- /** ATA/ATAPI register 4: cylinder low (read/write). */
- uint8_t uATARegLCyl;
- /** ATA/ATAPI register 4: cylinder low, high order byte. */
- uint8_t uATARegLCylHOB;
- /** ATA/ATAPI register 5: cylinder high (read/write). */
- uint8_t uATARegHCyl;
- /** ATA/ATAPI register 5: cylinder high, high order byte. */
- uint8_t uATARegHCylHOB;
- /** ATA/ATAPI register 6: select drive/head (read/write). */
- uint8_t uATARegSelect;
- /** ATA/ATAPI register 7: status (read-only). */
- uint8_t uATARegStatus;
- /** ATA/ATAPI register 7: command (write-only). */
- uint8_t uATARegCommand;
- /** ATA/ATAPI drive control register (write-only). */
- uint8_t uATARegDevCtl;
-
- /** Currently active transfer mode (MDMA/UDMA) and speed. */
- uint8_t uATATransferMode;
- /** Current transfer direction. */
- uint8_t uTxDir;
- /** Index of callback for begin transfer. */
- uint8_t iBeginTransfer;
- /** Index of callback for source/sink of data. */
- uint8_t iSourceSink;
- /** Flag indicating whether the current command transfers data in DMA mode. */
- bool fDMA;
- /** Set to indicate that ATAPI transfer semantics must be used. */
- bool fATAPITransfer;
-
- /** Total ATA/ATAPI transfer size, shared PIO/DMA. */
- uint32_t cbTotalTransfer;
- /** Elementary ATA/ATAPI transfer size, shared PIO/DMA. */
- uint32_t cbElementaryTransfer;
- /** Current read/write buffer position, shared PIO/DMA. */
- uint32_t iIOBufferCur;
- /** First element beyond end of valid buffer content, shared PIO/DMA. */
- uint32_t iIOBufferEnd;
-
- /** ATA/ATAPI current PIO read/write transfer position. Not shared with DMA for safety reasons. */
- uint32_t iIOBufferPIODataStart;
- /** ATA/ATAPI current PIO read/write transfer end. Not shared with DMA for safety reasons. */
- uint32_t iIOBufferPIODataEnd;
-
- /** ATAPI current LBA position. */
- uint32_t iATAPILBA;
- /** ATAPI current sector size. */
- uint32_t cbATAPISector;
- /** ATAPI current command. */
- uint8_t aATAPICmd[ATAPI_PACKET_SIZE];
- /** ATAPI sense data. */
- uint8_t abATAPISense[ATAPI_SENSE_SIZE];
- /** HACK: Countdown till we report a newly unmounted drive as mounted. */
- uint8_t cNotifiedMediaChange;
- /** The same for GET_EVENT_STATUS for mechanism */
- volatile uint32_t MediaEventStatus;
-
- /** The status LED state for this drive. */
- R3PTRTYPE(PPDMLED) pLed;
-#if HC_ARCH_BITS == 64
- uint32_t uAlignment3;
-#endif
-
- /** Size of I/O buffer. */
- uint32_t cbIOBuffer;
- /** Pointer to the I/O buffer. */
- R3PTRTYPE(uint8_t *) pbIOBufferR3;
- /** Pointer to the I/O buffer. */
- R0PTRTYPE(uint8_t *) pbIOBufferR0;
- /** Pointer to the I/O buffer. */
- RCPTRTYPE(uint8_t *) pbIOBufferRC;
-
- RTRCPTR Aligmnent1; /**< Align the statistics at an 8-byte boundary. */
-
- /*
- * No data that is part of the saved state after this point!!!!!
- */
-
- /* Release statistics: number of ATA DMA commands. */
- STAMCOUNTER StatATADMA;
- /* Release statistics: number of ATA PIO commands. */
- STAMCOUNTER StatATAPIO;
- /* Release statistics: number of ATAPI PIO commands. */
- STAMCOUNTER StatATAPIDMA;
- /* Release statistics: number of ATAPI PIO commands. */
- STAMCOUNTER StatATAPIPIO;
-#ifdef VBOX_INSTRUMENT_DMA_WRITES
- /* Release statistics: number of DMA sector writes and the time spent. */
- STAMPROFILEADV StatInstrVDWrites;
-#endif
-
- /** Statistics: number of read operations and the time spent reading. */
- STAMPROFILEADV StatReads;
- /** Statistics: number of bytes read. */
- R3PTRTYPE(PSTAMCOUNTER) pStatBytesRead;
-#if HC_ARCH_BITS == 64
- uint64_t uAlignment4;
-#endif
- /** Statistics: number of write operations and the time spent writing. */
- STAMPROFILEADV StatWrites;
- /** Statistics: number of bytes written. */
- R3PTRTYPE(PSTAMCOUNTER) pStatBytesWritten;
-#if HC_ARCH_BITS == 64
- uint64_t uAlignment5;
-#endif
- /** Statistics: number of flush operations and the time spend flushing. */
- STAMPROFILE StatFlushes;
-
- /** The serial number to use for IDENTIFY DEVICE commands. */
- R3PTRTYPE(const char *) pszSerialNumber;
- /** The firmware revision to use for IDENTIFY DEVICE commands. */
- R3PTRTYPE(const char *) pszFirmwareRevision;
- /** The model number to use for IDENTIFY DEVICE commands. */
- R3PTRTYPE(const char *) pszModelNumber;
- /** The vendor identification string for SCSI INQUIRY commands. */
- R3PTRTYPE(const char *) pszInquiryVendorId;
- /** The product identification string for SCSI INQUIRY commands. */
- R3PTRTYPE(const char *) pszInquiryProductId;
- /** The revision string for SCSI INQUIRY commands. */
- R3PTRTYPE(const char *) pszInquiryRevision;
- /** Mark the drive as having a non-rotational medium (i.e. as a SSD). */
- bool fNonRotational;
- /** Enable passing through commands directly to the ATAPI drive. */
- bool fATAPIPassthrough;
- /** Number of errors we've reported to the release log.
- * This is to prevent flooding caused by something going horribly wrong.
- * this value against MAX_LOG_REL_ERRORS in places likely to cause floods
- * like the ones we currently seeing on the linux smoke tests (2006-11-10). */
- uint32_t cErrors;
- /** Timestamp of last started command. 0 if no command pending. */
- uint64_t u64CmdTS;
-
- /** Pointer to the attached driver's base interface. */
- R3PTRTYPE(PPDMIBASE) pDrvBase;
- /** Pointer to the attached driver's block interface. */
- R3PTRTYPE(PPDMIBLOCK) pDrvBlock;
- /** Pointer to the attached driver's block bios interface. */
- R3PTRTYPE(PPDMIBLOCKBIOS) pDrvBlockBios;
- /** Pointer to the attached driver's mount interface.
- * This is NULL if the driver isn't a removable unit. */
- R3PTRTYPE(PPDMIMOUNT) pDrvMount;
- /** The base interface. */
- PDMIBASE IBase;
- /** The block port interface. */
- PDMIBLOCKPORT IPort;
- /** The mount notify interface. */
- PDMIMOUNTNOTIFY IMountNotify;
- /** The LUN #. */
- RTUINT iLUN;
-#if HC_ARCH_BITS == 64
- RTUINT Alignment2; /**< Align pDevInsR3 correctly. */
-#endif
- /** Pointer to device instance. */
- PPDMDEVINSR3 pDevInsR3;
- /** Pointer to controller instance. */
- R3PTRTYPE(struct AHCIATACONTROLLER *) pControllerR3;
- /** Pointer to device instance. */
- PPDMDEVINSR0 pDevInsR0;
- /** Pointer to controller instance. */
- R0PTRTYPE(struct AHCIATACONTROLLER *) pControllerR0;
- /** Pointer to device instance. */
- PPDMDEVINSRC pDevInsRC;
- /** Pointer to controller instance. */
- RCPTRTYPE(struct AHCIATACONTROLLER *) pControllerRC;
-} AHCIATADevState;
-
-
-typedef struct AHCIATATransferRequest
-{
- uint8_t iIf;
- uint8_t iBeginTransfer;
- uint8_t iSourceSink;
- uint32_t cbTotalTransfer;
- uint8_t uTxDir;
-} AHCIATATransferRequest;
-
-
-typedef struct AHCIATAAbortRequest
-{
- uint8_t iIf;
- bool fResetDrive;
-} AHCIATAAbortRequest;
-
-
-typedef enum
-{
- /** Begin a new transfer. */
- AHCIATA_AIO_NEW = 0,
- /** Continue a DMA transfer. */
- AHCIATA_AIO_DMA,
- /** Continue a PIO transfer. */
- AHCIATA_AIO_PIO,
- /** Reset the drives on current controller, stop all transfer activity. */
- AHCIATA_AIO_RESET_ASSERTED,
- /** Reset the drives on current controller, resume operation. */
- AHCIATA_AIO_RESET_CLEARED,
- /** Abort the current transfer of a particular drive. */
- AHCIATA_AIO_ABORT
-} AHCIATAAIO;
-
-
-typedef struct AHCIATARequest
-{
- AHCIATAAIO ReqType;
- union
- {
- AHCIATATransferRequest t;
- AHCIATAAbortRequest a;
- } u;
-} AHCIATARequest;
-
-
-typedef struct AHCIATACONTROLLER
-{
- /** The base of the first I/O Port range. */
- RTIOPORT IOPortBase1;
- /** The base of the second I/O Port range. (0 if none) */
- RTIOPORT IOPortBase2;
- /** The assigned IRQ. */
- RTUINT irq;
- /** Access critical section */
- PDMCRITSECT lock;
-
- /** Selected drive. */
- uint8_t iSelectedIf;
- /** The interface on which to handle async I/O. */
- uint8_t iAIOIf;
- /** The state of the async I/O thread. */
- uint8_t uAsyncIOState;
- /** Flag indicating whether the next transfer is part of the current command. */
- bool fChainedTransfer;
- /** Set when the reset processing is currently active on this controller. */
- bool fReset;
- /** Flag whether the current transfer needs to be redone. */
- bool fRedo;
- /** Flag whether the redo suspend has been finished. */
- bool fRedoIdle;
- /** Flag whether the DMA operation to be redone is the final transfer. */
- bool fRedoDMALastDesc;
- /** The BusMaster DMA state. */
- BMDMAState BmDma;
- /** Pointer to first DMA descriptor. */
- RTGCPHYS32 pFirstDMADesc;
- /** Pointer to last DMA descriptor. */
- RTGCPHYS32 pLastDMADesc;
- /** Pointer to current DMA buffer (for redo operations). */
- RTGCPHYS32 pRedoDMABuffer;
- /** Size of current DMA buffer (for redo operations). */
- uint32_t cbRedoDMABuffer;
-
- /** The ATA/ATAPI interfaces of this controller. */
- AHCIATADevState aIfs[2];
-
- /** Pointer to device instance. */
- PPDMDEVINSR3 pDevInsR3;
- /** Pointer to device instance. */
- PPDMDEVINSR0 pDevInsR0;
- /** Pointer to device instance. */
- PPDMDEVINSRC pDevInsRC;
-
- /** Set when the destroying the device instance and the thread must exit. */
- uint32_t volatile fShutdown;
- /** The async I/O thread handle. NIL_RTTHREAD if no thread. */
- RTTHREAD AsyncIOThread;
- /** The event semaphore the thread is waiting on for requests. */
- RTSEMEVENT AsyncIOSem;
- /** The request queue for the AIO thread. One element is always unused. */
- AHCIATARequest aAsyncIORequests[4];
- /** The position at which to insert a new request for the AIO thread. */
- uint8_t AsyncIOReqHead;
- /** The position at which to get a new request for the AIO thread. */
- uint8_t AsyncIOReqTail;
- /** Whether to call RTThreadUserSignal and PDMDevHlpAsyncNotificationCompleted
- * when idle. Before setting this, call RTThreadUserReset. */
- bool volatile fSignalIdle;
- uint8_t Alignment3[1]; /**< Explicit padding of the 1 byte gap. */
- /** Magic delay before triggering interrupts in DMA mode. */
- uint32_t DelayIRQMillies;
- /** The mutex protecting the request queue. */
- RTSEMMUTEX AsyncIORequestMutex;
- /** The event semaphore the thread is waiting on during suspended I/O. */
- RTSEMEVENT SuspendIOSem;
- /** Pointer to Media Notify interface. */
- R3PTRTYPE(PPDMIMEDIANOTIFY) pMediaNotify;
-#if HC_ARCH_BITS == 32
- uint32_t Alignment0;
-#endif
-
- /* Statistics */
- STAMCOUNTER StatAsyncOps;
- uint64_t StatAsyncMinWait;
- uint64_t StatAsyncMaxWait;
- STAMCOUNTER StatAsyncTimeUS;
- STAMPROFILEADV StatAsyncTime;
- STAMPROFILE StatLockWait;
-} AHCIATACONTROLLER, *PAHCIATACONTROLLER;
-
-#ifndef VBOX_DEVICE_STRUCT_TESTCASE
-
-#define ATADEVSTATE_2_CONTROLLER(pIf) ( (pIf)->CTX_SUFF(pController) )
-#define ATADEVSTATE_2_DEVINS(pIf) ( (pIf)->CTX_SUFF(pDevIns) )
-#define CONTROLLER_2_DEVINS(pController) ( (pController)->CTX_SUFF(pDevIns) )
-#define PDMIBASE_2_ATASTATE(pInterface) ( (AHCIATADevState *)((uintptr_t)(pInterface) - RT_OFFSETOF(AHCIATADevState, IBase)) )
-
-
-/*******************************************************************************
- * Internal Functions *
- ******************************************************************************/
-RT_C_DECLS_BEGIN
-int ataControllerIOPortWrite1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb);
-int ataControllerIOPortRead1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *u32, unsigned cb);
-int ataControllerIOPortWriteStr1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb);
-int ataControllerIOPortReadStr1(PAHCIATACONTROLLER pCtl, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb);
-int ataControllerIOPortWrite2(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb);
-int ataControllerIOPortRead2(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *u32, unsigned cb);
-int ataControllerBMDMAIOPortRead(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t *pu32, unsigned cb);
-int ataControllerBMDMAIOPortWrite(PAHCIATACONTROLLER pCtl, RTIOPORT Port, uint32_t u32, unsigned cb);
-RT_C_DECLS_END
-
-#ifdef IN_RING3
-/**
- * Initialize a controller state.
- *
- * @returns VBox status code.
- * @param pDevIns Pointer to the device instance which creates a controller.
- * @param pCtl Pointer to the unitialized ATA controller structure.
- * @param pMediaNotify Pointer to PDM interface for media eject.
- * @param iLUNMaster Port number of the master device.
- * @param pDrvBaseMaster Pointer to the base driver interface which acts as the master.
- * @param pLedMaster Pointer to LED state for master device.
- * @param pStatBytesReadMaster Pointer to statistics structure for reads.
- * @param pStatBytesWrittenMaster Pointer to statistics structure for writes.
- * @param pszSerialNumberMaster VPD serial number for master.
- * @param pszFirmwareRevisionMaster VPD firmware revision for master
- * @param pszModelNumberMaster VPD model number for master
- * @param pszInquiryVendorIdMaster VPD vendor ID for master
- * @param pszInquiryProductIdMaster VPD product ID for master
- * @param pszInquiryRevisionMaster VPD revision for master
- * @param fNonRotationalMaster Flag for non-rotational media.
- * @param iLUNSlave Port number of the slave device.
- * @param pDrvBaseSlave Pointer to the base driver interface which acts as the slave.
- * @param pLedSlave Pointer to LED state for slave device.
- * @param pStatBytesReadSlave Pointer to statistics structure for reads.
- * @param pStatBytesWrittenSlave Pointer to statistics structure for writes.
- * @param pszSerialNumberSlave VPD serial number for slave.
- * @param pszFirmwareRevisionSlave VPD firmware revision for slave
- * @param pszModelNumberSlave VPD model number for slave
- * @param pszInquiryVendorIdSlave VPD vendor ID for slave
- * @param pszInquiryProductIdSlave VPD product ID for slave
- * @param pszInquiryRevisionSlave VPD revision for slave
- * @param fNonRotationalSlave Flag for non-rotational media.
- * @param pcbSSMState Where to store the size of the device state for loading/saving.
- * @param szName Name of the controller (Used to initialize the critical section).
- */
-int ataControllerInit(PPDMDEVINS pDevIns, PAHCIATACONTROLLER pCtl,
- PPDMIMEDIANOTIFY pMediaNotify,
- unsigned iLUNMaster, PPDMIBASE pDrvBaseMaster, PPDMLED pLedMaster,
- PSTAMCOUNTER pStatBytesReadMaster, PSTAMCOUNTER pStatBytesWrittenMaster,
- const char *pszSerialNumberMaster, const char *pszFirmwareRevisionMaster,
- const char *pszModelNumberMaster, const char *pszInquiryVendorIdMaster,
- const char *pszInquiryProductIdMaster, const char *pszInquiryRevisionMaster,
- bool fNonRotationalMaster,
- unsigned iLUNSlave, PPDMIBASE pDrvBaseSlave, PPDMLED pLedSlave,
- PSTAMCOUNTER pStatBytesReadSlave, PSTAMCOUNTER pStatBytesWrittenSlave,
- const char *pszSerialNumberSlave, const char *pszFirmwareRevisionSlave,
- const char *pszModelNumberSlave, const char *pszInquiryVendorIdSlave,
- const char *pszInquiryProductIdSlave, const char *pszInquiryRevisionSlave,
- bool fNonRotationalSlave,
- uint32_t *pcbSSMState, const char *szName);
-
-/**
- * Free all allocated resources for one controller instance.
- *
- * @returns VBox status code.
- * @param pCtl The controller instance.
- */
-int ataControllerDestroy(PAHCIATACONTROLLER pCtl);
-
-/**
- * Tests if the controller is idle, leaving the PDM notifications on if busy.
- *
- * @returns true if idle, false if idle.
- * @param pCtl the controller instance.
- */
-bool ataControllerIsIdle(PAHCIATACONTROLLER pCtl);
-
-/**
- * Reset a controller instance to an initial state.
- *
- * @returns VBox status code.
- * @param pCtl Pointer to the controller.
- */
-void ataControllerReset(PAHCIATACONTROLLER pCtl);
-
-/**
- * Resume operation of an controller.
- *
- * @returns nothing
- * @param pCtl The controller instance.
- */
-
-void ataControllerResume(PAHCIATACONTROLLER pCtl);
-
-/**
- * Relocate necessary pointers.
- *
- * @returns nothing.
- * @param pCtl The controller instance.
- * @param offDelta The relocation delta relative to the old location.
- */
-void ataControllerRelocate(PAHCIATACONTROLLER pCtl, RTGCINTPTR offDelta);
-
-/**
- * Execute state save operation.
- *
- * @returns VBox status code.
- * @param pCtl The controller instance.
- * @param pSSM SSM operation handle.
- */
-int ataControllerSaveExec(PAHCIATACONTROLLER pCtl, PSSMHANDLE pSSM);
-
-/**
- * Excute state load operation.
- *
- * @returns VBox status code.
- * @param pCtl The controller instance.
- * @param pSSM SSM operation handle.
- */
-int ataControllerLoadExec(PAHCIATACONTROLLER pCtl, PSSMHANDLE pSSM);
-
-/**
- * Attach command.
- *
- * This is called when we change block driver for the DVD drive.
- *
- * @returns VBox status code.
- * @param pDevIns The device instance.
- * @param iLUN The logical unit which is being detached.
- */
-int ataControllerAttach(PAHCIATACONTROLLER pCtl, PPDMIBASE pDrvBase, bool fMaster);
-
-/**
- * Detach notification.
- *
- * The DVD drive has been unplugged.
- *
- * @param pDevIns The device instance.
- * @param fMaster True if the master is detached
- * false for the slave
- */
-void ataControllerDetach(PAHCIATACONTROLLER pCtl, bool fMaster);
-
-#endif /* IN_RING3 */
-
-#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
-#endif /* !___Storage_ATAController_h */
-
diff --git a/src/VBox/Devices/Storage/ATAPIPassthrough.cpp b/src/VBox/Devices/Storage/ATAPIPassthrough.cpp
new file mode 100644
index 00000000..22cc27fc
--- /dev/null
+++ b/src/VBox/Devices/Storage/ATAPIPassthrough.cpp
@@ -0,0 +1,632 @@
+/* $Id: ATAPIPassthrough.cpp $ */
+/** @file
+ * VBox storage devices: ATAPI emulation (common code for DevATA and DevAHCI).
+ */
+
+/*
+ * Copyright (C) 2012 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#define LOG_GROUP LOG_GROUP_DEV_IDE
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/cdefs.h>
+#include <VBox/scsi.h>
+
+#include "ATAPIPassthrough.h"
+
+/** The track was not detected yet. */
+#define TRACK_FLAGS_UNDETECTED RT_BIT_32(0)
+/** The track is the lead in track of the medium. */
+#define TRACK_FLAGS_LEAD_IN RT_BIT_32(1)
+/** The track is the lead out track of the medium. */
+#define TRACK_FLAGS_LEAD_OUT RT_BIT_32(2)
+
+/** Don't clear already detected tracks on the medium. */
+#define ATAPI_TRACK_LIST_REALLOCATE_FLAGS_DONT_CLEAR RT_BIT_32(0)
+
+/**
+ * Track main data form.
+ */
+typedef enum TRACKDATAFORM
+{
+ /** Invalid data form. */
+ TRACKDATAFORM_INVALID = 0,
+ /** 2352 bytes of data. */
+ TRACKDATAFORM_CDDA,
+ /** CDDA data is pause. */
+ TRACKDATAFORM_CDDA_PAUSE,
+ /** Mode 1 with 2048 bytes sector size. */
+ TRACKDATAFORM_MODE1_2048,
+ /** Mode 1 with 2352 bytes sector size. */
+ TRACKDATAFORM_MODE1_2352,
+ /** Mode 1 with 0 bytes sector size (generated by the drive). */
+ TRACKDATAFORM_MODE1_0,
+ /** XA Mode with 2336 bytes sector size. */
+ TRACKDATAFORM_XA_2336,
+ /** XA Mode with 2352 bytes sector size. */
+ TRACKDATAFORM_XA_2352,
+ /** XA Mode with 0 bytes sector size (generated by the drive). */
+ TRACKDATAFORM_XA_0,
+ /** Mode 2 with 2336 bytes sector size. */
+ TRACKDATAFORM_MODE2_2336,
+ /** Mode 2 with 2352 bytes sector size. */
+ TRACKDATAFORM_MODE2_2352,
+ /** Mode 2 with 0 bytes sector size (generated by the drive). */
+ TRACKDATAFORM_MODE2_0
+} TRACKDATAFORM;
+
+/**
+ * Subchannel data form.
+ */
+typedef enum SUBCHNDATAFORM
+{
+ /** Invalid subchannel data form. */
+ SUBCHNDATAFORM_INVALID = 0,
+ /** 0 bytes for the subchannel (generated by the drive). */
+ SUBCHNDATAFORM_0,
+ /** 96 bytes of data for the subchannel. */
+ SUBCHNDATAFORM_96
+} SUBCHNDATAFORM;
+
+/**
+ * Track entry.
+ */
+typedef struct TRACK
+{
+ /** Start LBA of the track. */
+ int64_t iLbaStart;
+ /** Number of sectors in the track. */
+ uint32_t cSectors;
+ /** Data form of main data. */
+ TRACKDATAFORM enmMainDataForm;
+ /** Data form of sub channel. */
+ SUBCHNDATAFORM enmSubChnDataForm;
+ /** Flags for the track. */
+ uint32_t fFlags;
+} TRACK, *PTRACK;
+
+/**
+ * Media track list.
+ */
+typedef struct TRACKLIST
+{
+ /** Number of detected tracks of the current medium. */
+ unsigned cTracksCurrent;
+ /** Maximum number of tracks the list can contain. */
+ unsigned cTracksMax;
+ /** Variable list of tracks. */
+ PTRACK paTracks;
+} TRACKLIST, *PTRACKLIST;
+
+DECLINLINE(uint16_t) atapiBE2H_U16(const uint8_t *pbBuf)
+{
+ return (pbBuf[0] << 8) | pbBuf[1];
+}
+
+
+DECLINLINE(uint32_t) atapiBE2H_U24(const uint8_t *pbBuf)
+{
+ return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2];
+}
+
+
+DECLINLINE(uint32_t) atapiBE2H_U32(const uint8_t *pbBuf)
+{
+ return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3];
+}
+
+DECLINLINE(int64_t) atapiMSF2LBA(const uint8_t *pbBuf)
+{
+ return ((int64_t)(pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2]) - 150; /* 2 second pregap */
+}
+
+/**
+ * Reallocate the given track list to be able to hold the given number of tracks.
+ *
+ * @returns VBox status code.
+ * @param pTrackList The track list to reallocate.
+ * @param cTracks Number of tracks the list must be able to hold.
+ * @param fFlags Flags for the reallocation.
+ */
+static int atapiTrackListReallocate(PTRACKLIST pTrackList, unsigned cTracks, uint32_t fFlags)
+{
+ int rc = VINF_SUCCESS;
+
+ if (!(fFlags & ATAPI_TRACK_LIST_REALLOCATE_FLAGS_DONT_CLEAR))
+ ATAPIPassthroughTrackListClear(pTrackList);
+
+ if (pTrackList->cTracksMax < cTracks)
+ {
+ PTRACK paTracksNew = (PTRACK)RTMemRealloc(pTrackList->paTracks, cTracks * sizeof(TRACK));
+ if (paTracksNew)
+ {
+ pTrackList->paTracks = paTracksNew;
+
+ /* Mark new tracks as undetected. */
+ for (unsigned i = pTrackList->cTracksMax; i < cTracks; i++)
+ pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED;
+
+ pTrackList->cTracksMax = cTracks;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ pTrackList->cTracksCurrent = cTracks;
+
+ return rc;
+}
+
+/**
+ * Initilizes the given track from the given CUE sheet entry.
+ *
+ * @returns nothing.
+ * @param pTrack The track to initialize.
+ * @param pbCueSheetEntry CUE sheet entry to use.
+ */
+static void atapiTrackListEntryCreateFromCueSheetEntry(PTRACK pTrack, const uint8_t *pbCueSheetEntry)
+{
+ TRACKDATAFORM enmTrackDataForm = TRACKDATAFORM_INVALID;
+ SUBCHNDATAFORM enmSubChnDataForm = SUBCHNDATAFORM_INVALID;
+
+ /* Determine size of main data based on the data form field. */
+ switch (pbCueSheetEntry[3] & 0x3f)
+ {
+ case 0x00: /* CD-DA with data. */
+ enmTrackDataForm = TRACKDATAFORM_CDDA;
+ break;
+ case 0x01: /* CD-DA without data (used for pauses between tracks). */
+ enmTrackDataForm = TRACKDATAFORM_CDDA_PAUSE;
+ break;
+ case 0x10: /* CD-ROM mode 1 */
+ case 0x12:
+ enmTrackDataForm = TRACKDATAFORM_MODE1_2048;
+ break;
+ case 0x11:
+ case 0x13:
+ enmTrackDataForm = TRACKDATAFORM_MODE1_2352;
+ break;
+ case 0x14:
+ enmTrackDataForm = TRACKDATAFORM_MODE1_0;
+ break;
+ case 0x20: /* CD-ROM XA, CD-I */
+ case 0x22:
+ enmTrackDataForm = TRACKDATAFORM_XA_2336;
+ break;
+ case 0x21:
+ case 0x23:
+ enmTrackDataForm = TRACKDATAFORM_XA_2352;
+ break;
+ case 0x24:
+ enmTrackDataForm = TRACKDATAFORM_XA_0;
+ break;
+ case 0x31: /* CD-ROM Mode 2 */
+ case 0x33:
+ enmTrackDataForm = TRACKDATAFORM_MODE2_2352;
+ break;
+ case 0x30:
+ case 0x32:
+ enmTrackDataForm = TRACKDATAFORM_MODE2_2336;
+ break;
+ case 0x34:
+ enmTrackDataForm = TRACKDATAFORM_MODE2_0;
+ break;
+ default: /* Reserved, invalid mode. Log and leave default sector size. */
+ LogRel(("ATA: Invalid data form mode %d for current CUE sheet\n",
+ pbCueSheetEntry[3] & 0x3f));
+ }
+
+ /* Determine size of sub channel data based on data form field. */
+ switch ((pbCueSheetEntry[3] & 0xc0) >> 6)
+ {
+ case 0x00: /* Sub channel all zeroes, autogenerated by the drive. */
+ enmSubChnDataForm = SUBCHNDATAFORM_0;
+ break;
+ case 0x01:
+ case 0x03:
+ enmSubChnDataForm = SUBCHNDATAFORM_96;
+ break;
+ default:
+ LogRel(("ATA: Invalid sub-channel data form mode %u for current CUE sheet\n",
+ pbCueSheetEntry[3] & 0xc0));
+ }
+
+ pTrack->enmMainDataForm = enmTrackDataForm;
+ pTrack->enmSubChnDataForm = enmSubChnDataForm;
+ pTrack->iLbaStart = atapiMSF2LBA(&pbCueSheetEntry[5]);
+ if (pbCueSheetEntry[1] != 0xaa)
+ {
+ /* Calculate number of sectors from the next entry. */
+ int64_t iLbaNext = atapiMSF2LBA(&pbCueSheetEntry[5+8]);
+ pTrack->cSectors = iLbaNext - pTrack->iLbaStart;
+ }
+ else
+ {
+ pTrack->fFlags |= TRACK_FLAGS_LEAD_OUT;
+ pTrack->cSectors = 0;
+ }
+ pTrack->fFlags &= ~TRACK_FLAGS_UNDETECTED;
+}
+
+/**
+ * Update the track list from a SEND CUE SHEET request.
+ *
+ * @returns VBox status code.
+ * @param pTrackList Track list to update.
+ * @param pbCDB CDB of the SEND CUE SHEET request.
+ * @param pvBuf The CUE sheet.
+ */
+static int atapiTrackListUpdateFromSendCueSheet(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
+{
+ int rc = VINF_SUCCESS;
+ unsigned cbCueSheet = atapiBE2H_U24(pbCDB + 6);
+ unsigned cTracks = cbCueSheet / 8;
+
+ AssertReturn(cbCueSheet % 8 == 0 && cTracks, VERR_INVALID_PARAMETER);
+
+ rc = atapiTrackListReallocate(pTrackList, cTracks, 0);
+ if (RT_SUCCESS(rc))
+ {
+ const uint8_t *pbCueSheet = (uint8_t *)pvBuf;
+ PTRACK pTrack = pTrackList->paTracks;
+
+ for (unsigned i = 0; i < cTracks; i++)
+ {
+ atapiTrackListEntryCreateFromCueSheetEntry(pTrack, pbCueSheet);
+ if (i == 0)
+ pTrack->fFlags |= TRACK_FLAGS_LEAD_IN;
+ pTrack++;
+ pbCueSheet += 8;
+ }
+ }
+
+ return rc;
+}
+
+static int atapiTrackListUpdateFromSendDvdStructure(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+/**
+ * Update track list from formatted TOC data.
+ *
+ * @returns VBox status code.
+ * @param pTrackList The track list to update.
+ * @param fMSF Flag whether block addresses are in MSF or LBA format.
+ * @param pbBuf Buffer holding the formatted TOC.
+ * @param cbBuffer Size of the buffer.
+ */
+static int atapiTrackListUpdateFromFormattedToc(PTRACKLIST pTrackList, uint8_t iTrack,
+ bool fMSF, const uint8_t *pbBuf, uint32_t cbBuffer)
+{
+ int rc = VINF_SUCCESS;
+ unsigned cbToc = atapiBE2H_U16(pbBuf);
+ uint8_t iTrackFirst = pbBuf[2];
+ unsigned cTracks;
+
+ cbToc -= 2;
+ pbBuf += 4;
+ AssertReturn(cbToc % 8 == 0, VERR_INVALID_PARAMETER);
+
+ cTracks = cbToc / 8 + iTrackFirst;
+
+ rc = atapiTrackListReallocate(pTrackList, cTracks, ATAPI_TRACK_LIST_REALLOCATE_FLAGS_DONT_CLEAR);
+ if (RT_SUCCESS(rc))
+ {
+ PTRACK pTrack = &pTrackList->paTracks[iTrackFirst];
+
+ for (unsigned i = iTrackFirst; i < cTracks; i++)
+ {
+ if (pbBuf[1] & 0x4)
+ pTrack->enmMainDataForm = TRACKDATAFORM_MODE1_2048;
+ else
+ pTrack->enmMainDataForm = TRACKDATAFORM_CDDA;
+
+ pTrack->enmSubChnDataForm = SUBCHNDATAFORM_0;
+ if (fMSF)
+ pTrack->iLbaStart = atapiMSF2LBA(&pbBuf[4]);
+ else
+ pTrack->iLbaStart = atapiBE2H_U32(&pbBuf[4]);
+
+ if (pbBuf[2] != 0xaa)
+ {
+ /* Calculate number of sectors from the next entry. */
+ int64_t iLbaNext;
+
+ if (fMSF)
+ iLbaNext = atapiMSF2LBA(&pbBuf[4+8]);
+ else
+ iLbaNext = atapiBE2H_U32(&pbBuf[4+8]);
+
+ pTrack->cSectors = iLbaNext - pTrack->iLbaStart;
+ }
+ else
+ pTrack->cSectors = 0;
+
+ pTrack->fFlags &= ~TRACK_FLAGS_UNDETECTED;
+ pbBuf += 8;
+ pTrack++;
+ }
+ }
+
+ return rc;
+}
+
+static int atapiTrackListUpdateFromReadTocPmaAtip(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
+{
+ int rc = VINF_SUCCESS;
+ uint16_t cbBuffer = atapiBE2H_U16(&pbCDB[7]);
+ bool fMSF = (pbCDB[1] & 0x2) != 0;
+ uint8_t uFmt = pbCDB[2] & 0xf;
+ uint8_t iTrack = pbCDB[6];
+
+ switch (uFmt)
+ {
+ case 0x00:
+ rc = atapiTrackListUpdateFromFormattedToc(pTrackList, iTrack, fMSF, (uint8_t *)pvBuf, cbBuffer);
+ break;
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ default:
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+static int atapiTrackListUpdateFromReadTrackInformation(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+static int atapiTrackListUpdateFromReadDvdStructure(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+static int atapiTrackListUpdateFromReadDiscInformation(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+/**
+ * Converts the given track data form to a string.
+ *
+ * @returns Track data form as a string.
+ * @param enmTrackDataForm The track main data form.
+ */
+static const char *atapiTrackListMainDataFormToString(TRACKDATAFORM enmTrackDataForm)
+{
+ switch (enmTrackDataForm)
+ {
+ case TRACKDATAFORM_CDDA:
+ return "CD-DA";
+ case TRACKDATAFORM_CDDA_PAUSE:
+ return "CD-DA Pause";
+ case TRACKDATAFORM_MODE1_2048:
+ return "Mode 1 (2048 bytes)";
+ case TRACKDATAFORM_MODE1_2352:
+ return "Mode 1 (2352 bytes)";
+ case TRACKDATAFORM_MODE1_0:
+ return "Mode 1 (0 bytes)";
+ case TRACKDATAFORM_XA_2336:
+ return "XA (2336 bytes)";
+ case TRACKDATAFORM_XA_2352:
+ return "XA (2352 bytes)";
+ case TRACKDATAFORM_XA_0:
+ return "XA (0 bytes)";
+ case TRACKDATAFORM_MODE2_2336:
+ return "Mode 2 (2336 bytes)";
+ case TRACKDATAFORM_MODE2_2352:
+ return "Mode 2 (2352 bytes)";
+ case TRACKDATAFORM_MODE2_0:
+ return "Mode 2 (0 bytes)";
+ case TRACKDATAFORM_INVALID:
+ default:
+ return "Invalid";
+ }
+}
+
+/**
+ * Converts the given subchannel data form to a string.
+ *
+ * @returns Subchannel data form as a string.
+ * @param enmSubChnDataForm The subchannel main data form.
+ */
+static const char *atapiTrackListSubChnDataFormToString(SUBCHNDATAFORM enmSubChnDataForm)
+{
+ switch (enmSubChnDataForm)
+ {
+ case SUBCHNDATAFORM_0:
+ return "0";
+ case SUBCHNDATAFORM_96:
+ return "96";
+ case SUBCHNDATAFORM_INVALID:
+ default:
+ return "Invalid";
+ }
+}
+
+/**
+ * Dump the complete track list to the release log.
+ *
+ * @returns nothing.
+ * @param pTrackList The track list to dump.
+ */
+static void atapiTrackListDump(PTRACKLIST pTrackList)
+{
+ LogRel(("Track List: cTracks=%u\n", pTrackList->cTracksCurrent));
+ for (unsigned i = 0; i < pTrackList->cTracksCurrent; i++)
+ {
+ PTRACK pTrack = &pTrackList->paTracks[i];
+
+ LogRel((" Track %u: LBAStart=%lld cSectors=%u enmMainDataForm=%s enmSubChnDataForm=%s fFlags=[%s%s%s]\n",
+ i, pTrack->iLbaStart, pTrack->cSectors, atapiTrackListMainDataFormToString(pTrack->enmMainDataForm),
+ atapiTrackListSubChnDataFormToString(pTrack->enmSubChnDataForm),
+ pTrack->fFlags & TRACK_FLAGS_UNDETECTED ? "UNDETECTED " : "",
+ pTrack->fFlags & TRACK_FLAGS_LEAD_IN ? "Lead-In " : "",
+ pTrack->fFlags & TRACK_FLAGS_LEAD_OUT ? "Lead-Out" : ""));
+ }
+}
+
+DECLHIDDEN(int) ATAPIPassthroughTrackListCreateEmpty(PTRACKLIST *ppTrackList)
+{
+ int rc = VERR_NO_MEMORY;
+ PTRACKLIST pTrackList = (PTRACKLIST)RTMemAllocZ(sizeof(TRACKLIST));
+
+ if (pTrackList)
+ {
+ rc = VINF_SUCCESS;
+ *ppTrackList = pTrackList;
+ }
+
+ return rc;
+}
+
+DECLHIDDEN(void) ATAPIPassthroughTrackListDestroy(PTRACKLIST pTrackList)
+{
+ if (pTrackList->paTracks)
+ RTMemFree(pTrackList->paTracks);
+ RTMemFree(pTrackList);
+}
+
+DECLHIDDEN(void) ATAPIPassthroughTrackListClear(PTRACKLIST pTrackList)
+{
+ pTrackList->cTracksCurrent = 0;
+
+ /* Mark all tracks as undetected. */
+ for (unsigned i = 0; i < pTrackList->cTracksMax; i++)
+ pTrackList->paTracks[i].fFlags |= TRACK_FLAGS_UNDETECTED;
+}
+
+DECLHIDDEN(int) ATAPIPassthroughTrackListUpdate(PTRACKLIST pTrackList, const uint8_t *pbCDB, const void *pvBuf)
+{
+ int rc = VINF_SUCCESS;
+
+ switch (pbCDB[0])
+ {
+ case SCSI_SEND_CUE_SHEET:
+ rc = atapiTrackListUpdateFromSendCueSheet(pTrackList, pbCDB, pvBuf);
+ break;
+ case SCSI_SEND_DVD_STRUCTURE:
+ rc = atapiTrackListUpdateFromSendDvdStructure(pTrackList, pbCDB, pvBuf);
+ break;
+ case SCSI_READ_TOC_PMA_ATIP:
+ rc = atapiTrackListUpdateFromReadTocPmaAtip(pTrackList, pbCDB, pvBuf);
+ break;
+ case SCSI_READ_TRACK_INFORMATION:
+ rc = atapiTrackListUpdateFromReadTrackInformation(pTrackList, pbCDB, pvBuf);
+ break;
+ case SCSI_READ_DVD_STRUCTURE:
+ rc = atapiTrackListUpdateFromReadDvdStructure(pTrackList, pbCDB, pvBuf);
+ break;
+ case SCSI_READ_DISC_INFORMATION:
+ rc = atapiTrackListUpdateFromReadDiscInformation(pTrackList, pbCDB, pvBuf);
+ break;
+ default:
+ LogRel(("ATAPI: Invalid opcode %#x while determining media layout\n", pbCDB[0]));
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+#ifdef LOG_ENABLED
+ atapiTrackListDump(pTrackList);
+#endif
+
+ return rc;
+}
+
+DECLHIDDEN(uint32_t) ATAPIPassthroughTrackListGetSectorSizeFromLba(PTRACKLIST pTrackList, uint32_t iAtapiLba)
+{
+ PTRACK pTrack = NULL;
+ uint32_t cbAtapiSector = 2048;
+
+ if (pTrackList->cTracksCurrent)
+ {
+ if ( iAtapiLba > UINT32_C(0xffff4fa1)
+ && (int32_t)iAtapiLba < -150)
+ {
+ /* Lead-In area, this is always the first entry in the cue sheet. */
+ pTrack = pTrackList->paTracks;
+ Assert(pTrack->fFlags & TRACK_FLAGS_LEAD_IN);
+ LogFlowFunc(("Selected Lead-In area\n"));
+ }
+ else
+ {
+ int64_t iAtapiLba64 = (int32_t)iAtapiLba;
+ pTrack = &pTrackList->paTracks[1];
+
+ /* Go through the track list and find the correct entry. */
+ for (unsigned i = 1; i < pTrackList->cTracksCurrent - 1; i++)
+ {
+ if (pTrack->fFlags & TRACK_FLAGS_UNDETECTED)
+ continue;
+
+ if ( pTrack->iLbaStart <= iAtapiLba64
+ && iAtapiLba64 < pTrack->iLbaStart + pTrack->cSectors)
+ break;
+
+ pTrack++;
+ }
+ }
+
+ if (pTrack)
+ {
+ switch (pTrack->enmMainDataForm)
+ {
+ case TRACKDATAFORM_CDDA:
+ case TRACKDATAFORM_MODE1_2352:
+ case TRACKDATAFORM_XA_2352:
+ case TRACKDATAFORM_MODE2_2352:
+ cbAtapiSector = 2352;
+ break;
+ case TRACKDATAFORM_MODE1_2048:
+ cbAtapiSector = 2048;
+ break;
+ case TRACKDATAFORM_CDDA_PAUSE:
+ case TRACKDATAFORM_MODE1_0:
+ case TRACKDATAFORM_XA_0:
+ case TRACKDATAFORM_MODE2_0:
+ cbAtapiSector = 0;
+ break;
+ case TRACKDATAFORM_XA_2336:
+ case TRACKDATAFORM_MODE2_2336:
+ cbAtapiSector = 2336;
+ break;
+ case TRACKDATAFORM_INVALID:
+ default:
+ AssertMsgFailed(("Invalid track data form %d\n", pTrack->enmMainDataForm));
+ }
+
+ switch (pTrack->enmSubChnDataForm)
+ {
+ case SUBCHNDATAFORM_0:
+ break;
+ case SUBCHNDATAFORM_96:
+ cbAtapiSector += 96;
+ break;
+ case SUBCHNDATAFORM_INVALID:
+ default:
+ AssertMsgFailed(("Invalid subchannel data form %d\n", pTrack->enmSubChnDataForm));
+ }
+ }
+ }
+
+ return cbAtapiSector;
+}
+
diff --git a/src/VBox/Devices/Storage/ATAPIPassthrough.h b/src/VBox/Devices/Storage/ATAPIPassthrough.h
new file mode 100644
index 00000000..0ccaf62e
--- /dev/null
+++ b/src/VBox/Devices/Storage/ATAPIPassthrough.h
@@ -0,0 +1,74 @@
+/* $Id: ATAPIPassthrough.h $ */
+/** @file
+ * VBox storage devices: ATAPI passthrough helpers (common code for DevATA and DevAHCI).
+ */
+
+/*
+ * Copyright (C) 2012 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+#ifndef __ATAPIPassthrough_h
+#define __ATAPIPassthrough_h
+
+#include <VBox/cdefs.h>
+
+RT_C_DECLS_BEGIN
+
+/**
+ * Opaque media track list.
+ */
+typedef struct TRACKLIST *PTRACKLIST;
+
+/**
+ * Creates an empty track list handle.
+ *
+ * @returns VBox status code.
+ * @param ppTrackList Where to store the track list handle on success.
+ */
+DECLHIDDEN(int) ATAPIPassthroughTrackListCreateEmpty(PTRACKLIST *ppTrackList);
+
+/**
+ * Destroys the allocated task list handle.
+ *
+ * @returns nothing.
+ * @param pTrackList The track list handle to destroy.
+ */
+DECLHIDDEN(void) ATAPIPassthroughTrackListDestroy(PTRACKLIST pTrackList);
+
+/**
+ * Clears all tracks from the given task list.
+ *
+ * @returns nothing.
+ * @param pTrackList The track list to clear.
+ */
+DECLHIDDEN(void) ATAPIPassthroughTrackListClear(PTRACKLIST pTrackList);
+
+/**
+ * Updates the track list from the given CDB and data buffer.
+ *
+ * @returns VBox status code.
+ * @param pTrackList The track list to update.
+ * @param pCDB The CDB buffer.
+ * @param pvBuf The data buffer.
+ */
+DECLHIDDEN(int) ATAPIPassthroughTrackListUpdate(PTRACKLIST pTrackList, const uint8_t *pCDB, const void *pvBuf);
+
+/**
+ * Return the sector size from the track matching the LBA in the given track list.
+ *
+ * @returns Sector size.
+ * @param pTrackList The track list to use.
+ * @param iAtapiLba The start LBA to get the sector size for.
+ */
+DECLHIDDEN(uint32_t) ATAPIPassthroughTrackListGetSectorSizeFromLba(PTRACKLIST pTrackList, uint32_t iAtapiLba);
+
+RT_C_DECLS_END
+
+#endif /* __ATAPIPassthrough_h */
diff --git a/src/VBox/Devices/Storage/Debug.cpp b/src/VBox/Devices/Storage/Debug.cpp
index 2b6678d5..1fb479e9 100644
--- a/src/VBox/Devices/Storage/Debug.cpp
+++ b/src/VBox/Devices/Storage/Debug.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2008 Oracle Corporation
+ * Copyright (C) 2008-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Devices/Storage/DevAHCI.cpp b/src/VBox/Devices/Storage/DevAHCI.cpp
index 0a300e3d..50b0ca8b 100644
--- a/src/VBox/Devices/Storage/DevAHCI.cpp
+++ b/src/VBox/Devices/Storage/DevAHCI.cpp
@@ -1,11 +1,12 @@
/* $Id: DevAHCI.cpp $ */
/** @file
- * VBox storage devices: AHCI controller device (disk and cdrom).
- * Implements the AHCI standard 1.1
+ * DevAHCI - AHCI controller device (disk and cdrom).
+ *
+ * Implements the AHCI standard 1.1
*/
/*
- * Copyright (C) 2006-2012 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -35,12 +36,12 @@
/*******************************************************************************
* Header Files *
*******************************************************************************/
-//#define DEBUG
#define LOG_GROUP LOG_GROUP_DEV_AHCI
#include <VBox/vmm/pdmdev.h>
#include <VBox/vmm/pdmqueue.h>
#include <VBox/vmm/pdmthread.h>
#include <VBox/vmm/pdmcritsect.h>
+#include <VBox/sup.h>
#include <VBox/scsi.h>
#include <iprt/assert.h>
#include <iprt/asm.h>
@@ -55,8 +56,20 @@
#endif
#include "PIIX3ATABmDma.h"
#include "ide.h"
+#include "ATAPIPassthrough.h"
#include "VBoxDD.h"
+#if defined(VBOX_WITH_DTRACE) \
+ && defined(IN_RING3) \
+ && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
+# include "dtrace/VBoxDD.h"
+#else
+# define VBOXDD_AHCI_REQ_SUBMIT(a,b,c,d) do { } while (0)
+# define VBOXDD_AHCI_REQ_SUBMIT_TIMESTAMP(a,b) do { } while (0)
+# define VBOXDD_AHCI_REQ_COMPLETED(a,b,c,d,e) do { } while (0)
+# define VBOXDD_AHCI_REQ_COMPLETED_TIMESTAMP(a,b) do { } while (0)
+#endif
+
/** Maximum number of ports available.
* Spec defines 32 but we have one allocated for command completion coalescing
* and another for a reserved future feature.
@@ -124,8 +137,6 @@
/* Media track type */
#define ATA_MEDIA_TYPE_UNKNOWN 0 /**< unknown CD type */
-#define ATA_MEDIA_TYPE_DATA 1 /**< Data CD */
-#define ATA_MEDIA_TYPE_CDDA 2 /**< CD-DA (audio) CD type */
/** ATAPI sense info size. */
#define ATAPI_SENSE_SIZE 64
@@ -278,6 +289,8 @@ typedef struct AHCIREQ
{
/** Task state. */
volatile AHCITXSTATE enmTxState;
+ /** Start timestamp of the request. */
+ uint64_t tsStart;
/** Tag of the task. */
uint32_t uTag;
/** The command header for this task. */
@@ -432,17 +445,15 @@ typedef struct AHCIPort
bool fAsyncInterface;
/** Flag if we are in a device reset. */
bool fResetDevice;
- /** Flag whether the I/O thread idles. */
- volatile bool fAsyncIOThreadIdle;
/** Flag whether the port is in redo task mode. */
volatile bool fRedo;
-
-#if HC_ARCH_BITS == 64
- bool fAlignment2;
-#endif
+ /** Flag whether the worker thread is sleeping. */
+ volatile bool fWrkThreadSleeping;
/** Number of total sectors. */
uint64_t cTotalSectors;
+ /** Size of one sector. */
+ uint32_t cbSector;
/** Currently configured number of sectors in a multi-sector transfer. */
uint32_t cMultSectors;
/** Currently active transfer mode (MDMA/UDMA) and speed. */
@@ -451,6 +462,8 @@ typedef struct AHCIPort
uint8_t abATAPISense[ATAPI_SENSE_SIZE];
/** HACK: Countdown till we report a newly unmounted drive as mounted. */
uint8_t cNotifiedMediaChange;
+ /** Exponent of logical sectors in a physical sector, number of logical sectors is 2^exp. */
+ uint8_t cLogSectorsPerPhysicalExp;
/** The same for GET_EVENT_STATUS for mechanism */
volatile uint32_t MediaEventStatus;
/** Media type if known. */
@@ -464,6 +477,8 @@ typedef struct AHCIPort
volatile uint32_t u32QueuedTasksFinished;
/** Bitmap for new queued tasks (Guest -> R3). */
volatile uint32_t u32TasksNew;
+ /** Bitmap of tasks which must be redone because of a non fatal error. */
+ volatile uint32_t u32TasksRedo;
/** Current command slot processed.
* Accessed by the guest by reading the CMD register.
@@ -504,8 +519,6 @@ typedef struct AHCIPort
/** Async IO Thread. */
R3PTRTYPE(PPDMTHREAD) pAsyncIOThread;
- /** Request semaphore. */
- RTSEMEVENT AsyncIORequestSem;
/**
* Array of cached tasks. The tag number is the index value.
* Only used with the async interface.
@@ -513,10 +526,11 @@ typedef struct AHCIPort
R3PTRTYPE(PAHCIREQ) aCachedTasks[AHCI_NR_COMMAND_SLOTS];
/** First task throwing an error. */
R3PTRTYPE(volatile PAHCIREQ) pTaskErr;
+ /** The current tracklist of the loaded medium if passthrough is used. */
+ R3PTRTYPE(PTRACKLIST) pTrackList;
-#if HC_ARCH_BITS == 32
- uint32_t u32Alignment4;
-#endif
+ /** The event semaphore the processing thread waits on. */
+ SUPSEMEVENT hEvtProcess;
/** Release statistics: number of DMA commands. */
STAMCOUNTER StatDMA;
@@ -529,12 +543,8 @@ typedef struct AHCIPort
#ifdef VBOX_WITH_STATISTICS
/** Statistics: Time to complete one request. */
STAMPROFILE StatProfileProcessTime;
- /** Statistics: Time to map requests into R3. */
- STAMPROFILE StatProfileMapIntoR3;
/** Statistics: Amount of time to read/write data. */
STAMPROFILE StatProfileReadWrite;
- /** Statistics: Amount of time to destroy a list. */
- STAMPROFILE StatProfileDestroyScatterGatherList;
#endif /* VBOX_WITH_STATISTICS */
/** The serial numnber to use for IDENTIFY DEVICE commands. */
@@ -664,6 +674,8 @@ typedef struct AHCI
/** Bitmask of ports which asserted an interrupt. */
volatile uint32_t u32PortsInterrupted;
+ /** Number of I/O threads currently active - used for async controller reset handling. */
+ volatile uint32_t cThreadsActive;
/** Device is in a reset state. */
bool fReset;
/** Supports 64bit addressing */
@@ -688,6 +700,12 @@ typedef struct AHCI
/** Flag whether we have written the first 4bytes in an 8byte MMIO write successfully. */
volatile bool f8ByteMMIO4BytesWrittenSuccessfully;
+#if HC_ARCH_BITS == 64
+ uint32_t Alignment7;
+#endif
+
+ /** The support driver session handle. */
+ R3R0PTRTYPE(PSUPDRVSESSION) pSupDrvSession;
} AHCI;
/** Pointer to the state of an AHCI device. */
typedef AHCI *PAHCI;
@@ -1049,8 +1067,6 @@ static int PortCmdIssue_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32
if ( (pAhciPort->regCMD & AHCI_PORT_CMD_CR)
&& u32Value > 0)
{
- uint32_t u32Tasks;
-
/*
* Clear all tasks which are already marked as busy. The guest
* shouldn't write already busy tasks actually.
@@ -1058,16 +1074,21 @@ static int PortCmdIssue_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32
u32Value &= ~pAhciPort->regCI;
ASMAtomicOrU32(&pAhciPort->u32TasksNew, u32Value);
- u32Tasks = ASMAtomicReadU32(&pAhciPort->u32TasksNew);
/* Send a notification to R3 if u32TasksNew was before our write. */
- if (!(u32Tasks ^ u32Value))
+ if (ASMAtomicReadBool(&pAhciPort->fWrkThreadSleeping))
{
+#ifdef IN_RC
PDEVPORTNOTIFIERQUEUEITEM pItem = (PDEVPORTNOTIFIERQUEUEITEM)PDMQueueAlloc(ahci->CTX_SUFF(pNotifierQueue));
AssertMsg(VALID_PTR(pItem), ("Allocating item for queue failed\n"));
pItem->iPort = pAhciPort->iLUN;
PDMQueueInsert(ahci->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem);
+#else
+ LogFlowFunc(("Signal event semaphore\n"));
+ int rc = SUPSemEventSignal(ahci->pSupDrvSession, pAhciPort->hEvtProcess);
+ AssertRC(rc);
+#endif
}
}
@@ -1160,7 +1181,7 @@ static int PortSControl_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32
Assert(fAllTasksCanceled);
if (!ASMAtomicXchgBool(&pAhciPort->fPortReset, true))
- LogRel(("AHCI#%d: Port %d reset\n", ahci->CTX_SUFF(pDevIns)->iInstance,
+ LogRel(("AHCI#%u: Port %d reset\n", ahci->CTX_SUFF(pDevIns)->iInstance,
pAhciPort->iLUN));
pAhciPort->regSSTS = 0;
@@ -1269,7 +1290,7 @@ static int PortTaskFileData_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, ui
*/
static int PortCmd_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
{
- ahciLog(("%s: read regCMD=%#010x\n", __FUNCTION__, pAhciPort->regCMD));
+ ahciLog(("%s: read regCMD=%#010x\n", __FUNCTION__, pAhciPort->regCMD | AHCI_PORT_CMD_CCS_SHIFT(pAhciPort->u32CurrentCommandSlot)));
ahciLog(("%s: ICC=%d ASP=%d ALPE=%d DLAE=%d ATAPI=%d CPD=%d ISP=%d HPCP=%d PMA=%d CPS=%d CR=%d FR=%d ISS=%d CCS=%d FRE=%d CLO=%d POD=%d SUD=%d ST=%d\n",
__FUNCTION__, (pAhciPort->regCMD & AHCI_PORT_CMD_ICC) >> 28, (pAhciPort->regCMD & AHCI_PORT_CMD_ASP) >> 27,
(pAhciPort->regCMD & AHCI_PORT_CMD_ALPE) >> 26, (pAhciPort->regCMD & AHCI_PORT_CMD_DLAE) >> 25,
@@ -1304,6 +1325,9 @@ static int PortCmd_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u3
(u32Value & AHCI_PORT_CMD_POD) >> 2, (u32Value & AHCI_PORT_CMD_SUD) >> 1,
(u32Value & AHCI_PORT_CMD_ST)));
+ /* The PxCMD.CCS bits are R/O and maintained separately. */
+ u32Value &= ~AHCI_PORT_CMD_CCS;
+
if (pAhciPort->fPoweredOn && pAhciPort->fSpunUp)
{
if (u32Value & AHCI_PORT_CMD_CLO)
@@ -1316,17 +1340,43 @@ static int PortCmd_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u3
if (u32Value & AHCI_PORT_CMD_ST)
{
- ahciLog(("%s: Engine starts\n", __FUNCTION__));
-
- /* Set engine state to running if there is a device attached. */
- if (pAhciPort->pDrvBase)
+ /*
+ * Set engine state to running if there is a device attached and
+ * IS.PCS is clear.
+ */
+ if ( pAhciPort->pDrvBase
+ && !(pAhciPort->regIS & AHCI_PORT_IS_PCS))
+ {
+ ahciLog(("%s: Engine starts\n", __FUNCTION__));
u32Value |= AHCI_PORT_CMD_CR;
+
+ /* If there is something in CI, kick the I/O thread. */
+ if ( pAhciPort->regCI > 0
+ && ASMAtomicReadBool(&pAhciPort->fWrkThreadSleeping))
+ {
+ ASMAtomicOrU32(&pAhciPort->u32TasksNew, pAhciPort->regCI);
+#ifdef IN_RC
+ PDEVPORTNOTIFIERQUEUEITEM pItem = (PDEVPORTNOTIFIERQUEUEITEM)PDMQueueAlloc(ahci->CTX_SUFF(pNotifierQueue));
+ AssertMsg(VALID_PTR(pItem), ("Allocating item for queue failed\n"));
+
+ pItem->iPort = pAhciPort->iLUN;
+ PDMQueueInsert(ahci->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem);
+#else
+ LogFlowFunc(("Signal event semaphore\n"));
+ int rc = SUPSemEventSignal(ahci->pSupDrvSession, pAhciPort->hEvtProcess);
+ AssertRC(rc);
+#endif
+ }
+ }
+ else
+ u32Value &= ~AHCI_PORT_CMD_CR;
}
else
{
ahciLog(("%s: Engine stops\n", __FUNCTION__));
/* Clear command issue register. */
pAhciPort->regCI = 0;
+ pAhciPort->regSACT = 0;
/* Clear current command slot. */
pAhciPort->u32CurrentCommandSlot = 0;
u32Value &= ~AHCI_PORT_CMD_CR;
@@ -1718,9 +1768,22 @@ static int HbaControl_w(PAHCI ahci, uint32_t iReg, uint32_t u32Value)
#ifndef IN_RING3
return VINF_IOM_R3_MMIO_WRITE;
#else
- ahci->regHbaCtrl = (u32Value & AHCI_HBA_CTRL_RW_MASK) | AHCI_HBA_CTRL_AE;
- if (ahci->regHbaCtrl & AHCI_HBA_CTRL_HR)
+ /*
+ * Increase the active thread counter because we might set the host controller
+ * reset bit.
+ */
+ ASMAtomicIncU32(&ahci->cThreadsActive);
+ ASMAtomicWriteU32(&ahci->regHbaCtrl, (u32Value & AHCI_HBA_CTRL_RW_MASK) | AHCI_HBA_CTRL_AE);
+
+ /*
+ * Do the HBA reset if requested and there is no other active thread at the moment,
+ * the work is deferred to the last active thread otherwise.
+ */
+ uint32_t cThreadsActive = ASMAtomicDecU32(&ahci->cThreadsActive);
+ if ( (u32Value & AHCI_HBA_CTRL_HR)
+ && !cThreadsActive)
ahciHBAReset(ahci);
+
return VINF_SUCCESS;
#endif
}
@@ -1935,6 +1998,7 @@ static void ahciPortSwReset(PAHCIPort pAhciPort)
pAhciPort->uATATransferMode = ATA_MODE_UDMA | 6;
pAhciPort->u32TasksNew = 0;
+ pAhciPort->u32TasksRedo = 0;
pAhciPort->u32TasksFinished = 0;
pAhciPort->u32QueuedTasksFinished = 0;
pAhciPort->u32CurrentCommandSlot = 0;
@@ -2008,7 +2072,7 @@ static void ahciHBAReset(PAHCI pThis)
unsigned i;
int rc = VINF_SUCCESS;
- LogRel(("AHCI#%d: Reset the HBA\n", pThis->CTX_SUFF(pDevIns)->iInstance));
+ LogRel(("AHCI#%u: Reset the HBA\n", pThis->CTX_SUFF(pDevIns)->iInstance));
/* Stop the CCC timer. */
if (pThis->regHbaCccCtl & AHCI_HBA_CCC_CTL_EN)
@@ -2037,7 +2101,6 @@ static void ahciHBAReset(PAHCI pThis)
AHCI_HBA_CAP_NCS_SET(pThis->cCmdSlotsAvail) | /* Number of command slots we support */
AHCI_HBA_CAP_NP_SET(pThis->cPortsImpl); /* Number of supported ports */
pThis->regHbaCtrl = AHCI_HBA_CTRL_AE;
- pThis->regHbaIs = 0;
pThis->regHbaPi = ahciGetPortsImplemented(pThis->cPortsImpl);
pThis->regHbaVs = AHCI_HBA_VS_MJR | AHCI_HBA_VS_MNR;
pThis->regHbaCccCtl = 0;
@@ -2046,6 +2109,11 @@ static void ahciHBAReset(PAHCI pThis)
pThis->uCccPortNr = 0;
pThis->uCccNr = 0;
+ /* Clear pending interrupts. */
+ pThis->regHbaIs = 0;
+ pThis->u32PortsInterrupted = 0;
+ ahciHbaClearInterrupt(pThis);
+
pThis->f64BitAddr = false;
pThis->u32PortsInterrupted = 0;
pThis->f8ByteMMIO4BytesWrittenSuccessfully = false;
@@ -2150,23 +2218,26 @@ static int ahciRegisterRead(PAHCI pAhci, uint32_t uReg, void *pv, unsigned cb)
* @returns VBox status code.
*
* @param pAhci The AHCI instance.
- * @param uReg The register to write.
- * @param pv Where to fetch the result.
- * @param cb Number of bytes to write.
+ * @param offReg The offset of the register to write to.
+ * @param u32Value The value to write.
*/
-static int ahciRegisterWrite(PAHCI pAhci, uint32_t uReg, void const *pv, unsigned cb)
+static int ahciRegisterWrite(PAHCI pAhci, uint32_t offReg, uint32_t u32Value)
{
- int rc = VINF_SUCCESS;
+ int rc;
uint32_t iReg;
- if (uReg < AHCI_HBA_GLOBAL_SIZE)
+ /*
+ * If the access offset is smaller than 100h the guest accesses the global registers.
+ * Otherwise it accesses the registers of a port.
+ */
+ if (offReg < AHCI_HBA_GLOBAL_SIZE)
{
Log3(("Write global HBA register\n"));
- iReg = uReg >> 2;
+ iReg = offReg >> 2;
if (iReg < RT_ELEMENTS(g_aOpRegs))
{
const AHCIOPREG *pReg = &g_aOpRegs[iReg];
- rc = pReg->pfnWrite(pAhci, iReg, *(uint32_t *)pv);
+ rc = pReg->pfnWrite(pAhci, iReg, u32Value);
}
else
{
@@ -2179,15 +2250,15 @@ static int ahciRegisterWrite(PAHCI pAhci, uint32_t uReg, void const *pv, unsigne
uint32_t iPort;
Log3(("Write Port register\n"));
/* Calculate accessed port. */
- uReg -= AHCI_HBA_GLOBAL_SIZE;
- iPort = uReg / AHCI_PORT_REGISTER_SIZE;
- iReg = (uReg % AHCI_PORT_REGISTER_SIZE) >> 2;
+ offReg -= AHCI_HBA_GLOBAL_SIZE;
+ iPort = offReg / AHCI_PORT_REGISTER_SIZE;
+ iReg = (offReg % AHCI_PORT_REGISTER_SIZE) >> 2;
Log3(("%s: Trying to write to port %u and register %u\n", __FUNCTION__, iPort, iReg));
if (RT_LIKELY( iPort < pAhci->cPortsImpl
&& iReg < RT_ELEMENTS(g_aPortOpRegs)))
{
const AHCIPORTOPREG *pPortReg = &g_aPortOpRegs[iReg];
- rc = pPortReg->pfnWrite(pAhci, &pAhci->ahciPort[iPort], iReg, *(uint32_t *)pv);
+ rc = pPortReg->pfnWrite(pAhci, &pAhci->ahciPort[iPort], iReg, u32Value);
}
else
{
@@ -2213,23 +2284,10 @@ static int ahciRegisterWrite(PAHCI pAhci, uint32_t uReg, void const *pv, unsigne
PDMBOTHCBDECL(int) ahciMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
{
PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
- int rc = VINF_SUCCESS;
-
- /* Break up 64 bits reads into two dword reads. */
- if (cb == 8)
- {
- rc = ahciMMIORead(pDevIns, pvUser, GCPhysAddr, pv, 4);
- if (RT_FAILURE(rc))
- return rc;
-
- return ahciMMIORead(pDevIns, pvUser, GCPhysAddr + 4, (uint8_t *)pv + 4, 4);
- }
-
- Log2(("#%d ahciMMIORead: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Rrc\n",
- pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr, rc));
+ Log2(("#%d ahciMMIORead: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp\n",
+ pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr));
- uint32_t uOffset = (GCPhysAddr - pAhci->MMIOBase);
- rc = ahciRegisterRead(pAhci, uOffset, pv, cb);
+ int rc = ahciRegisterRead(pAhci, GCPhysAddr - pAhci->MMIOBase, pv, cb);
Log2(("#%d ahciMMIORead: return pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Rrc\n",
pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr, rc));
@@ -2251,9 +2309,12 @@ PDMBOTHCBDECL(int) ahciMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhy
PDMBOTHCBDECL(int) ahciMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
{
PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
- int rc = VINF_SUCCESS;
+ Assert(cb == 4 || cb == 8);
+ Assert(!(GCPhysAddr & (cb - 1)));
/* Break up 64 bits writes into two dword writes. */
+ /** @todo Eliminate this code once the IOM/EM starts taking care of these
+ * situations. */
if (cb == 8)
{
/*
@@ -2263,6 +2324,7 @@ PDMBOTHCBDECL(int) ahciMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPh
* Writing the first 4 bytes again could cause indeterminate behavior
* which can cause errors in the guest.
*/
+ int rc = VINF_SUCCESS;
if (!pAhci->f8ByteMMIO4BytesWrittenSuccessfully)
{
rc = ahciMMIOWrite(pDevIns, pvUser, GCPhysAddr, pv, 4);
@@ -2283,29 +2345,9 @@ PDMBOTHCBDECL(int) ahciMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPh
return rc;
}
- Log2(("#%d ahciMMIOWrite: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp\n",
- pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr));
-
- /* Validate access. */
- if (cb != sizeof(uint32_t))
- {
- Log2(("%s: Bad write size!!! GCPhysAddr=%RGp cb=%d\n", __FUNCTION__, GCPhysAddr, cb));
- return VINF_SUCCESS;
- }
- if (GCPhysAddr & 0x3)
- {
- Log2(("%s: Unaligned write!!! GCPhysAddr=%RGp cb=%d\n", __FUNCTION__, GCPhysAddr, cb));
- return VINF_SUCCESS;
- }
-
- /*
- * If the access offset is smaller than 100h the guest accesses the global registers.
- * Otherwise it accesses the registers of a port.
- */
- uint32_t uOffset = (GCPhysAddr - pAhci->MMIOBase);
- rc = ahciRegisterWrite(pAhci, uOffset, pv, cb);
-
- return rc;
+ /* Do the access. */
+ Log2(("#%d ahciMMIOWrite: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp\n", pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr));
+ return ahciRegisterWrite(pAhci, GCPhysAddr - pAhci->MMIOBase, *(uint32_t const *)pv);
}
PDMBOTHCBDECL(int) ahciLegacyFakeWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
@@ -2349,8 +2391,9 @@ PDMBOTHCBDECL(int) ahciIdxDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT P
}
else
{
+ /** @todo range check? */
Assert(iReg == 1);
- rc = ahciRegisterWrite(pAhci, pAhci->regIdx, &u32, cb);
+ rc = ahciRegisterWrite(pAhci, pAhci->regIdx, u32);
if (rc == VINF_IOM_R3_MMIO_WRITE)
rc = VINF_IOM_R3_IOPORT_WRITE;
}
@@ -2392,9 +2435,12 @@ PDMBOTHCBDECL(int) ahciIdxDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Po
else
{
Assert(iReg == 1);
+ /** @todo range check? */
rc = ahciRegisterRead(pAhci, pAhci->regIdx, pu32, cb);
if (rc == VINF_IOM_R3_MMIO_READ)
rc = VINF_IOM_R3_IOPORT_READ;
+ else if (rc == VINF_IOM_MMIO_UNUSED_00)
+ rc = VERR_IOM_IOPORT_UNUSED;
}
}
else
@@ -2419,8 +2465,10 @@ static DECLCALLBACK(int) ahciR3MMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iReg
Assert(cb >= 4352);
/* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
+ /** @todo change this to IOMMMIO_FLAGS_WRITE_ONLY_DWORD once EM/IOM starts
+ * handling 2nd DWORD failures on split accesses correctly. */
rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
- IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_ONLY_DWORD_QWORD,
ahciMMIOWrite, ahciMMIORead, "AHCI");
if (RT_FAILURE(rc))
return rc;
@@ -2586,7 +2634,7 @@ static DECLCALLBACK(int) ahciR3PortQueryDeviceLocation(PPDMIBLOCKPORT pInterface
return VINF_SUCCESS;
}
-#ifdef DEBUG
+#ifdef LOG_ENABLED
/**
* Dump info about the FIS
@@ -2701,7 +2749,8 @@ static void ahciDumpCmdHdrInfo(PAHCIPort pAhciPort, CmdHdr *pCmdHdr)
ahciLog(("%s: Command FIS length %u DW\n", __FUNCTION__, (pCmdHdr->u32DescInf & AHCI_CMDHDR_CFL_MASK)));
ahciLog(("%s: *** End command header info dump. ***\n", __FUNCTION__));
}
-#endif /* DEBUG */
+
+#endif /* LOG_ENABLED */
/**
* Post the first D2H FIS from the device into guest memory.
@@ -2801,8 +2850,8 @@ static int ahciPostFisIntoMemory(PAHCIPort pAhciPort, unsigned uFisType, uint8_t
}
/* Post the FIS into memory. */
- ahciLog(("%s: PDMDevHlpPhysWrite GCPhysAddrRecFis=%RGp cbFis=%u\n", __FUNCTION__, GCPhysAddrRecFis, cbFis));
- PDMDevHlpPhysWrite(pAhciPort->CTX_SUFF(pDevIns), GCPhysAddrRecFis, pCmdFis, cbFis);
+ ahciLog(("%s: PDMDevHlpPCIPhysWrite GCPhysAddrRecFis=%RGp cbFis=%u\n", __FUNCTION__, GCPhysAddrRecFis, cbFis));
+ PDMDevHlpPCIPhysWrite(pAhciPort->CTX_SUFF(pDevIns), GCPhysAddrRecFis, pCmdFis, cbFis);
}
return rc;
@@ -2990,7 +3039,9 @@ static int ahciIdentifySS(PAHCIPort pAhciPort, void *pvBuf)
p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
if ( pAhciPort->pDrvBlock->pfnDiscard
|| ( pAhciPort->fAsyncInterface
- && pAhciPort->pDrvBlockAsync->pfnStartDiscard))
+ && pAhciPort->pDrvBlockAsync->pfnStartDiscard)
+ || pAhciPort->cbSector != 512
+ || pAhciPort->fNonRotational)
{
p[80] = RT_H2LE_U16(0x1f0); /* support everything up to ATA/ATAPI-8 ACS */
p[81] = RT_H2LE_U16(0x28); /* conforms to ATA/ATAPI-8 ACS */
@@ -3012,6 +3063,20 @@ static int ahciIdentifySS(PAHCIPort pAhciPort, void *pvBuf)
p[101] = RT_H2LE_U16(pAhciPort->cTotalSectors >> 16);
p[102] = RT_H2LE_U16(pAhciPort->cTotalSectors >> 32);
p[103] = RT_H2LE_U16(pAhciPort->cTotalSectors >> 48);
+
+ /* valid information, more than one logical sector per physical sector, 2^cLogSectorsPerPhysicalExp logical sectors per physical sector */
+ if (pAhciPort->cLogSectorsPerPhysicalExp)
+ p[106] = RT_H2LE_U16(RT_BIT(14) | RT_BIT(13) | pAhciPort->cLogSectorsPerPhysicalExp);
+
+ if (pAhciPort->cbSector != 512)
+ {
+ uint32_t cSectorSizeInWords = pAhciPort->cbSector / sizeof(uint16_t);
+ /* Enable reporting of logical sector size. */
+ p[106] |= RT_H2LE_U16(RT_BIT(12) | RT_BIT(14));
+ p[117] = RT_H2LE_U16(cSectorSizeInWords);
+ p[118] = RT_H2LE_U16(cSectorSizeInWords >> 16);
+ }
+
if (pAhciPort->fNonRotational)
p[217] = RT_H2LE_U16(1); /* Non-rotational medium */
@@ -3047,6 +3112,7 @@ static int atapiReadTOCRawSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
static int atapiReadTrackInformationSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
static int atapiRequestSenseSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
static int atapiPassthroughSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
+static int atapiReadDVDStructureSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
/**
* Source/sink function indexes for g_apfnAtapiFuncs.
@@ -3069,6 +3135,7 @@ typedef enum ATAPIFN
ATAFN_SS_ATAPI_READ_TRACK_INFORMATION,
ATAFN_SS_ATAPI_REQUEST_SENSE,
ATAFN_SS_ATAPI_PASSTHROUGH,
+ ATAFN_SS_ATAPI_READ_DVD_STRUCTURE,
ATAFN_SS_MAX
} ATAPIFN;
@@ -3093,7 +3160,8 @@ static const PAtapiFunc g_apfnAtapiFuncs[ATAFN_SS_MAX] =
atapiReadTOCRawSS,
atapiReadTrackInformationSS,
atapiRequestSenseSS,
- atapiPassthroughSS
+ atapiPassthroughSS,
+ atapiReadDVDStructureSS
};
static int atapiIdentifySS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
@@ -3804,7 +3872,7 @@ static int atapiReadTOCRawSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbDa
/**
* Sets the given media track type.
*/
-static uint32_t ataMediumTypeSet(PAHCIPort pAhciPort, uint32_t MediaTrackType)
+static uint32_t ahciMediumTypeSet(PAHCIPort pAhciPort, uint32_t MediaTrackType)
{
return ASMAtomicXchgU32(&pAhciPort->MediaTrackType, MediaTrackType);
}
@@ -3967,10 +4035,35 @@ static int atapiPassthroughSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbD
if (RT_SUCCESS(rc))
{
- Assert(cbTransfer <= pAhciReq->cbTransfer);
+ /* Do post processing for certain commands. */
+ switch (pAhciReq->aATAPICmd[0])
+ {
+ case SCSI_SEND_CUE_SHEET:
+ case SCSI_READ_TOC_PMA_ATIP:
+ {
+ if (!pAhciPort->pTrackList)
+ rc = ATAPIPassthroughTrackListCreateEmpty(&pAhciPort->pTrackList);
+
+ if (RT_SUCCESS(rc))
+ rc = ATAPIPassthroughTrackListUpdate(pAhciPort->pTrackList, pAhciReq->aATAPICmd, pvBuf);
+
+ if ( RT_FAILURE(rc)
+ && pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS)
+ LogRel(("AHCI: Error (%Rrc) while updating the tracklist during %s, burning the disc might fail\n",
+ rc, pAhciReq->aATAPICmd[0] == SCSI_SEND_CUE_SHEET ? "SEND CUE SHEET" : "READ TOC/PMA/ATIP"));
+ break;
+ }
+ case SCSI_SYNCHRONIZE_CACHE:
+ {
+ ATAPIPassthroughTrackListClear(pAhciPort->pTrackList);
+ break;
+ }
+ }
if (pAhciReq->enmTxDir == AHCITXDIR_READ)
{
+ Assert(cbTransfer <= pAhciReq->cbTransfer);
+
if (pAhciReq->aATAPICmd[0] == SCSI_INQUIRY)
{
/* Make sure that the real drive cannot be identified.
@@ -3983,37 +4076,6 @@ static int atapiPassthroughSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbD
if (cbTransfer >= 32 + 4)
ataSCSIPadStr((uint8_t *)pvBuf + 32, "1.0", 4);
}
- else if ( pAhciReq->aATAPICmd[0] == SCSI_READ_TOC_PMA_ATIP
- && (pAhciReq->aATAPICmd[2] & 0xf) != 0x05
- && pAhciReq->aATAPICmd[6] != 0xaa)
- {
- /* Set the media type if we can detect it. */
- uint8_t *pbBuf = (uint8_t *)pvBuf;
-
- /** @todo: Implemented only for formatted TOC now. */
- if ( (pAhciReq->aATAPICmd[1] & 0xf) == 0
- && cbTransfer >= 6)
- {
- uint32_t NewMediaType;
- uint32_t OldMediaType;
-
- if (pbBuf[5] & 0x4)
- NewMediaType = ATA_MEDIA_TYPE_DATA;
- else
- NewMediaType = ATA_MEDIA_TYPE_CDDA;
-
- OldMediaType = ataMediumTypeSet(pAhciPort, NewMediaType);
-
- if (OldMediaType != NewMediaType)
- LogRel(("AHCI: LUN#%d: CD-ROM passthrough, detected %s CD\n",
- pAhciPort->iLUN,
- NewMediaType == ATA_MEDIA_TYPE_DATA
- ? "data"
- : "audio"));
- }
- else /* Play safe and set to unknown. */
- ataMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN);
- }
if (cbTransfer)
{
@@ -4058,10 +4120,170 @@ static int atapiPassthroughSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbD
return VINF_SUCCESS;
}
+/** @todo: Revise ASAP. */
+/* Keep in sync with DevATA.cpp! */
+static int atapiReadDVDStructureSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
+{
+ uint8_t aBuf[25]; /* Counted a maximum of 20 bytes but better be on the safe side. */
+ uint8_t *buf = aBuf;
+ int media = pAhciReq->aATAPICmd[1];
+ int format = pAhciReq->aATAPICmd[7];
+
+ uint16_t max_len = ataBE2H_U16(&pAhciReq->aATAPICmd[8]);
+
+ memset(buf, 0, max_len);
+
+ switch (format) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ case 0x0f:
+ case 0x10:
+ case 0x11:
+ case 0x30:
+ case 0x31:
+ case 0xff:
+ if (media == 0)
+ {
+ int uASC = SCSI_ASC_NONE;
+
+ switch (format)
+ {
+ case 0x0: /* Physical format information */
+ {
+ int layer = pAhciReq->aATAPICmd[6];
+ uint64_t total_sectors;
+
+ if (layer != 0)
+ {
+ uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
+ break;
+ }
+
+ total_sectors = pAhciPort->cTotalSectors;
+ total_sectors >>= 2;
+ if (total_sectors == 0)
+ {
+ uASC = -SCSI_ASC_MEDIUM_NOT_PRESENT;
+ break;
+ }
+
+ buf[4] = 1; /* DVD-ROM, part version 1 */
+ buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
+ buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */
+ buf[7] = 0; /* default densities */
+
+ /* FIXME: 0x30000 per spec? */
+ ataH2BE_U32(buf + 8, 0); /* start sector */
+ ataH2BE_U32(buf + 12, total_sectors - 1); /* end sector */
+ ataH2BE_U32(buf + 16, total_sectors - 1); /* l0 end sector */
+
+ /* Size of buffer, not including 2 byte size field */
+ ataH2BE_U32(&buf[0], 2048 + 2);
+
+ /* 2k data + 4 byte header */
+ uASC = (2048 + 4);
+ break;
+ }
+ case 0x01: /* DVD copyright information */
+ buf[4] = 0; /* no copyright data */
+ buf[5] = 0; /* no region restrictions */
+
+ /* Size of buffer, not including 2 byte size field */
+ ataH2BE_U16(buf, 4 + 2);
+
+ /* 4 byte header + 4 byte data */
+ uASC = (4 + 4);
+
+ case 0x03: /* BCA information - invalid field for no BCA info */
+ uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
+ break;
+
+ case 0x04: /* DVD disc manufacturing information */
+ /* Size of buffer, not including 2 byte size field */
+ ataH2BE_U16(buf, 2048 + 2);
+
+ /* 2k data + 4 byte header */
+ uASC = (2048 + 4);
+ break;
+ case 0xff:
+ /*
+ * This lists all the command capabilities above. Add new ones
+ * in order and update the length and buffer return values.
+ */
+
+ buf[4] = 0x00; /* Physical format */
+ buf[5] = 0x40; /* Not writable, is readable */
+ ataH2BE_U16((buf + 6), 2048 + 4);
+
+ buf[8] = 0x01; /* Copyright info */
+ buf[9] = 0x40; /* Not writable, is readable */
+ ataH2BE_U16((buf + 10), 4 + 4);
+
+ buf[12] = 0x03; /* BCA info */
+ buf[13] = 0x40; /* Not writable, is readable */
+ ataH2BE_U16((buf + 14), 188 + 4);
+
+ buf[16] = 0x04; /* Manufacturing info */
+ buf[17] = 0x40; /* Not writable, is readable */
+ ataH2BE_U16((buf + 18), 2048 + 4);
+
+ /* Size of buffer, not including 2 byte size field */
+ ataH2BE_U16(buf, 16 + 2);
+
+ /* data written + 4 byte header */
+ uASC = (16 + 4);
+ break;
+ default: /* TODO: formats beyond DVD-ROM requires */
+ uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
+ }
+
+ if (uASC < 0)
+ {
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, -uASC);
+ return false;
+ }
+ break;
+ }
+ /* TODO: BD support, fall through for now */
+
+ /* Generic disk structures */
+ case 0x80: /* TODO: AACS volume identifier */
+ case 0x81: /* TODO: AACS media serial number */
+ case 0x82: /* TODO: AACS media identifier */
+ case 0x83: /* TODO: AACS media key block */
+ case 0x90: /* TODO: List of recognized format layers */
+ case 0xc0: /* TODO: Write protection status */
+ default:
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST,
+ SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
+ return false;
+ }
+
+ /* Copy the buffer into the scatter gather list. */
+ *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
+ RT_MIN(cbData, max_len));
+
+ atapiCmdOK(pAhciPort, pAhciReq);
+ return false;
+}
+
static int atapiDoTransfer(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, size_t cbMax, ATAPIFN iSourceSink)
{
size_t cbTransfered = 0;
- int rc, rcSourceSink;
+ int rcSourceSink;
rcSourceSink = g_apfnAtapiFuncs[iSourceSink](pAhciReq, pAhciPort, cbMax,
&cbTransfered);
@@ -4072,8 +4294,7 @@ static int atapiDoTransfer(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, size_t cbMax,
LogFlow(("cbTransfered=%d\n", cbTransfered));
/* Write updated command header into memory of the guest. */
- PDMDevHlpPhysWrite(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr,
- &pAhciReq->cmdHdr, sizeof(CmdHdr));
+ PDMDevHlpPCIPhysWrite(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, &pAhciReq->cmdHdr, sizeof(CmdHdr));
return rcSourceSink;
}
@@ -4085,8 +4306,9 @@ static int atapiReadSectors2352PostProcess(PAHCIREQ pAhciReq, void **ppvProc, si
uint32_t iATAPILBA = pAhciReq->uOffset / 2048;
uint8_t *pbBufDst;
uint8_t *pbBufSrc = (uint8_t *)pAhciReq->u.Io.DataSeg.pvSeg;
+ size_t cbAlloc = pAhciReq->cbTransfer + cSectors * (1 + 11 + 3 + 1 + 288); /* Per sector data like ECC. */
- pbBuf = (uint8_t *)RTMemAlloc(pAhciReq->cbTransfer);
+ pbBuf = (uint8_t *)RTMemAlloc(cbAlloc);
if (RT_UNLIKELY(!pbBuf))
return VERR_NO_MEMORY;
@@ -4112,7 +4334,7 @@ static int atapiReadSectors2352PostProcess(PAHCIREQ pAhciReq, void **ppvProc, si
}
*ppvProc = pbBuf;
- *pcbProc = pAhciReq->cbTransfer;
+ *pcbProc = cbAlloc;
return VINF_SUCCESS;
}
@@ -4144,7 +4366,7 @@ static int atapiReadSectors(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint32_t iAT
static AHCITXDIR atapiParseCmdVirtualATAPI(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
{
- AHCITXDIR rc = AHCITXDIR_NONE;
+ AHCITXDIR enmTxDir = AHCITXDIR_NONE;
const uint8_t *pbPacket;
uint32_t cbMax;
@@ -4172,37 +4394,37 @@ static AHCITXDIR atapiParseCmdVirtualATAPI(PAHCIPort pAhciPort, PAHCIREQ pAhciRe
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION);
break;
case SCSI_MODE_SENSE_10:
+ {
+ uint8_t uPageControl, uPageCode;
+ cbMax = ataBE2H_U16(pbPacket + 7);
+ uPageControl = pbPacket[2] >> 6;
+ uPageCode = pbPacket[2] & 0x3f;
+ switch (uPageControl)
{
- uint8_t uPageControl, uPageCode;
- cbMax = ataBE2H_U16(pbPacket + 7);
- uPageControl = pbPacket[2] >> 6;
- uPageCode = pbPacket[2] & 0x3f;
- switch (uPageControl)
- {
- case SCSI_PAGECONTROL_CURRENT:
- switch (uPageCode)
- {
- case SCSI_MODEPAGE_ERROR_RECOVERY:
- atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY);
- break;
- case SCSI_MODEPAGE_CD_STATUS:
- atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS);
- break;
- default:
- goto error_cmd;
- }
- break;
- case SCSI_PAGECONTROL_CHANGEABLE:
- goto error_cmd;
- case SCSI_PAGECONTROL_DEFAULT:
- goto error_cmd;
- default:
- case SCSI_PAGECONTROL_SAVED:
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
- break;
- }
+ case SCSI_PAGECONTROL_CURRENT:
+ switch (uPageCode)
+ {
+ case SCSI_MODEPAGE_ERROR_RECOVERY:
+ atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY);
+ break;
+ case SCSI_MODEPAGE_CD_STATUS:
+ atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS);
+ break;
+ default:
+ goto error_cmd;
+ }
+ break;
+ case SCSI_PAGECONTROL_CHANGEABLE:
+ goto error_cmd;
+ case SCSI_PAGECONTROL_DEFAULT:
+ goto error_cmd;
+ default:
+ case SCSI_PAGECONTROL_SAVED:
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
+ break;
}
break;
+ }
case SCSI_REQUEST_SENSE:
cbMax = pbPacket[4];
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_REQUEST_SENSE);
@@ -4221,224 +4443,224 @@ static AHCITXDIR atapiParseCmdVirtualATAPI(PAHCIPort pAhciPort, PAHCIREQ pAhciRe
break;
case SCSI_READ_10:
case SCSI_READ_12:
- {
- uint32_t cSectors, iATAPILBA;
+ {
+ uint32_t cSectors, iATAPILBA;
- if (pAhciPort->cNotifiedMediaChange > 0)
- {
- pAhciPort->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
- {
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- if (pbPacket[0] == SCSI_READ_10)
- cSectors = ataBE2H_U16(pbPacket + 7);
- else
- cSectors = ataBE2H_U32(pbPacket + 6);
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- if (cSectors == 0)
- {
- atapiCmdOK(pAhciPort, pAhciReq);
- break;
- }
- if ((uint64_t)iATAPILBA + cSectors > pAhciPort->cTotalSectors)
+ if (pAhciPort->cNotifiedMediaChange > 0)
+ {
+ pAhciPort->cNotifiedMediaChange-- ;
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
+ break;
+ }
+ if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
+ {
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ if (pbPacket[0] == SCSI_READ_10)
+ cSectors = ataBE2H_U16(pbPacket + 7);
+ else
+ cSectors = ataBE2H_U32(pbPacket + 6);
+ iATAPILBA = ataBE2H_U32(pbPacket + 2);
+ if (cSectors == 0)
+ {
+ atapiCmdOK(pAhciPort, pAhciReq);
+ break;
+ }
+ if ((uint64_t)iATAPILBA + cSectors > pAhciPort->cTotalSectors)
+ {
+ /* Rate limited logging, one log line per second. For
+ * guests that insist on reading from places outside the
+ * valid area this often generates too many release log
+ * entries otherwise. */
+ static uint64_t s_uLastLogTS = 0;
+ if (RTTimeMilliTS() >= s_uLastLogTS + 1000)
{
- /* Rate limited logging, one log line per second. For
- * guests that insist on reading from places outside the
- * valid area this often generates too many release log
- * entries otherwise. */
- static uint64_t uLastLogTS = 0;
- if (RTTimeMilliTS() >= uLastLogTS + 1000)
- {
- LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors));
- uLastLogTS = RTTimeMilliTS();
- }
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
- break;
+ LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors));
+ s_uLastLogTS = RTTimeMilliTS();
}
- atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2048);
- rc = AHCITXDIR_READ;
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
+ break;
}
+ atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2048);
+ enmTxDir = AHCITXDIR_READ;
break;
+ }
case SCSI_READ_CD:
- {
- uint32_t cSectors, iATAPILBA;
+ {
+ uint32_t cSectors, iATAPILBA;
- if (pAhciPort->cNotifiedMediaChange > 0)
- {
- pAhciPort->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
+ if (pAhciPort->cNotifiedMediaChange > 0)
+ {
+ pAhciPort->cNotifiedMediaChange-- ;
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
+ break;
+ }
+ else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
+ {
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8];
+ iATAPILBA = ataBE2H_U32(pbPacket + 2);
+ if (cSectors == 0)
+ {
+ atapiCmdOK(pAhciPort, pAhciReq);
+ break;
+ }
+ if ((uint64_t)iATAPILBA + cSectors > pAhciPort->cTotalSectors)
+ {
+ /* Rate limited logging, one log line per second. For
+ * guests that insist on reading from places outside the
+ * valid area this often generates too many release log
+ * entries otherwise. */
+ static uint64_t s_uLastLogTS = 0;
+ if (RTTimeMilliTS() >= s_uLastLogTS + 1000)
{
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
+ LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors));
+ s_uLastLogTS = RTTimeMilliTS();
}
- cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8];
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- if (cSectors == 0)
- {
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
+ break;
+ }
+ switch (pbPacket[9] & 0xf8)
+ {
+ case 0x00:
+ /* nothing */
atapiCmdOK(pAhciPort, pAhciReq);
break;
- }
- if ((uint64_t)iATAPILBA + cSectors > pAhciPort->cTotalSectors)
- {
- /* Rate limited logging, one log line per second. For
- * guests that insist on reading from places outside the
- * valid area this often generates too many release log
- * entries otherwise. */
- static uint64_t uLastLogTS = 0;
- if (RTTimeMilliTS() >= uLastLogTS + 1000)
- {
- LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors));
- uLastLogTS = RTTimeMilliTS();
- }
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
+ case 0x10:
+ /* normal read */
+ atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2048);
+ enmTxDir = AHCITXDIR_READ;
+ break;
+ case 0xf8:
+ /* read all data */
+ atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2352);
+ enmTxDir = AHCITXDIR_READ;
+ break;
+ default:
+ LogRel(("AHCI ATAPI: LUN#%d: CD-ROM sector format not supported\n", pAhciPort->iLUN));
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
break;
- }
- switch (pbPacket[9] & 0xf8)
- {
- case 0x00:
- /* nothing */
- atapiCmdOK(pAhciPort, pAhciReq);
- break;
- case 0x10:
- /* normal read */
- atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2048);
- rc = AHCITXDIR_READ;
- break;
- case 0xf8:
- /* read all data */
- atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2352);
- rc = AHCITXDIR_READ;
- break;
- default:
- LogRel(("AHCI ATAPI: LUN#%d: CD-ROM sector format not supported\n", pAhciPort->iLUN));
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- break;
- }
}
break;
+ }
case SCSI_SEEK_10:
+ {
+ uint32_t iATAPILBA;
+ if (pAhciPort->cNotifiedMediaChange > 0)
{
- uint32_t iATAPILBA;
- if (pAhciPort->cNotifiedMediaChange > 0)
- {
- pAhciPort->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
- {
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- if (iATAPILBA > pAhciPort->cTotalSectors)
+ pAhciPort->cNotifiedMediaChange-- ;
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
+ break;
+ }
+ else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
+ {
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ iATAPILBA = ataBE2H_U32(pbPacket + 2);
+ if (iATAPILBA > pAhciPort->cTotalSectors)
+ {
+ /* Rate limited logging, one log line per second. For
+ * guests that insist on seeking to places outside the
+ * valid area this often generates too many release log
+ * entries otherwise. */
+ static uint64_t s_uLastLogTS = 0;
+ if (RTTimeMilliTS() >= s_uLastLogTS + 1000)
{
- /* Rate limited logging, one log line per second. For
- * guests that insist on seeking to places outside the
- * valid area this often generates too many release log
- * entries otherwise. */
- static uint64_t uLastLogTS = 0;
- if (RTTimeMilliTS() >= uLastLogTS + 1000)
- {
- LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA));
- uLastLogTS = RTTimeMilliTS();
- }
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
- break;
+ LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA));
+ s_uLastLogTS = RTTimeMilliTS();
}
- atapiCmdOK(pAhciPort, pAhciReq);
- pAhciReq->uATARegStatus |= ATA_STAT_SEEK; /* Linux expects this. */
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
+ break;
}
+ atapiCmdOK(pAhciPort, pAhciReq);
+ pAhciReq->uATARegStatus |= ATA_STAT_SEEK; /* Linux expects this. */
break;
+ }
case SCSI_START_STOP_UNIT:
+ {
+ int rc = VINF_SUCCESS;
+ switch (pbPacket[4] & 3)
{
- int rc2 = VINF_SUCCESS;
- switch (pbPacket[4] & 3)
+ case 0: /* 00 - Stop motor */
+ case 1: /* 01 - Start motor */
+ break;
+ case 2: /* 10 - Eject media */
{
- case 0: /* 00 - Stop motor */
- case 1: /* 01 - Start motor */
- break;
- case 2: /* 10 - Eject media */
+ /* This must be done from EMT. */
+ PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci);
+ PPDMDEVINS pDevIns = pAhci->CTX_SUFF(pDevIns);
+
+ rc = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
+ (PFNRT)pAhciPort->pDrvMount->pfnUnmount, 3,
+ pAhciPort->pDrvMount, false/*=fForce*/, true/*=fEject*/);
+ Assert(RT_SUCCESS(rc) || rc == VERR_PDM_MEDIA_LOCKED || rc == VERR_PDM_MEDIA_NOT_MOUNTED);
+ if (RT_SUCCESS(rc) && pAhci->pMediaNotify)
{
- /* This must be done from EMT. */
- PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci);
- PPDMDEVINS pDevIns = pAhci->CTX_SUFF(pDevIns);
-
- rc2 = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
- (PFNRT)pAhciPort->pDrvMount->pfnUnmount, 3,
- pAhciPort->pDrvMount, false/*=fForce*/, true/*=fEject*/);
- Assert(RT_SUCCESS(rc2) || (rc2 == VERR_PDM_MEDIA_LOCKED) || (rc2 = VERR_PDM_MEDIA_NOT_MOUNTED));
- if (RT_SUCCESS(rc) && pAhci->pMediaNotify)
- {
- rc2 = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
- (PFNRT)pAhci->pMediaNotify->pfnEjected, 2,
- pAhci->pMediaNotify, pAhciPort->iLUN);
- AssertRC(rc);
- }
- break;
+ rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
+ (PFNRT)pAhci->pMediaNotify->pfnEjected, 2,
+ pAhci->pMediaNotify, pAhciPort->iLUN);
+ AssertRC(rc);
}
- case 3: /* 11 - Load media */
- /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */
- break;
+ break;
}
- if (RT_SUCCESS(rc2))
- atapiCmdOK(pAhciPort, pAhciReq);
- else
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED);
+ case 3: /* 11 - Load media */
+ /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */
+ break;
}
+ if (RT_SUCCESS(rc))
+ atapiCmdOK(pAhciPort, pAhciReq);
+ else
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED);
break;
+ }
case SCSI_MECHANISM_STATUS:
- {
- cbMax = ataBE2H_U16(pbPacket + 8);
- atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MECHANISM_STATUS);
- }
+ {
+ cbMax = ataBE2H_U16(pbPacket + 8);
+ atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MECHANISM_STATUS);
break;
+ }
case SCSI_READ_TOC_PMA_ATIP:
- {
- uint8_t format;
+ {
+ uint8_t format;
- if (pAhciPort->cNotifiedMediaChange > 0)
- {
- pAhciPort->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
+ if (pAhciPort->cNotifiedMediaChange > 0)
+ {
+ pAhciPort->cNotifiedMediaChange-- ;
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
+ break;
+ }
+ else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
+ {
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ cbMax = ataBE2H_U16(pbPacket + 7);
+ /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits),
+ * but Linux kernel uses offset 9 (topmost 2 bits). Hope that
+ * the other field is clear... */
+ format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6);
+ switch (format)
+ {
+ case 0:
+ atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_NORMAL);
break;
- }
- else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
- {
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
+ case 1:
+ atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_MULTI);
+ break;
+ case 2:
+ atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_RAW);
+ break;
+ default:
+ error_cmd:
+ atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
break;
- }
- cbMax = ataBE2H_U16(pbPacket + 7);
- /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits),
- * but Linux kernel uses offset 9 (topmost 2 bits). Hope that
- * the other field is clear... */
- format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6);
- switch (format)
- {
- case 0:
- atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_NORMAL);
- break;
- case 1:
- atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_MULTI);
- break;
- case 2:
- atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_RAW);
- break;
- default:
- error_cmd:
- atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- break;
- }
}
break;
+ }
case SCSI_READ_CAPACITY:
if (pAhciPort->cNotifiedMediaChange > 0)
{
@@ -4492,12 +4714,16 @@ static AHCITXDIR atapiParseCmdVirtualATAPI(PAHCIPort pAhciPort, PAHCIREQ pAhciRe
cbMax = pbPacket[4];
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_INQUIRY);
break;
+ case SCSI_READ_DVD_STRUCTURE:
+ cbMax = ataBE2H_U16(pbPacket + 8);
+ atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_DVD_STRUCTURE);
+ break;
default:
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
break;
}
- return rc;
+ return enmTxDir;
}
/*
@@ -4603,16 +4829,26 @@ static AHCITXDIR atapiParseCmdPassthrough(PAHCIPort pAhciPort, PAHCIREQ pAhciReq
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_READ_CD:
+ case SCSI_READ_CD_MSF:
{
/* Get sector size based on the expected sector type field. */
switch ((pbPacket[1] >> 2) & 0x7)
{
case 0x0: /* All types. */
- if (ASMAtomicReadU32(&pAhciPort->MediaTrackType) == ATA_MEDIA_TYPE_CDDA)
- pAhciReq->cbATAPISector = 2352;
+ {
+ uint32_t iLbaStart;
+
+ if (pbPacket[0] == SCSI_READ_CD)
+ iLbaStart = ataBE2H_U32(&pbPacket[2]);
+ else
+ iLbaStart = ataMSF2LBA(&pbPacket[3]);
+
+ if (pAhciPort->pTrackList)
+ pAhciReq->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pAhciPort->pTrackList, iLbaStart);
else
pAhciReq->cbATAPISector = 2048; /* Might be incorrect if we couldn't determine the type. */
break;
+ }
case 0x1: /* CD-DA */
pAhciReq->cbATAPISector = 2352;
break;
@@ -4633,18 +4869,18 @@ static AHCITXDIR atapiParseCmdPassthrough(PAHCIPort pAhciPort, PAHCIREQ pAhciReq
pAhciReq->cbATAPISector = 0; /** @todo we should probably fail the command here already. */
}
- cbTransfer = ataBE2H_U24(pbPacket + 6) * pAhciReq->cbATAPISector;
+ if (pbPacket[0] == SCSI_READ_CD)
+ cbTransfer = ataBE2H_U24(pbPacket + 6) * pAhciReq->cbATAPISector;
+ else /* SCSI_READ_MSF */
+ {
+ cSectors = ataMSF2LBA(pbPacket + 6) - ataMSF2LBA(pbPacket + 3);
+ if (cSectors > 32)
+ cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */
+ cbTransfer = cSectors * pAhciReq->cbATAPISector;
+ }
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
}
- case SCSI_READ_CD_MSF:
- cSectors = ataMSF2LBA(pbPacket + 6) - ataMSF2LBA(pbPacket + 3);
- if (cSectors > 32)
- cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */
- pAhciReq->cbATAPISector = 2048; /**< @todo this size is not always correct */
- cbTransfer = cSectors * pAhciReq->cbATAPISector;
- enmTxDir = AHCITXDIR_READ;
- goto sendcmd;
case SCSI_READ_DISC_INFORMATION:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_READ;
@@ -4731,10 +4967,14 @@ static AHCITXDIR atapiParseCmdPassthrough(PAHCIPort pAhciPort, PAHCIREQ pAhciReq
case SCSI_VERIFY_10:
goto sendcmd;
case SCSI_WRITE_10:
+ case SCSI_WRITE_AND_VERIFY_10:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U16(pbPacket + 7);
Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
- pAhciReq->cbATAPISector = 2048; /**< @todo this size is not always correct */
+ if (pAhciPort->pTrackList)
+ pAhciReq->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pAhciPort->pTrackList, iATAPILBA);
+ else
+ pAhciReq->cbATAPISector = 2048;
cbTransfer = cSectors * pAhciReq->cbATAPISector;
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
@@ -4742,20 +4982,13 @@ static AHCITXDIR atapiParseCmdPassthrough(PAHCIPort pAhciPort, PAHCIREQ pAhciReq
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U32(pbPacket + 6);
Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
- pAhciReq->cbATAPISector = 2048; /**< @todo this size is not always correct */
+ if (pAhciPort->pTrackList)
+ pAhciReq->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pAhciPort->pTrackList, iATAPILBA);
+ else
+ pAhciReq->cbATAPISector = 2048;
cbTransfer = cSectors * pAhciReq->cbATAPISector;
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
- case SCSI_WRITE_AND_VERIFY_10:
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- cSectors = ataBE2H_U16(pbPacket + 7);
- Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
- /* The sector size is determined by the async I/O thread. */
- pAhciReq->cbATAPISector = 0;
- /* Preliminary, will be corrected once the sector size is known. */
- cbTransfer = cSectors;
- enmTxDir = AHCITXDIR_WRITE;
- goto sendcmd;
case SCSI_WRITE_BUFFER:
switch (pbPacket[1] & 0x1f)
{
@@ -4809,11 +5042,7 @@ static AHCITXDIR atapiParseCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
const uint8_t *pbPacket;
pbPacket = pAhciReq->aATAPICmd;
-#ifdef DEBUG
Log(("%s: LUN#%d CMD=%#04x \"%s\"\n", __FUNCTION__, pAhciPort->iLUN, pbPacket[0], SCSICmdText(pbPacket[0])));
-#else /* !DEBUG */
- Log(("%s: LUN#%d CMD=%#04x\n", __FUNCTION__, pAhciPort->iLUN, pbPacket[0]));
-#endif /* !DEBUG */
Log2(("%s: limit=%#x packet: %.*Rhxs\n", __FUNCTION__, pAhciReq->cmdFis[AHCI_CMDFIS_CYLL] | (pAhciReq->cmdFis[AHCI_CMDFIS_CYLH] << 8), ATAPI_PACKET_SIZE, pbPacket));
if (pAhciPort->fATAPIPassthrough)
@@ -4875,7 +5104,7 @@ static void ahciDeviceReset(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
/**
* Create a PIO setup FIS and post it into the memory area of the guest.
*
- * @returns nothing.
+ * @returns nothing.
* @param pAhciPort The port of the SATA controller.
* @param pAhciReq The state of the task.
* @param pCmdFis Pointer to the command FIS from the guest.
@@ -5219,9 +5448,9 @@ static size_t ahciCopyToPrdtl(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq,
do
{
- uint32_t cPrdtlEntriesRead = (cPrdtlEntries < RT_ELEMENTS(aPrdtlEntries))
- ? cPrdtlEntries
- : RT_ELEMENTS(aPrdtlEntries);
+ uint32_t cPrdtlEntriesRead = cPrdtlEntries < RT_ELEMENTS(aPrdtlEntries)
+ ? cPrdtlEntries
+ : RT_ELEMENTS(aPrdtlEntries);
PDMDevHlpPhysRead(pDevIns, GCPhysPrdtl, &aPrdtlEntries[0], cPrdtlEntriesRead * sizeof(SGLEntry));
@@ -5233,7 +5462,7 @@ static size_t ahciCopyToPrdtl(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq,
cbThisCopy = RT_MIN(cbThisCopy, cbBuf);
/* Copy into SG entry. */
- PDMDevHlpPhysWrite(pDevIns, GCPhysAddrDataBase, pbBuf, cbThisCopy);
+ PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrDataBase, pbBuf, cbThisCopy);
pbBuf += cbThisCopy;
cbBuf -= cbThisCopy;
@@ -5307,6 +5536,7 @@ static size_t ahciCopyFromPrdtl(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq,
* Allocate I/O memory and copies the guest buffer for writes.
*
* @returns VBox status code.
+ * @param pDevIns The device instance.
* @param pAhciReq The request state.
* @param cbTransfer Amount of bytes to allocate.
*/
@@ -5330,6 +5560,15 @@ static int ahciIoBufAllocate(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq, size_t cbTra
return VINF_SUCCESS;
}
+/**
+ * Frees the I/O memory of the given request and updates the guest buffer if necessary.
+ *
+ * @returns nothing.
+ * @param pDevIns The device instance.
+ * @param pAhciReq The request state.
+ * @param fCopyToGuest Flag whether to update the guest buffer if necessary.
+ * Nothing is copied if false even if the request was a read.
+ */
static void ahciIoBufFree(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq,
bool fCopyToGuest)
{
@@ -5348,7 +5587,7 @@ static void ahciIoBufFree(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq,
if (RT_SUCCESS(rc))
{
- ahciCopyToPrdtl(pDevIns, pAhciReq, pv, cb);
+ pAhciReq->cbTransfer = ahciCopyToPrdtl(pDevIns, pAhciReq, pv, cb);
RTMemFree(pv);
}
}
@@ -5393,7 +5632,7 @@ static bool ahciCancelActiveTasks(PAHCIPort pAhciPort)
* a new task structure for this tag.
*/
ASMAtomicWriteNullPtr(&pAhciPort->aCachedTasks[i]);
- LogRel(("AHCI#%dP%d: Cancelled task %u\n", pAhciPort->CTX_SUFF(pDevIns)->iInstance,
+ LogRel(("AHCI#%uP%u: Cancelled task %u\n", pAhciPort->CTX_SUFF(pDevIns)->iInstance,
pAhciPort->iLUN, pAhciReq->uTag));
}
else
@@ -5567,8 +5806,8 @@ static int ahciTrimRangesCreate(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
aRanges[idxRangeSrc] = RT_H2LE_U64(aRanges[idxRangeSrc]);
if (AHCI_RANGE_LENGTH_GET(aRanges[idxRangeSrc]) != 0)
{
- pAhciReq->u.Trim.paRanges[idxRange].offStart = (aRanges[idxRangeSrc] & AHCI_RANGE_LBA_MASK) * 512;
- pAhciReq->u.Trim.paRanges[idxRange].cbRange = AHCI_RANGE_LENGTH_GET(aRanges[idxRangeSrc]) * 512;
+ pAhciReq->u.Trim.paRanges[idxRange].offStart = (aRanges[idxRangeSrc] & AHCI_RANGE_LBA_MASK) * pAhciPort->cbSector;
+ pAhciReq->u.Trim.paRanges[idxRange].cbRange = AHCI_RANGE_LENGTH_GET(aRanges[idxRangeSrc]) * pAhciPort->cbSector;
idxRange++;
}
else
@@ -5603,7 +5842,7 @@ static void ahciTrimRangesDestroy(PAHCIREQ pAhciReq)
* Complete a data transfer task by freeing all occupied resources
* and notifying the guest.
*
- * @returns Flag whether the given request was canceled inbetween.
+ * @returns Flag whether the given request was canceled inbetween;
*
* @param pAhciPort Pointer to the port where to request completed.
* @param pAhciReq Pointer to the task which finished.
@@ -5615,6 +5854,45 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR
bool fXchg = false;
bool fRedo = false;
bool fCanceled = false;
+ uint64_t tsNow = RTTimeMilliTS();
+ AHCITXSTATE enmTxState = AHCITXSTATE_INVALID;
+
+ LogFlowFunc(("pAhciPort=%p pAhciReq=%p rcReq=%d fFreeReq=%RTbool\n",
+ pAhciPort, pAhciReq, rcReq, fFreeReq));
+
+ ASMAtomicReadSize(&pAhciReq->enmTxState, &enmTxState);
+ VBOXDD_AHCI_REQ_COMPLETED(pAhciReq, rcReq, enmTxState, pAhciReq->uOffset, pAhciReq->cbTransfer);
+ VBOXDD_AHCI_REQ_COMPLETED_TIMESTAMP(pAhciReq, tsNow);
+
+ /*
+ * Leave a release log entry if the request was active for more than 25 seconds
+ * (30 seconds is the timeout of the guest).
+ */
+ if (tsNow - pAhciReq->tsStart >= 25 * 1000)
+ {
+ const char *pcszReq = NULL;
+
+ switch (pAhciReq->enmTxDir)
+ {
+ case AHCITXDIR_READ:
+ pcszReq = "Read";
+ break;
+ case AHCITXDIR_WRITE:
+ pcszReq = "Write";
+ break;
+ case AHCITXDIR_FLUSH:
+ pcszReq = "Flush";
+ break;
+ case AHCITXDIR_TRIM:
+ pcszReq = "Trim";
+ break;
+ default:
+ pcszReq = "<Invalid>";
+ }
+
+ LogRel(("AHCI#%uP%u: %s request was active for %llu seconds\n",
+ pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, pcszReq, (tsNow - pAhciReq->tsStart) / 1000));
+ }
ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg);
@@ -5644,14 +5922,14 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR
if (pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS)
{
if (pAhciReq->enmTxDir == AHCITXDIR_FLUSH)
- LogRel(("AHCI#%u: Flush returned rc=%Rrc\n",
- pAhciPort->iLUN, rcReq));
+ LogRel(("AHCI#%uP%u: Flush returned rc=%Rrc\n",
+ pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, rcReq));
else if (pAhciReq->enmTxDir == AHCITXDIR_TRIM)
- LogRel(("AHCI#%u: Trim returned rc=%Rrc\n",
- pAhciPort->iLUN, rcReq));
+ LogRel(("AHCI#%uP%u: Trim returned rc=%Rrc\n",
+ pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, rcReq));
else
- LogRel(("AHCI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
- pAhciPort->iLUN,
+ LogRel(("AHCI#%uP%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
+ pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN,
pAhciReq->enmTxDir == AHCITXDIR_READ
? "Read"
: "Write",
@@ -5668,7 +5946,7 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR
ASMAtomicCmpXchgPtr(&pAhciPort->pTaskErr, pAhciReq, NULL);
}
else
- ASMAtomicOrU32(&pAhciPort->u32TasksNew, RT_BIT_32(pAhciReq->uTag));
+ ASMAtomicOrU32(&pAhciPort->u32TasksRedo, RT_BIT_32(pAhciReq->uTag));
}
else
{
@@ -5682,8 +5960,7 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR
}
/* Write updated command header into memory of the guest. */
- PDMDevHlpPhysWrite(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr,
- &pAhciReq->cmdHdr, sizeof(CmdHdr));
+ PDMDevHlpPCIPhysWrite(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, &pAhciReq->cmdHdr, sizeof(CmdHdr));
if (pAhciReq->fFlags & AHCI_REQ_OVERFLOW)
{
@@ -5749,14 +6026,14 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR
if (pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS)
{
if (pAhciReq->enmTxDir == AHCITXDIR_FLUSH)
- LogRel(("AHCI#%u: Canceled flush returned rc=%Rrc\n",
- pAhciPort->iLUN, rcReq));
+ LogRel(("AHCI#%uP%u: Canceled flush returned rc=%Rrc\n",
+ pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, rcReq));
else if (pAhciReq->enmTxDir == AHCITXDIR_TRIM)
- LogRel(("AHCI#%u: Canceled trim returned rc=%Rrc\n",
- pAhciPort->iLUN, rcReq));
+ LogRel(("AHCI#%uP%u: Canceled trim returned rc=%Rrc\n",
+ pAhciPort->CTX_SUFF(pDevIns)->iInstance,pAhciPort->iLUN, rcReq));
else
- LogRel(("AHCI#%u: Canceled %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
- pAhciPort->iLUN,
+ LogRel(("AHCI#%uP%u: Canceled %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
+ pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN,
pAhciReq->enmTxDir == AHCITXDIR_READ
? "read"
: "write",
@@ -5769,6 +6046,9 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR
RTMemFree(pAhciReq);
}
+ if (pAhciPort->cTasksActive == 0 && pAhciPort->pAhciR3->fSignalIdle)
+ PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3);
+
return fCanceled;
}
@@ -5780,7 +6060,7 @@ static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcR
* @param pvUser User data.
* @param rcReq IPRT Status code of the completed request.
*/
-static DECLCALLBACK(int) ahciTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rcReq)
+static DECLCALLBACK(int) ahciR3TransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rcReq)
{
PAHCIPort pAhciPort = PDMIBLOCKASYNCPORT_2_PAHCIPORT(pInterface);
PAHCIREQ pAhciReq = (PAHCIREQ)pvUser;
@@ -5788,11 +6068,9 @@ static DECLCALLBACK(int) ahciTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterfa
ahciLog(("%s: pInterface=%p pvUser=%p uTag=%u\n",
__FUNCTION__, pInterface, pvUser, pAhciReq->uTag));
- int rc = ahciTransferComplete(pAhciPort, pAhciReq, rcReq, true);
+ ahciTransferComplete(pAhciPort, pAhciReq, rcReq, true);
- if (pAhciPort->cTasksActive == 0 && pAhciPort->pAhciR3->fSignalIdle)
- PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3);
- return rc;
+ return VINF_SUCCESS;
}
/**
@@ -5803,7 +6081,7 @@ static DECLCALLBACK(int) ahciTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterfa
*/
static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t *pCmdFis)
{
- AHCITXDIR rc = AHCITXDIR_NONE;
+ AHCITXDIR enmTxDir = AHCITXDIR_NONE;
bool fLBA48 = false;
CmdHdr *pCmdHdr = &pAhciReq->cmdHdr;
@@ -5817,7 +6095,6 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t
{
if (pAhciPort->pDrvBlock && !pAhciPort->fATAPI)
{
- int rc2;
uint16_t u16Temp[256];
size_t cbCopied;
@@ -5854,10 +6131,11 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK;
break;
case 0x82: /* write cache disable */
- rc = AHCITXDIR_FLUSH;
+ enmTxDir = AHCITXDIR_FLUSH;
break;
case 0x03:
- { /* set transfer mode */
+ {
+ /* set transfer mode */
Log2(("%s: transfer mode %#04x\n", __FUNCTION__, pCmdFis[AHCI_CMDFIS_SECTC]));
switch (pCmdFis[AHCI_CMDFIS_SECTC] & 0xf8)
{
@@ -5895,7 +6173,7 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t
}
case ATA_FLUSH_CACHE_EXT:
case ATA_FLUSH_CACHE:
- rc = AHCITXDIR_FLUSH;
+ enmTxDir = AHCITXDIR_FLUSH;
break;
case ATA_PACKET:
if (!pAhciPort->fATAPI)
@@ -5904,7 +6182,7 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
}
else
- rc = atapiParseCmd(pAhciPort, pAhciReq);
+ enmTxDir = atapiParseCmd(pAhciPort, pAhciReq);
break;
case ATA_IDENTIFY_PACKET_DEVICE:
if (!pAhciPort->fATAPI)
@@ -5957,34 +6235,34 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t
fLBA48 = true;
case ATA_READ_DMA:
{
- pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * 512;
- pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * 512;
- rc = AHCITXDIR_READ;
+ pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * pAhciPort->cbSector;
+ pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * pAhciPort->cbSector;
+ enmTxDir = AHCITXDIR_READ;
break;
}
case ATA_WRITE_DMA_EXT:
fLBA48 = true;
case ATA_WRITE_DMA:
{
- pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * 512;
- pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * 512;
- rc = AHCITXDIR_WRITE;
+ pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * pAhciPort->cbSector;
+ pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * pAhciPort->cbSector;
+ enmTxDir = AHCITXDIR_WRITE;
break;
}
case ATA_READ_FPDMA_QUEUED:
{
- pAhciReq->cbTransfer = ahciGetNSectorsQueued(pCmdFis) * 512;
- pAhciReq->uOffset = ahciGetSectorQueued(pCmdFis) * 512;
+ pAhciReq->cbTransfer = ahciGetNSectorsQueued(pCmdFis) * pAhciPort->cbSector;
+ pAhciReq->uOffset = ahciGetSectorQueued(pCmdFis) * pAhciPort->cbSector;
pAhciReq->fFlags |= AHCI_REQ_IS_QUEUED;
- rc = AHCITXDIR_READ;
+ enmTxDir = AHCITXDIR_READ;
break;
}
case ATA_WRITE_FPDMA_QUEUED:
{
- pAhciReq->cbTransfer = ahciGetNSectorsQueued(pCmdFis) * 512;
- pAhciReq->uOffset = ahciGetSectorQueued(pCmdFis) * 512;
+ pAhciReq->cbTransfer = ahciGetNSectorsQueued(pCmdFis) * pAhciPort->cbSector;
+ pAhciReq->uOffset = ahciGetSectorQueued(pCmdFis) * pAhciPort->cbSector;
pAhciReq->fFlags |= AHCI_REQ_IS_QUEUED;
- rc = AHCITXDIR_WRITE;
+ enmTxDir = AHCITXDIR_WRITE;
break;
}
case ATA_READ_LOG_EXT:
@@ -6065,7 +6343,7 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK;
}
else
- rc = AHCITXDIR_TRIM;
+ enmTxDir = AHCITXDIR_TRIM;
break;
}
/* else: fall through and report error to the guest. */
@@ -6084,20 +6362,22 @@ static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
}
- return rc;
+ return enmTxDir;
}
/**
* Retrieve a command FIS from guest memory.
*
- * @returns nothing
+ * @returns whether the H2D FIS was successfully read from the guest memory.
* @param pAhciReq The state of the actual task.
*/
-static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
+static bool ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
{
RTGCPHYS GCPhysAddrCmdTbl;
- AssertMsg(pAhciPort->GCPhysAddrClb && pAhciPort->GCPhysAddrFb, ("%s: GCPhysAddrClb and/or GCPhysAddrFb are 0\n", __FUNCTION__));
+ AssertMsgReturn(pAhciPort->GCPhysAddrClb && pAhciPort->GCPhysAddrFb,
+ ("%s: GCPhysAddrClb and/or GCPhysAddrFb are 0\n", __FUNCTION__),
+ false);
/*
* First we are reading the command header pointed to by regCLB.
@@ -6109,20 +6389,25 @@ static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
pAhciReq->GCPhysCmdHdrAddr, sizeof(CmdHdr)));
PDMDevHlpPhysRead(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, &pAhciReq->cmdHdr, sizeof(CmdHdr));
-#ifdef DEBUG
+#ifdef LOG_ENABLED
/* Print some infos about the command header. */
ahciDumpCmdHdrInfo(pAhciPort, &pAhciReq->cmdHdr);
#endif
GCPhysAddrCmdTbl = AHCI_RTGCPHYS_FROM_U32(pAhciReq->cmdHdr.u32CmdTblAddrUp, pAhciReq->cmdHdr.u32CmdTblAddr);
- AssertMsg((pAhciReq->cmdHdr.u32DescInf & AHCI_CMDHDR_CFL_MASK) * sizeof(uint32_t) == AHCI_CMDFIS_TYPE_H2D_SIZE,
- ("This is not a command FIS!!\n"));
+ AssertMsgReturn((pAhciReq->cmdHdr.u32DescInf & AHCI_CMDHDR_CFL_MASK) * sizeof(uint32_t) == AHCI_CMDFIS_TYPE_H2D_SIZE,
+ ("This is not a command FIS!!\n"),
+ false);
/* Read the command Fis. */
LogFlow(("%s: PDMDevHlpPhysRead GCPhysAddrCmdTbl=%RGp cbCmdFis=%u\n", __FUNCTION__, GCPhysAddrCmdTbl, AHCI_CMDFIS_TYPE_H2D_SIZE));
PDMDevHlpPhysRead(pAhciPort->CTX_SUFF(pDevIns), GCPhysAddrCmdTbl, &pAhciReq->cmdFis[0], AHCI_CMDFIS_TYPE_H2D_SIZE);
+ AssertMsgReturn(pAhciReq->cmdFis[AHCI_CMDFIS_TYPE] == AHCI_CMDFIS_TYPE_H2D,
+ ("This is not a command FIS\n"),
+ false);
+
/* Set transfer direction. */
pAhciReq->enmTxDir = (pAhciReq->cmdHdr.u32DescInf & AHCI_CMDHDR_W) ? AHCITXDIR_WRITE : AHCITXDIR_READ;
@@ -6147,7 +6432,7 @@ static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
pAhciReq->GCPhysPrdtl = AHCI_RTGCPHYS_FROM_U32(pAhciReq->cmdHdr.u32CmdTblAddrUp, pAhciReq->cmdHdr.u32CmdTblAddr) + AHCI_CMDHDR_PRDT_OFFSET;
pAhciReq->cPrdtlEntries = AHCI_CMDHDR_PRDTL_ENTRIES(pAhciReq->cmdHdr.u32DescInf);
-#ifdef DEBUG
+#ifdef LOG_ENABLED
/* Print some infos about the FIS. */
ahciDumpFisInfo(pAhciPort, &pAhciReq->cmdFis[0]);
@@ -6168,6 +6453,8 @@ static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
GCPhysPrdtl += sizeof(SGLEntry);
}
#endif
+
+ return true;
}
/**
@@ -6182,25 +6469,73 @@ static void ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
{
PDEVPORTNOTIFIERQUEUEITEM pNotifierItem = (PDEVPORTNOTIFIERQUEUEITEM)pItem;
- PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
- PAHCIPort pAhciPort = &pAhci->ahciPort[pNotifierItem->iPort];
+ PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
+ PAHCIPort pAhciPort = &pThis->ahciPort[pNotifierItem->iPort];
int rc = VINF_SUCCESS;
- if (!pAhciPort->fAsyncInterface)
- {
- ahciLog(("%s: Got notification from GC\n", __FUNCTION__));
- /* Notify the async IO thread. */
- rc = RTSemEventSignal(pAhciPort->AsyncIORequestSem);
- AssertRC(rc);
- }
- else
+ ahciLog(("%s: Got notification from GC\n", __FUNCTION__));
+ /* Notify the async IO thread. */
+ rc = SUPSemEventSignal(pThis->pSupDrvSession, pAhciPort->hEvtProcess);
+ AssertRC(rc);
+
+ return true;
+}
+
+/* The async IO thread for one port. */
+static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ PAHCIPort pAhciPort = (PAHCIPort)pThread->pvUser;
+ PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci);
+ int rc = VINF_SUCCESS;
+ uint64_t u64StartTime = 0;
+ uint64_t u64StopTime = 0;
+ uint32_t uIORequestsProcessed = 0;
+ uint32_t uIOsPerSec = 0;
+ uint32_t fTasksToProcess = 0;
+
+ ahciLog(("%s: Port %d entering async IO loop.\n", __FUNCTION__, pAhciPort->iLUN));
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
{
unsigned idx = 0;
- uint32_t u32Tasks = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0);
+ uint32_t u32Tasks = 0;
+ uint32_t u32RegHbaCtrl = 0;
+
+ ASMAtomicWriteBool(&pAhciPort->fWrkThreadSleeping, true);
+ u32Tasks = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0);
+ if (!u32Tasks)
+ {
+ Assert(ASMAtomicReadBool(&pAhciPort->fWrkThreadSleeping));
+ rc = SUPSemEventWaitNoResume(pAhci->pSupDrvSession, pAhciPort->hEvtProcess, RT_INDEFINITE_WAIT);
+ AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
+ if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
+ break;
+ LogFlowFunc(("Woken up with rc=%Rrc\n", rc));
+ u32Tasks = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0);
+ }
+
+ ASMAtomicWriteBool(&pAhciPort->fWrkThreadSleeping, false);
+ ASMAtomicIncU32(&pAhci->cThreadsActive);
+
+ /*
+ * Check whether the global host controller bit is set and go to sleep immediately again
+ * if it is set.
+ */
+ u32RegHbaCtrl = ASMAtomicReadU32(&pAhci->regHbaCtrl);
+ if ( u32RegHbaCtrl & AHCI_HBA_CTRL_HR
+ && !ASMAtomicDecU32(&pAhci->cThreadsActive))
+ {
+ ahciHBAReset(pAhci);
+ continue;
+ }
idx = ASMBitFirstSetU32(u32Tasks);
while (idx)
{
+ bool fReqCanceled = false;
AHCITXDIR enmTxDir;
PAHCIREQ pAhciReq;
@@ -6226,6 +6561,7 @@ static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEI
ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_ACTIVE, AHCITXSTATE_FREE, fXchg);
AssertMsg(fXchg, ("Task is already active\n"));
+ pAhciReq->tsStart = RTTimeMilliTS();
pAhciReq->uATARegStatus = 0;
pAhciReq->uATARegError = 0;
pAhciReq->fFlags = 0;
@@ -6234,7 +6570,23 @@ static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEI
pAhciReq->uTag = idx;
ASMAtomicWriteU32(&pAhciPort->u32CurrentCommandSlot, pAhciReq->uTag);
- ahciPortTaskGetCommandFis(pAhciPort, pAhciReq);
+ bool fFisRead = ahciPortTaskGetCommandFis(pAhciPort, pAhciReq);
+ if (RT_UNLIKELY(!fFisRead))
+ {
+ /*
+ * Couldn't find anything in either the AHCI or SATA spec which
+ * indicates what should be done if the FIS is not read successfully.
+ * The closest thing is in the state machine, stating that the device
+ * should go into idle state again (SATA spec 1.0 chapter 8.7.1).
+ * Do the same here and ignore any corrupt FIS types, after all
+ * the guest messed up everything and this behavior is undefined.
+ */
+ ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg);
+ Assert(fXchg);
+ u32Tasks &= ~RT_BIT_32(idx); /* Clear task bit. */
+ idx = ASMBitFirstSetU32(u32Tasks);
+ continue;
+ }
/* Mark the task as processed by the HBA if this is a queued task so that it doesn't occur in the CI register anymore. */
if (pAhciPort->regSACT & (1 << idx))
@@ -6251,21 +6603,15 @@ static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEI
ahciLog(("%s: Setting device into reset state\n", __FUNCTION__));
pAhciPort->fResetDevice = true;
ahciSendD2HFis(pAhciPort, pAhciReq, pAhciReq->cmdFis, true);
-
- ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg);
- AssertMsg(fXchg, ("Task is not active\n"));
- return true;
}
else if (pAhciPort->fResetDevice) /* The bit is not set and we are in a reset state. */
- {
ahciFinishStorageDeviceReset(pAhciPort, pAhciReq);
-
- ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg);
- AssertMsg(fXchg, ("Task is not active\n"));
- return true;
- }
else /* We are not in a reset state update the control registers. */
AssertMsgFailed(("%s: Update the control register\n", __FUNCTION__));
+
+ ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg);
+ AssertMsg(fXchg, ("Task is not active\n"));
+ break;
}
else
{
@@ -6290,282 +6636,106 @@ static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEI
if (!(pAhciReq->fFlags & AHCI_REQ_OVERFLOW))
{
- if (enmTxDir == AHCITXDIR_FLUSH)
- {
- rc = pAhciPort->pDrvBlockAsync->pfnStartFlush(pAhciPort->pDrvBlockAsync,
- pAhciReq);
- }
- else if (enmTxDir == AHCITXDIR_TRIM)
+ if (pAhciPort->fAsyncInterface)
{
- rc = ahciTrimRangesCreate(pAhciPort, pAhciReq);
- if (RT_SUCCESS(rc))
+ VBOXDD_AHCI_REQ_SUBMIT(pAhciReq, enmTxDir, pAhciReq->uOffset, pAhciReq->cbTransfer);
+ VBOXDD_AHCI_REQ_SUBMIT_TIMESTAMP(pAhciReq, pAhciReq->tsStart);
+ if (enmTxDir == AHCITXDIR_FLUSH)
+ {
+ rc = pAhciPort->pDrvBlockAsync->pfnStartFlush(pAhciPort->pDrvBlockAsync,
+ pAhciReq);
+ }
+ else if (enmTxDir == AHCITXDIR_TRIM)
+ {
+ rc = ahciTrimRangesCreate(pAhciPort, pAhciReq);
+ if (RT_SUCCESS(rc))
+ {
+ pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
+ rc = pAhciPort->pDrvBlockAsync->pfnStartDiscard(pAhciPort->pDrvBlockAsync, pAhciReq->u.Trim.paRanges,
+ pAhciReq->u.Trim.cRanges, pAhciReq);
+ }
+ }
+ else if (enmTxDir == AHCITXDIR_READ)
+ {
+ pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
+ rc = pAhciPort->pDrvBlockAsync->pfnStartRead(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset,
+ &pAhciReq->u.Io.DataSeg, 1,
+ pAhciReq->cbTransfer,
+ pAhciReq);
+ }
+ else
{
pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
- rc = pAhciPort->pDrvBlockAsync->pfnStartDiscard(pAhciPort->pDrvBlockAsync, pAhciReq->u.Trim.paRanges,
- pAhciReq->u.Trim.cRanges, pAhciReq);
+ rc = pAhciPort->pDrvBlockAsync->pfnStartWrite(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset,
+ &pAhciReq->u.Io.DataSeg, 1,
+ pAhciReq->cbTransfer,
+ pAhciReq);
}
- }
- else if (enmTxDir == AHCITXDIR_READ)
- {
- pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
- rc = pAhciPort->pDrvBlockAsync->pfnStartRead(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset,
- &pAhciReq->u.Io.DataSeg, 1,
- pAhciReq->cbTransfer,
- pAhciReq);
+ if (rc == VINF_VD_ASYNC_IO_FINISHED)
+ fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true);
+ else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
+ fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, true);
}
else
{
- pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
- rc = pAhciPort->pDrvBlockAsync->pfnStartWrite(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset,
- &pAhciReq->u.Io.DataSeg, 1,
- pAhciReq->cbTransfer,
- pAhciReq);
+ if (enmTxDir == AHCITXDIR_FLUSH)
+ rc = pAhciPort->pDrvBlock->pfnFlush(pAhciPort->pDrvBlock);
+ else if (enmTxDir == AHCITXDIR_TRIM)
+ {
+ rc = ahciTrimRangesCreate(pAhciPort, pAhciReq);
+ if (RT_SUCCESS(rc))
+ {
+ pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
+ rc = pAhciPort->pDrvBlock->pfnDiscard(pAhciPort->pDrvBlock, pAhciReq->u.Trim.paRanges,
+ pAhciReq->u.Trim.cRanges);
+ pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0;
+ }
+ }
+ else if (enmTxDir == AHCITXDIR_READ)
+ {
+ pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
+ rc = pAhciPort->pDrvBlock->pfnRead(pAhciPort->pDrvBlock, pAhciReq->uOffset,
+ pAhciReq->u.Io.DataSeg.pvSeg,
+ pAhciReq->cbTransfer);
+ pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 0;
+ }
+ else
+ {
+ pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
+ rc = pAhciPort->pDrvBlock->pfnWrite(pAhciPort->pDrvBlock, pAhciReq->uOffset,
+ pAhciReq->u.Io.DataSeg.pvSeg,
+ pAhciReq->cbTransfer);
+ pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0;
+ }
+ fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, true);
}
- if (rc == VINF_VD_ASYNC_IO_FINISHED)
- rc = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true);
- else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
- rc = ahciTransferComplete(pAhciPort, pAhciReq, rc, true);
}
}
else
- rc = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true);
+ fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true);
} /* Command */
- u32Tasks &= ~RT_BIT_32(idx); /* Clear task bit. */
- idx = ASMBitFirstSetU32(u32Tasks);
- } /* while tasks available */
- } /* fUseAsyncInterface */
-
- return true;
-}
-
-/* The async IO thread for one port. */
-static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
-{
- PAHCIPort pAhciPort = (PAHCIPort)pThread->pvUser;
- PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci);
- PAHCIREQ pAhciReq;
- int rc = VINF_SUCCESS;
- uint64_t u64StartTime = 0;
- uint64_t u64StopTime = 0;
- uint32_t uIORequestsProcessed = 0;
- uint32_t uIOsPerSec = 0;
- uint32_t fTasksToProcess = 0;
- unsigned idx = 0;
-
- ahciLog(("%s: Port %d entering async IO loop.\n", __FUNCTION__, pAhciPort->iLUN));
-
- if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
- return VINF_SUCCESS;
-
- /* We use only one task structure. */
- pAhciReq = (PAHCIREQ)RTMemAllocZ(sizeof(AHCIREQ));
- if (!pAhciReq)
- {
- AssertMsgFailed(("Failed to allocate task state memory\n"));
- return VERR_NO_MEMORY;
- }
-
- pAhciReq->enmTxState = AHCITXSTATE_FREE;
-
- while (pThread->enmState == PDMTHREADSTATE_RUNNING)
- {
- /* New run to get number of I/O requests per second?. */
- if (!u64StartTime)
- u64StartTime = RTTimeMilliTS();
-
- ASMAtomicXchgBool(&pAhciPort->fAsyncIOThreadIdle, true);
- if (pAhci->fSignalIdle)
- PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3);
-
- rc = RTSemEventWait(pAhciPort->AsyncIORequestSem, 1000);
- if (rc == VERR_TIMEOUT)
- {
- /* No I/O requests in-between. Reset statistics and wait again. */
- pAhciPort->StatIORequestsPerSecond.c = 0;
- rc = RTSemEventWait(pAhciPort->AsyncIORequestSem, RT_INDEFINITE_WAIT);
- }
-
- if (RT_FAILURE(rc) || (pThread->enmState != PDMTHREADSTATE_RUNNING))
- break;
-
- /* Go to sleep again if we are in redo mode. */
- if (RT_UNLIKELY(pAhciPort->fRedo))
- continue;
-
- AssertMsg(pAhciPort->pDrvBase, ("I/O thread without attached device?!\n"));
-
- ASMAtomicXchgBool(&pAhciPort->fAsyncIOThreadIdle, false);
- fTasksToProcess = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0);
-
- idx = ASMBitFirstSetU32(fTasksToProcess);
-
- /* Process commands. */
- while ( idx
- && RT_LIKELY(!pAhciPort->fPortReset))
- {
- bool fReqCanceled = false;
- AHCITXDIR enmTxDir;
-
- idx--;
- STAM_PROFILE_START(&pAhciPort->StatProfileProcessTime, a);
-
- pAhciReq->uATARegStatus = 0;
- pAhciReq->uATARegError = 0;
- pAhciReq->fFlags = 0;
- pAhciReq->uTag = idx;
- AssertMsg(pAhciReq->uTag < AHCI_NR_COMMAND_SLOTS, ("%s: Invalid Tag number %u!!\n", __FUNCTION__, pAhciReq->uTag));
-
- bool fXchg;
- ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_ACTIVE, AHCITXSTATE_FREE, fXchg);
- AssertMsg(fXchg, ("Task is already active\n"));
-
- /* Set current command slot */
- ASMAtomicWriteU32(&pAhciPort->u32CurrentCommandSlot, pAhciReq->uTag);
- pAhciPort->aCachedTasks[0] = pAhciReq; /* Make cancelling the request possible. */
-
- /* Mark the task as processed by the HBA if this is a queued task so that it doesn't occur in the CI register anymore. */
- if (pAhciPort->regSACT & (1 << idx))
- {
- pAhciReq->fFlags |= AHCI_REQ_CLEAR_SACT;
- ASMAtomicOrU32(&pAhciPort->u32TasksFinished, (1 << pAhciReq->uTag));
- }
-
- ahciPortTaskGetCommandFis(pAhciPort, pAhciReq);
-
- ahciLog(("%s: Got command at slot %d\n", __FUNCTION__, pAhciReq->uTag));
-
- if (!(pAhciReq->cmdFis[AHCI_CMDFIS_BITS] & AHCI_CMDFIS_C))
- {
- /* If the reset bit is set put the device into reset state. */
- if (pAhciReq->cmdFis[AHCI_CMDFIS_CTL] & AHCI_CMDFIS_CTL_SRST)
- {
- ahciLog(("%s: Setting device into reset state\n", __FUNCTION__));
- pAhciPort->fResetDevice = true;
- ahciSendD2HFis(pAhciPort, pAhciReq, &pAhciReq->cmdFis[0], true);
- }
- else if (pAhciPort->fResetDevice) /* The bit is not set and we are in a reset state. */
- {
- ahciFinishStorageDeviceReset(pAhciPort, pAhciReq);
- }
- /* TODO: We are not in a reset state update the control registers. */
-
- ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg);
- AssertMsg(fXchg, ("Task is already free\n"));
- }
- else
- {
- AssertReleaseMsg(ASMAtomicReadU32(&pAhciPort->cTasksActive) < AHCI_NR_COMMAND_SLOTS,
- ("There are more than 32 requests active"));
- ASMAtomicIncU32(&pAhciPort->cTasksActive);
- enmTxDir = ahciProcessCmd(pAhciPort, pAhciReq, &pAhciReq->cmdFis[0]);
- pAhciReq->enmTxDir = enmTxDir;
-
- if (enmTxDir == AHCITXDIR_FLUSH)
- rc = pAhciPort->pDrvBlock->pfnFlush(pAhciPort->pDrvBlock);
- else if (enmTxDir == AHCITXDIR_TRIM)
- {
- rc = ahciTrimRangesCreate(pAhciPort, pAhciReq);
- if (RT_SUCCESS(rc))
- {
- pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
- rc = pAhciPort->pDrvBlock->pfnDiscard(pAhciPort->pDrvBlock,
- pAhciReq->u.Trim.paRanges,
- pAhciReq->u.Trim.cRanges);
- pAhciPort->Led.Actual.s.fWriting = 0;
- }
- }
- else if (enmTxDir != AHCITXDIR_NONE)
- {
- uint64_t uOffset = 0;
- size_t cbTransfer = 0;
-
- rc = ahciIoBufAllocate(pAhciPort->pDevInsR3, pAhciReq, pAhciReq->cbTransfer);
- if (RT_FAILURE(rc))
- AssertMsgFailed(("%s: Failed to get number of list elments %Rrc\n", __FUNCTION__, rc));
-
- if (!(pAhciReq->fFlags & AHCI_REQ_OVERFLOW))
- {
- STAM_REL_COUNTER_INC(&pAhciPort->StatDMA);
-
- /* Initialize all values. */
- uOffset = pAhciReq->uOffset;
- cbTransfer = pAhciReq->cbTransfer;
-
- STAM_PROFILE_START(&pAhciPort->StatProfileReadWrite, b);
-
- AssertMsg(!(uOffset % 512), ("Offset is not sector aligned %llu\n", uOffset));
- AssertMsg(!(cbTransfer % 512), ("Number of bytes to process is not sector aligned %lu\n", cbTransfer));
-
- if (enmTxDir == AHCITXDIR_READ)
- {
- pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
- rc = pAhciPort->pDrvBlock->pfnRead(pAhciPort->pDrvBlock, uOffset,
- pAhciReq->u.Io.DataSeg.pvSeg,
- cbTransfer);
- pAhciPort->Led.Actual.s.fReading = 0;
- STAM_REL_COUNTER_ADD(&pAhciPort->StatBytesRead, cbTransfer);
- }
- else
- {
- pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
- rc = pAhciPort->pDrvBlock->pfnWrite(pAhciPort->pDrvBlock, uOffset,
- pAhciReq->u.Io.DataSeg.pvSeg,
- cbTransfer);
- pAhciPort->Led.Actual.s.fWriting = 0;
- STAM_REL_COUNTER_ADD(&pAhciPort->StatBytesWritten, cbTransfer);
- }
-
- STAM_PROFILE_STOP(&pAhciPort->StatProfileReadWrite, b);
- }
- }
-
- fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, false /* fFreeReq */);
- uIORequestsProcessed++;
- STAM_PROFILE_STOP(&pAhciPort->StatProfileProcessTime, a);
- }
-
- if (!pAhciPort->fRedo)
- {
-#ifdef DEBUG
- /* Be paranoid. */
- memset(&pAhciReq->cmdHdr, 0, sizeof(CmdHdr));
- memset(&pAhciReq->cmdFis, 0, AHCI_CMDFIS_TYPE_H2D_SIZE);
- pAhciReq->GCPhysCmdHdrAddr = 0;
- pAhciReq->uOffset = 0;
- pAhciReq->cbTransfer = 0;
-#endif
- }
-
/*
* Don't process other requests if the last one was canceled,
* the others are not valid anymore.
*/
if (fReqCanceled)
break;
- fTasksToProcess &= ~(1 << idx);
- idx = ASMBitFirstSetU32(fTasksToProcess);
- } /* while tasks to process */
-
- u64StopTime = RTTimeMilliTS();
- /* Check if one second has passed. */
- if (u64StopTime - u64StartTime >= 1000)
- {
- /* Calculate number of I/O requests per second. */
- uIOsPerSec = uIORequestsProcessed / ((u64StopTime - u64StartTime) / 1000);
- ahciLog(("%s: Processed %u requests in %llu ms -> %u requests/s\n", __FUNCTION__, uIORequestsProcessed, u64StopTime - u64StartTime, uIOsPerSec));
- u64StartTime = 0;
- uIORequestsProcessed = 0;
- /* For the release statistics. There is no macro to set the counter to a specific value. */
- pAhciPort->StatIORequestsPerSecond.c = uIOsPerSec;
- }
- } /* While running */
- if (pAhci->fSignalIdle)
- PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3);
+ u32Tasks &= ~RT_BIT_32(idx); /* Clear task bit. */
+ idx = ASMBitFirstSetU32(u32Tasks);
+ } /* while tasks available */
- RTMemFree(pAhciReq);
- memset(pAhciPort->aCachedTasks, 0, sizeof(pAhciPort->aCachedTasks));
+ /*
+ * Check whether a host controller reset is pending and execute the reset
+ * if this is the last active thread.
+ */
+ u32RegHbaCtrl = ASMAtomicReadU32(&pAhci->regHbaCtrl);
+ uint32_t cThreadsActive = ASMAtomicDecU32(&pAhci->cThreadsActive);
+ if ( (u32RegHbaCtrl & AHCI_HBA_CTRL_HR)
+ && !cThreadsActive)
+ ahciHBAReset(pAhci);
+ } /* While running */
ahciLog(("%s: Port %d async IO thread exiting\n", __FUNCTION__, pAhciPort->iLUN));
return VINF_SUCCESS;
@@ -6575,13 +6745,14 @@ static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
* Unblock the async I/O thread so it can respond to a state change.
*
* @returns VBox status code.
- * @param pDevIns The pcnet device instance.
+ * @param pDevIns The device instance.
* @param pThread The send thread.
*/
static DECLCALLBACK(int) ahciAsyncIOLoopWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
{
+ PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
PAHCIPort pAhciPort = (PAHCIPort)pThread->pvUser;
- return RTSemEventSignal(pAhciPort->AsyncIORequestSem);
+ return SUPSemEventSignal(pThis->pSupDrvSession, pAhciPort->hEvtProcess);
}
/* -=-=-=-=- DBGF -=-=-=-=- */
@@ -6653,7 +6824,6 @@ static DECLCALLBACK(void) ahciR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, con
pHlp->pfnPrintf(pHlp, "PortATAPI=%RTbool\n", pThisPort->fATAPI);
pHlp->pfnPrintf(pHlp, "PortTasksFinished=%#x\n", pThisPort->u32TasksFinished);
pHlp->pfnPrintf(pHlp, "PortQueuedTasksFinished=%#x\n", pThisPort->u32QueuedTasksFinished);
- pHlp->pfnPrintf(pHlp, "PortAsyncIoThreadIdle=%RTbool\n", pThisPort->fAsyncIOThreadIdle);
pHlp->pfnPrintf(pHlp, "\n");
}
}
@@ -6673,17 +6843,16 @@ static bool ahciR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
+ if (pThis->cThreadsActive)
+ return false;
+
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->ahciPort); i++)
{
PAHCIPort pThisPort = &pThis->ahciPort[i];
if (pThisPort->pDrvBase)
{
- bool fFinished;
- if (pThisPort->fAsyncInterface)
- fFinished = (pThisPort->cTasksActive == 0);
- else
- fFinished = ((pThisPort->cTasksActive == 0) && (pThisPort->fAsyncIOThreadIdle));
- if (!fFinished)
+ if ( (pThisPort->cTasksActive != 0)
+ || (pThisPort->u32TasksNew != 0))
return false;
}
}
@@ -7099,59 +7268,6 @@ static DECLCALLBACK(void) ahciR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta
}
/**
- * Destroy a driver instance.
- *
- * Most VM resources are freed by the VM. This callback is provided so that any non-VM
- * resources can be freed correctly.
- *
- * @param pDevIns The device instance data.
- */
-static DECLCALLBACK(int) ahciR3Destruct(PPDMDEVINS pDevIns)
-{
- PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
- int rc = VINF_SUCCESS;
- unsigned iActPort = 0;
- PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
-
- /*
- * At this point the async I/O thread is suspended and will not enter
- * this module again. So, no coordination is needed here and PDM
- * will take care of terminating and cleaning up the thread.
- */
- if (PDMCritSectIsInitialized(&pAhci->lock))
- {
- TMR3TimerDestroy(pAhci->CTX_SUFF(pHbaCccTimer));
-
- Log(("%s: Destruct every port\n", __FUNCTION__));
- for (iActPort = 0; iActPort < pAhci->cPortsImpl; iActPort++)
- {
- PAHCIPort pAhciPort = &pAhci->ahciPort[iActPort];
-
- if (pAhciPort->pAsyncIOThread)
- {
- /* Destroy the event semaphore. */
- rc = RTSemEventDestroy(pAhciPort->AsyncIORequestSem);
- if (RT_FAILURE(rc))
- {
- Log(("%s: Destroying event semaphore for port %d failed rc=%Rrc\n", __FUNCTION__, iActPort, rc));
- }
- }
-
- /* Free all cached tasks. */
- for (uint32_t i = 0; i < AHCI_NR_COMMAND_SLOTS; i++)
- {
- if (pAhciPort->aCachedTasks[i])
- RTMemFree(pAhciPort->aCachedTasks[i]);
- }
- }
-
- PDMR3CritSectDelete(&pAhci->lock);
- }
-
- return rc;
-}
-
-/**
* SCSI_GET_EVENT_STATUS_NOTIFICATION should return "medium removed" event
* from now on, regardless if there was a medium inserted or not.
*/
@@ -7160,7 +7276,6 @@ static void ahciMediumRemoved(PAHCIPort pAhciPort)
ASMAtomicWriteU32(&pAhciPort->MediaEventStatus, ATA_EVENT_STATUS_MEDIA_REMOVED);
}
-
/**
* SCSI_GET_EVENT_STATUS_NOTIFICATION should return "medium inserted". If
* there was already a medium inserted, don't forget to send the "medium
@@ -7191,7 +7306,7 @@ static void ahciMediumInserted(PAHCIPort pAhciPort)
*
* @param pInterface Pointer to the interface structure containing the called function pointer.
*/
-static DECLCALLBACK(void) ahciMountNotify(PPDMIMOUNTNOTIFY pInterface)
+static DECLCALLBACK(void) ahciR3MountNotify(PPDMIMOUNTNOTIFY pInterface)
{
PAHCIPort pAhciPort = PDMIMOUNTNOTIFY_2_PAHCIPORT(pInterface);
Log(("%s: changing LUN#%d\n", __FUNCTION__, pAhciPort->iLUN));
@@ -7210,7 +7325,7 @@ static DECLCALLBACK(void) ahciMountNotify(PPDMIMOUNTNOTIFY pInterface)
if (pAhciPort->cNotifiedMediaChange < 2)
pAhciPort->cNotifiedMediaChange = 2;
ahciMediumInserted(pAhciPort);
- ataMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN);
+ ahciMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN);
}
else
AssertMsgFailed(("Hard disks don't have a mount interface!\n"));
@@ -7220,7 +7335,7 @@ static DECLCALLBACK(void) ahciMountNotify(PPDMIMOUNTNOTIFY pInterface)
* Called when a media is unmounted
* @param pInterface Pointer to the interface structure containing the called function pointer.
*/
-static DECLCALLBACK(void) ahciUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
+static DECLCALLBACK(void) ahciR3UnmountNotify(PPDMIMOUNTNOTIFY pInterface)
{
PAHCIPort pAhciPort = PDMIMOUNTNOTIFY_2_PAHCIPORT(pInterface);
Log(("%s:\n", __FUNCTION__));
@@ -7237,7 +7352,7 @@ static DECLCALLBACK(void) ahciUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
*/
pAhciPort->cNotifiedMediaChange = 4;
ahciMediumRemoved(pAhciPort);
- ataMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN);
+ ahciMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN);
}
else
AssertMsgFailed(("Hard disks don't have a mount interface!\n"));
@@ -7310,7 +7425,8 @@ static int ahciR3ConfigureLUN(PPDMDEVINS pDevIns, PAHCIPort pAhciPort)
}
else
{
- pAhciPort->cTotalSectors = pAhciPort->pDrvBlock->pfnGetSize(pAhciPort->pDrvBlock) / 512;
+ pAhciPort->cbSector = pAhciPort->pDrvBlock->pfnGetSectorSize(pAhciPort->pDrvBlock);
+ pAhciPort->cTotalSectors = pAhciPort->pDrvBlock->pfnGetSize(pAhciPort->pDrvBlock) / pAhciPort->cbSector;
rc = pAhciPort->pDrvBlockBios->pfnGetPCHSGeometry(pAhciPort->pDrvBlockBios,
&pAhciPort->PCHSGeometry);
if (rc == VERR_PDM_MEDIA_NOT_MOUNTED)
@@ -7407,11 +7523,14 @@ static DECLCALLBACK(void) ahciR3Resume(PPDMDEVINS pDevIns)
{
PAHCIPort pAhciPort = &pAhci->ahciPort[i];
- if (pAhciPort->u32TasksNew)
+ if (pAhciPort->u32TasksRedo)
{
PDEVPORTNOTIFIERQUEUEITEM pItem = (PDEVPORTNOTIFIERQUEUEITEM)PDMQueueAlloc(pAhci->CTX_SUFF(pNotifierQueue));
AssertMsg(pItem, ("Allocating item for queue failed\n"));
+ pAhciPort->u32TasksNew |= pAhciPort->u32TasksRedo;
+ pAhciPort->u32TasksRedo = 0;
+
Assert(pAhciPort->fRedo);
pAhciPort->fRedo = false;
@@ -7494,6 +7613,14 @@ static int ahciR3VpdInit(PPDMDEVINS pDevIns, PAHCIPort pAhciPort, const char *ps
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read \"NonRotationalMedium\" as boolean"));
+ rc = CFGMR3QueryU8Def(pCfgNode, "LogicalSectorsPerPhysical", &pAhciPort->cLogSectorsPerPhysicalExp, 0);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("AHCI configuration error: failed to read \"LogicalSectorsPerPhysical\" as integer"));
+ if (pAhciPort->cLogSectorsPerPhysicalExp >= 16)
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("AHCI configuration error: \"LogicalSectorsPerPhysical\" must be between 0 and 15"));
+
/* There are three other identification strings for CD drives used for INQUIRY */
if (pAhciPort->fATAPI)
{
@@ -7555,7 +7682,7 @@ static DECLCALLBACK(void) ahciR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32
AssertMsg(iLUN < pAhci->cPortsImpl, ("iLUN=%u", iLUN));
- if (!pAhciPort->fAsyncInterface)
+ if (pAhciPort->pAsyncIOThread)
{
int rcThread;
/* Destroy the thread. */
@@ -7564,10 +7691,6 @@ static DECLCALLBACK(void) ahciR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32
AssertMsgFailed(("%s Failed to destroy async IO thread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
pAhciPort->pAsyncIOThread = NULL;
-
- rc = RTSemEventDestroy(pAhciPort->AsyncIORequestSem);
- if (RT_FAILURE(rc))
- AssertMsgFailed(("%s: Failed to destroy the event semaphore rc=%Rrc.\n", __FUNCTION__, rc));
}
if (pAhciPort->fATAPI)
@@ -7579,13 +7702,14 @@ static DECLCALLBACK(void) ahciR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32
* Inform the guest about the removed device.
*/
pAhciPort->regSSTS = 0;
+ pAhciPort->regSIG = 0;
/*
* Clear CR bit too to prevent submission of new commands when CI is written
* (AHCI Spec 1.2: 7.4 Interaction of the Command List and Port Change Status).
*/
ASMAtomicAndU32(&pAhciPort->regCMD, ~(AHCI_PORT_CMD_CPS | AHCI_PORT_CMD_CR));
- ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_CPDS | AHCI_PORT_IS_PRCS);
- ASMAtomicOrU32(&pAhciPort->regSERR, AHCI_PORT_SERR_N);
+ ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_CPDS | AHCI_PORT_IS_PRCS | AHCI_PORT_IS_PCS);
+ ASMAtomicOrU32(&pAhciPort->regSERR, AHCI_PORT_SERR_X | AHCI_PORT_SERR_N);
if ( (pAhciPort->regIE & AHCI_PORT_IE_CPDE)
|| (pAhciPort->regIE & AHCI_PORT_IE_PCE)
|| (pAhciPort->regIE & AHCI_PORT_IE_PRCE))
@@ -7614,14 +7738,14 @@ static DECLCALLBACK(void) ahciR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32
*/
static DECLCALLBACK(int) ahciR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
- PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
- PAHCIPort pAhciPort = &pAhci->ahciPort[iLUN];
+ PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
+ PAHCIPort pAhciPort = &pThis->ahciPort[iLUN];
int rc;
Log(("%s:\n", __FUNCTION__));
/* the usual paranoia */
- AssertMsg(iLUN < pAhci->cPortsImpl, ("iLUN=%u", iLUN));
+ AssertMsg(iLUN < pThis->cPortsImpl, ("iLUN=%u", iLUN));
AssertRelease(!pAhciPort->pDrvBase);
AssertRelease(!pAhciPort->pDrvBlock);
AssertRelease(!pAhciPort->pDrvBlockAsync);
@@ -7633,7 +7757,15 @@ static DECLCALLBACK(int) ahciR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32
*/
rc = PDMDevHlpDriverAttach(pDevIns, pAhciPort->iLUN, &pAhciPort->IBase, &pAhciPort->pDrvBase, NULL);
if (RT_SUCCESS(rc))
+ {
rc = ahciR3ConfigureLUN(pDevIns, pAhciPort);
+
+ /*
+ * In case there is a medium inserted.
+ */
+ ahciMediumInserted(pAhciPort);
+ ahciMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN);
+ }
else
AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pAhciPort->iLUN, rc));
@@ -7651,26 +7783,18 @@ static DECLCALLBACK(int) ahciR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32
&& !pAhciPort->fATAPI)
pAhciPort->fAsyncInterface = true;
else
- {
pAhciPort->fAsyncInterface = false;
- /* Create event semaphore. */
- rc = RTSemEventCreate(&pAhciPort->AsyncIORequestSem);
- if (RT_FAILURE(rc))
- {
- Log(("%s: Failed to create event semaphore for %s.\n", __FUNCTION__, szName));
- return rc;
- }
+ rc = SUPSemEventCreate(pThis->pSupDrvSession, &pAhciPort->hEvtProcess);
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("AHCI: Failed to create SUP event semaphore"));
- /* Create the async IO thread. */
- rc = PDMDevHlpThreadCreate(pDevIns, &pAhciPort->pAsyncIOThread, pAhciPort, ahciAsyncIOLoop, ahciAsyncIOLoopWakeUp, 0,
- RTTHREADTYPE_IO, szName);
- if (RT_FAILURE(rc))
- {
- AssertMsgFailed(("%s: Async IO Thread creation for %s failed rc=%d\n", __FUNCTION__, szName, rc));
- return rc;
- }
- }
+ /* Create the async IO thread. */
+ rc = PDMDevHlpThreadCreate(pDevIns, &pAhciPort->pAsyncIOThread, pAhciPort, ahciAsyncIOLoop, ahciAsyncIOLoopWakeUp, 0,
+ RTTHREADTYPE_IO, szName);
+ if (RT_FAILURE(rc))
+ return rc;
/*
* Init vendor product data.
@@ -7774,6 +7898,56 @@ static DECLCALLBACK(void) ahciR3PowerOff(PPDMDEVINS pDevIns)
}
/**
+ * Destroy a driver instance.
+ *
+ * Most VM resources are freed by the VM. This callback is provided so that any non-VM
+ * resources can be freed correctly.
+ *
+ * @param pDevIns The device instance data.
+ */
+static DECLCALLBACK(int) ahciR3Destruct(PPDMDEVINS pDevIns)
+{
+ PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
+ int rc = VINF_SUCCESS;
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+
+ /*
+ * At this point the async I/O thread is suspended and will not enter
+ * this module again. So, no coordination is needed here and PDM
+ * will take care of terminating and cleaning up the thread.
+ */
+ if (PDMCritSectIsInitialized(&pThis->lock))
+ {
+ TMR3TimerDestroy(pThis->CTX_SUFF(pHbaCccTimer));
+ pThis->CTX_SUFF(pHbaCccTimer) = NULL;
+
+ Log(("%s: Destruct every port\n", __FUNCTION__));
+ for (unsigned iActPort = 0; iActPort < pThis->cPortsImpl; iActPort++)
+ {
+ PAHCIPort pAhciPort = &pThis->ahciPort[iActPort];
+
+ if (pAhciPort->hEvtProcess != NIL_SUPSEMEVENT)
+ {
+ SUPSemEventClose(pThis->pSupDrvSession, pAhciPort->hEvtProcess);
+ pAhciPort->hEvtProcess = NIL_SUPSEMEVENT;
+ }
+
+ /* Free all cached tasks. */
+ for (uint32_t i = 0; i < AHCI_NR_COMMAND_SLOTS; i++)
+ if (pAhciPort->aCachedTasks[i])
+ {
+ RTMemFree(pAhciPort->aCachedTasks[i]);
+ pAhciPort->aCachedTasks[i] = NULL;
+ }
+ }
+
+ PDMR3CritSectDelete(&pThis->lock);
+ }
+
+ return rc;
+}
+
+/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
@@ -7855,11 +8029,16 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
N_("AHCI configuration error: CmdSlotsAvail=%u should be at least 1"),
pThis->cCmdSlotsAvail);
+ /*
+ * Initialize the instance data (everything touched by the destructor need
+ * to be initialized here!).
+ */
pThis->fR0Enabled = fR0Enabled;
pThis->fGCEnabled = fGCEnabled;
pThis->pDevInsR3 = pDevIns;
pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns);
PCIDevSetVendorId (&pThis->dev, 0x8086); /* Intel */
PCIDevSetDeviceId (&pThis->dev, 0x2829); /* ICH-8M */
@@ -7894,6 +8073,39 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
PCIDevSetWord(&pThis->dev, 0xaa, 0x0010); /* Revision */
PCIDevSetDWord(&pThis->dev, 0xac, 0x00000028); /* SATA Capability Register 1 */
+ pThis->cThreadsActive = 0;
+
+ /* Initialize port members. */
+ for (i = 0; i < AHCI_MAX_NR_PORTS_IMPL; i++)
+ {
+ PAHCIPort pAhciPort = &pThis->ahciPort[i];
+ pAhciPort->pDevInsR3 = pDevIns;
+ pAhciPort->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pAhciPort->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pAhciPort->iLUN = i;
+ pAhciPort->pAhciR3 = pThis;
+ pAhciPort->pAhciR0 = PDMINS_2_DATA_R0PTR(pDevIns);
+ pAhciPort->pAhciRC = PDMINS_2_DATA_RCPTR(pDevIns);
+ pAhciPort->Led.u32Magic = PDMLED_MAGIC;
+ pAhciPort->pDrvBase = NULL;
+ pAhciPort->pAsyncIOThread = NULL;
+ pAhciPort->hEvtProcess = NIL_SUPSEMEVENT;
+ }
+
+ /*
+ * Init locks, using explicit locking where necessary.
+ */
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->lock, RT_SRC_POS, "AHCI#%u", iInstance);
+ if (RT_FAILURE(rc))
+ {
+ Log(("%s: Failed to create critical section.\n", __FUNCTION__));
+ return rc;
+ }
+
/*
* Register the PCI device, it's I/O regions.
*/
@@ -7902,14 +8114,13 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
return rc;
#ifdef VBOX_WITH_MSI_DEVICES
- PDMMSIREG aMsiReg;
-
- RT_ZERO(aMsiReg);
- aMsiReg.cMsiVectors = 1;
- aMsiReg.iMsiCapOffset = 0x80;
- aMsiReg.iMsiNextOffset = 0x70;
- rc = PDMDevHlpPCIRegisterMsi(pDevIns, &aMsiReg);
- if (RT_FAILURE (rc))
+ PDMMSIREG MsiReg;
+ RT_ZERO(MsiReg);
+ MsiReg.cMsiVectors = 1;
+ MsiReg.iMsiCapOffset = 0x80;
+ MsiReg.iMsiNextOffset = 0x70;
+ rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
+ if (RT_FAILURE(rc))
{
LogRel(("Chipset cannot do MSI: %Rrc\n", rc));
PCIDevSetCapabilityList(&pThis->dev, 0x70);
@@ -7956,13 +8167,6 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI cannot register PCI memory region for registers"));
- rc = PDMDevHlpCritSectInit(pDevIns, &pThis->lock, RT_SRC_POS, "AHCI#%u", iInstance);
- if (RT_FAILURE(rc))
- {
- Log(("%s: Failed to create critical section.\n", __FUNCTION__));
- return rc;
- }
-
/* Create the timer for command completion coalescing feature. */
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ahciCccTimer, pThis,
TMTIMER_FLAGS_NO_CRIT_SECT, "AHCI CCC Timer", &pThis->pHbaCccTimerR3);
@@ -7983,7 +8187,7 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
*
* We need 2 items for every port because of SMP races.
*/
- rc = PDMDevHlpQueueCreate(pDevIns, sizeof(DEVPORTNOTIFIERQUEUEITEM), AHCI_MAX_NR_PORTS_IMPL*2, 0,
+ rc = PDMDevHlpQueueCreate(pDevIns, sizeof(DEVPORTNOTIFIERQUEUEITEM), AHCI_MAX_NR_PORTS_IMPL * 2, 0,
ahciNotifyQueueConsumer, true, "AHCI-Xmit", &pThis->pNotifierQueueR3);
if (RT_FAILURE(rc))
return rc;
@@ -7993,21 +8197,8 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
/* Initialize static members on every port. */
for (i = 0; i < AHCI_MAX_NR_PORTS_IMPL; i++)
{
- /*
- * Init members of the port.
- */
- PAHCIPort pAhciPort = &pThis->ahciPort[i];
- pAhciPort->pDevInsR3 = pDevIns;
- pAhciPort->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
- pAhciPort->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
- pAhciPort->iLUN = i;
- pAhciPort->pAhciR3 = pThis;
- pAhciPort->pAhciR0 = PDMINS_2_DATA_R0PTR(pDevIns);
- pAhciPort->pAhciRC = PDMINS_2_DATA_RCPTR(pDevIns);
- pAhciPort->Led.u32Magic = PDMLED_MAGIC;
- pAhciPort->pDrvBase = NULL;
-
- /* Register statistics counter. */
+ PAHCIPort pAhciPort = &pThis->ahciPort[i];
+
PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatDMA, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
"Number of DMA transfers.", "/Devices/SATA%d/Port%d/DMA", iInstance, i);
PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
@@ -8019,12 +8210,8 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
#ifdef VBOX_WITH_STATISTICS
PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatProfileProcessTime, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
"Amount of time to process one request.", "/Devices/SATA%d/Port%d/ProfileProcessTime", iInstance, i);
- PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatProfileMapIntoR3, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
- "Amount of time to map the guest buffers into R3.", "/Devices/SATA%d/Port%d/ProfileMapIntoR3", iInstance, i);
PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatProfileReadWrite, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
"Amount of time for the read/write operation to complete.", "/Devices/SATA%d/Port%d/ProfileReadWrite", iInstance, i);
- PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatProfileDestroyScatterGatherList, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
- "Amount of time to destroy the scatter gather list and free associated resources.", "/Devices/SATA%d/Port%d/ProfileDestroyScatterGatherList", iInstance, i);
#endif
ahciPortHwReset(pAhciPort);
@@ -8040,12 +8227,11 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
/*
* Init interfaces.
*/
- pAhciPort->IBase.pfnQueryInterface = ahciR3PortQueryInterface;
- pAhciPort->IPortAsync.pfnTransferCompleteNotify = ahciTransferCompleteNotify;
- pAhciPort->IPort.pfnQueryDeviceLocation = ahciR3PortQueryDeviceLocation;
- pAhciPort->IMountNotify.pfnMountNotify = ahciMountNotify;
- pAhciPort->IMountNotify.pfnUnmountNotify = ahciUnmountNotify;
- pAhciPort->fAsyncIOThreadIdle = true;
+ pAhciPort->IBase.pfnQueryInterface = ahciR3PortQueryInterface;
+ pAhciPort->IPortAsync.pfnTransferCompleteNotify = ahciR3TransferCompleteNotify;
+ pAhciPort->IPort.pfnQueryDeviceLocation = ahciR3PortQueryDeviceLocation;
+ pAhciPort->IMountNotify.pfnMountNotify = ahciR3MountNotify;
+ pAhciPort->IMountNotify.pfnUnmountNotify = ahciR3UnmountNotify;
/*
* Attach the block driver
@@ -8085,15 +8271,18 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
{
LogRel(("AHCI: LUN#%d: using normal I/O\n", pAhciPort->iLUN));
pAhciPort->fAsyncInterface = false;
+ }
- rc = RTSemEventCreate(&pAhciPort->AsyncIORequestSem);
- AssertMsgRC(rc, ("Failed to create event semaphore for %s rc=%Rrc.\n", szName, rc));
-
+ rc = SUPSemEventCreate(pThis->pSupDrvSession, &pAhciPort->hEvtProcess);
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("AHCI: Failed to create SUP event semaphore"));
- rc = PDMDevHlpThreadCreate(pDevIns, &pAhciPort->pAsyncIOThread, pAhciPort, ahciAsyncIOLoop, ahciAsyncIOLoopWakeUp, 0,
- RTTHREADTYPE_IO, szName);
- AssertMsgRC(rc, ("%s: Async IO Thread creation for %s failed rc=%Rrc\n", szName, rc));
- }
+ rc = PDMDevHlpThreadCreate(pDevIns, &pAhciPort->pAsyncIOThread, pAhciPort, ahciAsyncIOLoop,
+ ahciAsyncIOLoopWakeUp, 0, RTTHREADTYPE_IO, szName);
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("AHCI: Failed to create worker thread %s"), szName);
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
@@ -8120,7 +8309,7 @@ static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
return PDMDEV_SET_ERROR(pDevIns, rc, N_("AHCI cannot attach to status driver"));
}
- rc = PDMDevHlpSSMRegisterEx(pDevIns, AHCI_SAVED_STATE_VERSION, sizeof(*pThis)+cbTotalBufferSize, NULL,
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, AHCI_SAVED_STATE_VERSION, sizeof(*pThis) + cbTotalBufferSize, NULL,
NULL, ahciR3LiveExec, NULL,
ahciR3SavePrep, ahciR3SaveExec, NULL,
ahciR3LoadPrep, ahciR3LoadExec, NULL);
@@ -8168,7 +8357,7 @@ const PDMDEVREG g_DeviceAHCI =
ahciR3Destruct,
/* pfnRelocate */
ahciR3Relocate,
- /* pfnIOCtl */
+ /* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
diff --git a/src/VBox/Devices/Storage/DevATA.cpp b/src/VBox/Devices/Storage/DevATA.cpp
index bf369dd6..b87bdc27 100644
--- a/src/VBox/Devices/Storage/DevATA.cpp
+++ b/src/VBox/Devices/Storage/DevATA.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2012 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -59,6 +59,7 @@
#include "PIIX3ATABmDma.h"
#include "ide.h"
+#include "ATAPIPassthrough.h"
#include "VBoxDD.h"
/*******************************************************************************
@@ -100,8 +101,6 @@
/* Media track type */
#define ATA_MEDIA_TYPE_UNKNOWN 0 /**< unknown CD type */
-#define ATA_MEDIA_TYPE_DATA 1 /**< Data CD */
-#define ATA_MEDIA_TYPE_CDDA 2 /**< CD-DA (audio) CD type */
#define ATA_MEDIA_NO_DISC 0x70 /**< Door closed, no medium */
/*******************************************************************************
@@ -128,6 +127,8 @@ typedef struct ATADevState
PDMMEDIAGEOMETRY PCHSGeometry;
/** Total number of sectors on this disk. */
uint64_t cTotalSectors;
+ /** Sector size of the medium. */
+ uint32_t cbSector;
/** Number of sectors to transfer per IRQ. */
uint32_t cSectorsPerIRQ;
@@ -179,6 +180,8 @@ typedef struct ATADevState
uint32_t cbTotalTransfer;
/** Elementary ATA/ATAPI transfer size, shared PIO/DMA. */
uint32_t cbElementaryTransfer;
+ /** Maximum ATAPI elementary transfer size, PIO only. */
+ uint32_t cbPIOTransferLimit;
/** Current read/write buffer position, shared PIO/DMA. */
uint32_t iIOBufferCur;
/** First element beyond end of valid buffer content, shared PIO/DMA. */
@@ -304,12 +307,8 @@ typedef struct ATADevState
char szInquiryProductId[ATAPI_INQUIRY_PRODUCT_ID_LENGTH+1];
/** The revision string for SCSI INQUIRY commands. */
char szInquiryRevision[ATAPI_INQUIRY_REVISION_LENGTH+1];
- /** Size of the current CUE sheet in bytes. */
- uint32_t cbCueSheet;
- /** Align pbCueSheet correctly. */
- uint32_t u32Alignment3;
- /** The current CUE Sheet if passthrough is used. */
- R3PTRTYPE(uint8_t *) pbCueSheet;
+ /** The current tracklist of the loaded medium if passthrough is used. */
+ R3PTRTYPE(PTRACKLIST) pTrackList;
uint8_t abAlignment4[HC_ARCH_BITS == 64 ? 7 : 3];
} ATADevState;
@@ -987,6 +986,7 @@ static void ataPIOTransferStart(ATADevState *s, uint32_t start, uint32_t size)
s->iIOBufferPIODataStart = start;
s->iIOBufferPIODataEnd = start + size;
ataSetStatus(s, ATA_STAT_DRQ | ATA_STAT_SEEK);
+ ataUnsetStatus(s, ATA_STAT_BUSY);
}
@@ -1013,7 +1013,7 @@ static void ataPIOTransferLimitATAPI(ATADevState *s)
{
uint32_t cbLimit, cbTransfer;
- cbLimit = s->uATARegLCyl | (s->uATARegHCyl << 8);
+ cbLimit = s->cbPIOTransferLimit;
/* Use maximum transfer size if the guest requested 0. Avoids a hang. */
if (cbLimit == 0)
cbLimit = 0xfffe;
@@ -1162,117 +1162,6 @@ DECLINLINE(int) atapiCmpMSF(const uint8_t *pbMSF1, const uint8_t *pbMSF2)
return iRes;
}
-/**
- * Return the correct sector size from the given LBA.
- *
- * @returns Sector size.
- * @param s ATA Device state.
- * @param iATAPILBA The LBA.
- */
-static size_t atapiGetSectorSizeFromLba(ATADevState *s, uint32_t iATAPILBA)
-{
- size_t cbATAPISector = 2048;
-
- /*
- * Check if there is a valid CUE Sheet we can use,
- * otherwise we just return the standard sector size.
- */
- if (s->pbCueSheet)
- {
- uint8_t *pbCueSheetEntry = NULL;
- uint8_t iMSF[3];
-
- /*
- * Convert the LBA to the MSF format so we can look it up in the cue sheet.
- * Note that it is possible to have negative LBA values for the Lead-In area.
- * See MMC6 spec chapter 6.46.3.3 for more details.
- */
- LogFlowFunc(("iATAPILBA=%#x (signed: %d unsigned: %u)\n",
- iATAPILBA, (int32_t)iATAPILBA, iATAPILBA));
-
- if ( iATAPILBA > UINT32_C(0xffff4fa1)
- && (int32_t)iATAPILBA < -150)
- {
- /* Lead-In area, this is always the first entry in the cue sheet. */
- pbCueSheetEntry = s->pbCueSheet;
- LogFlowFunc(("Selected Lead-In area\n"));
- }
- else
- {
- iATAPILBA += 150;
- iMSF[0] = (iATAPILBA / 75) / 60;
- iMSF[1] = (iATAPILBA / 75) % 60;
- iMSF[2] = iATAPILBA % 75;
-
- pbCueSheetEntry = s->pbCueSheet + 8; /* Start after the Lead-in track. */
-
- /* Go through the CUE sheet and find the correct entry. */
- for (unsigned i = 0; i < (s->cbCueSheet / 8) - 2; i++)
- {
- if ( atapiCmpMSF(&pbCueSheetEntry[5], iMSF) != 1
- && atapiCmpMSF(&pbCueSheetEntry[8+5], iMSF) == 1)
- break;
- pbCueSheetEntry += 8;
- }
- LogFlowFunc(("Selected CUE sheet entry iMin=%u iSec=%u iFrame=%u\n",
- pbCueSheetEntry[5], pbCueSheetEntry[6], pbCueSheetEntry[7]));
- }
-
- if (pbCueSheetEntry)
- {
- /* Determine size of main data based on the data form field. */
- switch (pbCueSheetEntry[3] & 0x3f)
- {
- case 0x00: /* CD-DA with data. */
- case 0x11: /* CD-ROM mode 1 */
- case 0x13:
- case 0x21: /* CD-ROM XA, CD-I */
- case 0x23:
- case 0x31: /* CD-ROM Mode 2 */
- case 0x33:
- cbATAPISector = 2352;
- break;
- case 0x01: /* CD-DA without data (used for pauses between tracks). */
- case 0x14:
- case 0x24:
- case 0x34:
- cbATAPISector = 0;
- break;
- case 0x10: /* CD-ROM mode 1 */
- case 0x12:
- cbATAPISector = 2048;
- break;
- case 0x20: /* CD-ROM XA, CD-I */
- case 0x22:
- case 0x30: /* CD-ROM Mode 2 */
- case 0x32:
- cbATAPISector = 2336;
- break;
- default: /* Reserved, invalid mode. Log and leave default sector size. */
- LogRel(("ATA: Invalid data form mode %u for current CUE sheet\n",
- pbCueSheetEntry[3] & 0x3f));
- }
-
- /* Determine size of sub channel data based on data form field. */
- switch ((pbCueSheetEntry[3] & 0xc0) >> 6)
- {
- case 0x00: /* Sub channel all zeroes, autogenerated by the drive. */
- break;
- case 0x01:
- case 0x03:
- cbATAPISector += 96;
- break;
- default:
- LogRel(("ATA: Invalid sub-channel data form mode %u for current CUE sheet\n",
- pbCueSheetEntry[3] & 0xc0));
- }
- }
- }
-
- LogFlowFunc(("cbATAPISector=%zu\n", cbATAPISector));
- return cbATAPISector;
-}
-
static void ataCmdOK(ATADevState *s, uint8_t status)
{
s->uATARegError = 0; /* Not needed by ATA spec, but cannot hurt. */
@@ -1321,7 +1210,7 @@ static bool ataIdentifySS(ATADevState *s)
p[1] = RT_H2LE_U16(RT_MIN(s->PCHSGeometry.cCylinders, 16383));
p[3] = RT_H2LE_U16(s->PCHSGeometry.cHeads);
/* Block size; obsolete, but required for the BIOS. */
- p[5] = RT_H2LE_U16(512);
+ p[5] = RT_H2LE_U16(s->cbSector);
p[6] = RT_H2LE_U16(s->PCHSGeometry.cSectors);
ataPadString((uint8_t *)(p + 10), s->szSerialNumber, ATA_SERIAL_NUMBER_LENGTH); /* serial number */
p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */
@@ -1366,7 +1255,9 @@ static bool ataIdentifySS(ATADevState *s)
p[66] = RT_H2LE_U16(120); /* recommended DMA multiword tx cycle time */
p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */
p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
- if (s->pDrvBlock->pfnDiscard)
+ if ( s->pDrvBlock->pfnDiscard
+ || s->cbSector != 512
+ || s->fNonRotational)
{
p[80] = RT_H2LE_U16(0x1f0); /* support everything up to ATA/ATAPI-8 ACS */
p[81] = RT_H2LE_U16(0x28); /* conforms to ATA/ATAPI-8 ACS */
@@ -1397,6 +1288,16 @@ static bool ataIdentifySS(ATADevState *s)
p[102] = RT_H2LE_U16(s->cTotalSectors >> 32);
p[103] = RT_H2LE_U16(s->cTotalSectors >> 48);
}
+
+ if (s->cbSector != 512)
+ {
+ uint32_t cSectorSizeInWords = s->cbSector / sizeof(uint16_t);
+ /* Enable reporting of logical sector size. */
+ p[106] |= RT_H2LE_U16(RT_BIT(12) | RT_BIT(14));
+ p[117] = RT_H2LE_U16(cSectorSizeInWords);
+ p[118] = RT_H2LE_U16(cSectorSizeInWords >> 16);
+ }
+
if (s->pDrvBlock->pfnDiscard) /** @todo: Set bit 14 in word 69 too? (Deterministic read after TRIM). */
p[169] = RT_H2LE_U16(1); /* DATA SET MANAGEMENT command supported. */
if (s->fNonRotational)
@@ -1440,8 +1341,8 @@ static bool atapiIdentifySS(ATADevState *s)
p = (uint16_t *)s->CTX_SUFF(pbIOBuffer);
memset(p, 0, 512);
- /* Removable CDROM, 50us response, 12 byte packets */
- p[0] = RT_H2LE_U16(2 << 14 | 5 << 8 | 1 << 7 | 2 << 5 | 0 << 0);
+ /* Removable CDROM, 3ms response, 12 byte packets */
+ p[0] = RT_H2LE_U16(2 << 14 | 5 << 8 | 1 << 7 | 0 << 5 | 0 << 0);
ataPadString((uint8_t *)(p + 10), s->szSerialNumber, ATA_SERIAL_NUMBER_LENGTH); /* serial number */
p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */
p[21] = RT_H2LE_U16(512); /* XXX: retired, cache size in sectors */
@@ -1653,11 +1554,13 @@ static int ataReadSectors(ATADevState *s, uint64_t u64Sector, void *pvBuf,
STAM_PROFILE_ADV_START(&s->StatReads, r);
s->Led.Asserted.s.fReading = s->Led.Actual.s.fReading = 1;
- rc = s->pDrvBlock->pfnRead(s->pDrvBlock, u64Sector * 512, pvBuf, cSectors * 512);
+ rc = s->pDrvBlock->pfnRead(s->pDrvBlock, u64Sector * s->cbSector, pvBuf, cSectors * s->cbSector);
s->Led.Actual.s.fReading = 0;
STAM_PROFILE_ADV_STOP(&s->StatReads, r);
+ Log4(("ataReadSectors: rc=%Rrc cSectors=%#x u64Sector=%llu\n%.*Rhxd\n",
+ rc, cSectors, u64Sector, cSectors * s->cbSector, pvBuf));
- STAM_REL_COUNTER_ADD(&s->StatBytesRead, cSectors * 512);
+ STAM_REL_COUNTER_ADD(&s->StatBytesRead, cSectors * s->cbSector);
if (RT_SUCCESS(rc))
*pfRedo = false;
@@ -1685,15 +1588,17 @@ static int ataWriteSectors(ATADevState *s, uint64_t u64Sector,
if (s->fDMA)
STAM_PROFILE_ADV_START(&s->StatInstrVDWrites, vw);
#endif
- rc = s->pDrvBlock->pfnWrite(s->pDrvBlock, u64Sector * 512, pvBuf, cSectors * 512);
+ rc = s->pDrvBlock->pfnWrite(s->pDrvBlock, u64Sector * s->cbSector, pvBuf, cSectors * s->cbSector);
#ifdef VBOX_INSTRUMENT_DMA_WRITES
if (s->fDMA)
STAM_PROFILE_ADV_STOP(&s->StatInstrVDWrites, vw);
#endif
s->Led.Actual.s.fWriting = 0;
STAM_PROFILE_ADV_STOP(&s->StatWrites, w);
+ Log4(("ataWriteSectors: rc=%Rrc cSectors=%#x u64Sector=%llu\n%.*Rhxd\n",
+ rc, cSectors, u64Sector, cSectors * s->cbSector, pvBuf));
- STAM_REL_COUNTER_ADD(&s->StatBytesWritten, cSectors * 512);
+ STAM_REL_COUNTER_ADD(&s->StatBytesWritten, cSectors * s->cbSector);
if (RT_SUCCESS(rc))
*pfRedo = false;
@@ -1711,11 +1616,11 @@ static void ataReadWriteSectorsBT(ATADevState *s)
{
uint32_t cSectors;
- cSectors = s->cbTotalTransfer / 512;
+ cSectors = s->cbTotalTransfer / s->cbSector;
if (cSectors > s->cSectorsPerIRQ)
- s->cbElementaryTransfer = s->cSectorsPerIRQ * 512;
+ s->cbElementaryTransfer = s->cSectorsPerIRQ * s->cbSector;
else
- s->cbElementaryTransfer = cSectors * 512;
+ s->cbElementaryTransfer = cSectors * s->cbSector;
if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
ataCmdOK(s, 0);
}
@@ -1728,7 +1633,7 @@ static bool ataReadSectorsSS(ATADevState *s)
uint64_t iLBA;
bool fRedo;
- cSectors = s->cbElementaryTransfer / 512;
+ cSectors = s->cbElementaryTransfer / s->cbSector;
Assert(cSectors);
iLBA = ataGetSector(s);
Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iLBA));
@@ -1766,7 +1671,7 @@ static bool ataWriteSectorsSS(ATADevState *s)
uint64_t iLBA;
bool fRedo;
- cSectors = s->cbElementaryTransfer / 512;
+ cSectors = s->cbElementaryTransfer / s->cbSector;
Assert(cSectors);
iLBA = ataGetSector(s);
Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iLBA));
@@ -1850,6 +1755,7 @@ static void atapiCmdBT(ATADevState *s)
{
s->fATAPITransfer = true;
s->cbElementaryTransfer = s->cbTotalTransfer;
+ s->cbPIOTransferLimit = s->uATARegLCyl | (s->uATARegHCyl << 8);
if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
atapiCmdOK(s);
}
@@ -1955,36 +1861,36 @@ static bool atapiReadSS(ATADevState *s)
rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)s->iATAPILBA * s->cbATAPISector, s->CTX_SUFF(pbIOBuffer), s->cbATAPISector * cSectors);
break;
case 2352:
- {
- uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
+ {
+ uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
- for (uint32_t i = s->iATAPILBA; i < s->iATAPILBA + cSectors; i++)
- {
- /* Sync bytes, see 4.2.3.8 CD Main Channel Block Formats */
- *pbBuf++ = 0x00;
- memset(pbBuf, 0xff, 10);
- pbBuf += 10;
- *pbBuf++ = 0x00;
- /* MSF */
- ataLBA2MSF(pbBuf, i);
- pbBuf += 3;
- *pbBuf++ = 0x01; /* mode 1 data */
- /* data */
- rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)i * 2048, pbBuf, 2048);
- if (RT_FAILURE(rc))
- break;
- pbBuf += 2048;
- /**
- * @todo: maybe compute ECC and parity, layout is:
- * 2072 4 EDC
- * 2076 172 P parity symbols
- * 2248 104 Q parity symbols
- */
- memset(pbBuf, 0, 280);
- pbBuf += 280;
- }
+ for (uint32_t i = s->iATAPILBA; i < s->iATAPILBA + cSectors; i++)
+ {
+ /* Sync bytes, see 4.2.3.8 CD Main Channel Block Formats */
+ *pbBuf++ = 0x00;
+ memset(pbBuf, 0xff, 10);
+ pbBuf += 10;
+ *pbBuf++ = 0x00;
+ /* MSF */
+ ataLBA2MSF(pbBuf, i);
+ pbBuf += 3;
+ *pbBuf++ = 0x01; /* mode 1 data */
+ /* data */
+ rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)i * 2048, pbBuf, 2048);
+ if (RT_FAILURE(rc))
+ break;
+ pbBuf += 2048;
+ /**
+ * @todo: maybe compute ECC and parity, layout is:
+ * 2072 4 EDC
+ * 2076 172 P parity symbols
+ * 2248 104 Q parity symbols
+ */
+ memset(pbBuf, 0, 280);
+ pbBuf += 280;
}
break;
+ }
default:
break;
}
@@ -2039,7 +1945,7 @@ static bool atapiPassthroughSS(ATADevState *s)
uint32_t cbTransfer;
PSTAMPROFILEADV pProf = NULL;
- cbTransfer = RT_MIN(s->cbElementaryTransfer, s->cbIOBuffer);
+ cbTransfer = RT_MIN(s->cbTotalTransfer, s->cbIOBuffer);
if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
Log3(("ATAPI PT data write (%d): %.*Rhxs\n", cbTransfer, cbTransfer, s->CTX_SUFF(pbIOBuffer)));
@@ -2241,33 +2147,23 @@ static bool atapiPassthroughSS(ATADevState *s)
switch (s->aATAPICmd[0])
{
case SCSI_SEND_CUE_SHEET:
+ case SCSI_READ_TOC_PMA_ATIP:
{
- /* Save the CUE sheet to determine sector sizes during writes */
- if (s->pbCueSheet)
- {
- s->cbCueSheet = 0;
- RTMemFree(s->pbCueSheet);
- }
+ if (!s->pTrackList)
+ rc = ATAPIPassthroughTrackListCreateEmpty(&s->pTrackList);
- s->pbCueSheet = (uint8_t *)RTMemAllocZ(s->cbElementaryTransfer);
- if (s->pbCueSheet)
- {
- s->cbCueSheet = s->cbElementaryTransfer;
- memcpy(s->pbCueSheet, s->CTX_SUFF(pbIOBuffer), s->cbElementaryTransfer);
- }
- else if (s->cErrors++ < MAX_LOG_REL_ERRORS)
- LogRel(("ATA: Out of memory while saving the CUE sheet, burning disc might fail\n"));
+ if (RT_SUCCESS(rc))
+ rc = ATAPIPassthroughTrackListUpdate(s->pTrackList, s->aATAPICmd, s->CTX_SUFF(pbIOBuffer));
+
+ if ( RT_FAILURE(rc)
+ && s->cErrors++ < MAX_LOG_REL_ERRORS)
+ LogRel(("ATA: Error (%Rrc) while updating the tracklist during %s, burning the disc might fail\n",
+ rc, s->aATAPICmd[0] == SCSI_SEND_CUE_SHEET ? "SEND CUE SHEET" : "READ TOC/PMA/ATIP"));
break;
}
case SCSI_SYNCHRONIZE_CACHE:
{
- /* Free the current CUE sheet after session at once recording. */
- if (s->pbCueSheet)
- {
- s->cbCueSheet = 0;
- RTMemFree(s->pbCueSheet);
- s->pbCueSheet = NULL;
- }
+ ATAPIPassthroughTrackListClear(s->pTrackList);
break;
}
}
@@ -2277,10 +2173,12 @@ static bool atapiPassthroughSS(ATADevState *s)
Assert(cbTransfer <= s->cbTotalTransfer);
/*
* Reply with the same amount of data as the real drive
- * but only if the command wasn't splitted.
+ * but only if the command wasn't split.
*/
+#if 0 //@todo: This destroys commands where cbTotalTransfer > cbIOBuffer
if (s->cbElementaryTransfer < s->cbIOBuffer)
s->cbTotalTransfer = cbTransfer;
+#endif
if ( s->aATAPICmd[0] == SCSI_INQUIRY
&& s->fOverwriteInquiry)
@@ -2293,37 +2191,7 @@ static bool atapiPassthroughSS(ATADevState *s)
ataSCSIPadStr(s->CTX_SUFF(pbIOBuffer) + 16, "CD-ROM", 16);
ataSCSIPadStr(s->CTX_SUFF(pbIOBuffer) + 32, "1.0", 4);
}
- else if ( s->aATAPICmd[0] == SCSI_READ_TOC_PMA_ATIP
- && (s->aATAPICmd[2] & 0xf) != 0x05
- && s->aATAPICmd[6] != 0xaa)
- {
- /* Set the media type if we can detect it. */
- uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
-
- /** @todo: Implemented only for formatted TOC now. */
- if ( (s->aATAPICmd[2] & 0xf) == 0
- && cbTransfer >= 6)
- {
- uint32_t NewMediaType;
- uint32_t OldMediaType;
- if (pbBuf[5] & 0x4)
- NewMediaType = ATA_MEDIA_TYPE_DATA;
- else
- NewMediaType = ATA_MEDIA_TYPE_CDDA;
-
- OldMediaType = ataMediumTypeSet(s, NewMediaType);
-
- if (OldMediaType != NewMediaType)
- LogRel(("PIIX3 ATA: LUN#%d: CD-ROM passthrough, detected %s CD\n",
- s->iLUN,
- NewMediaType == ATA_MEDIA_TYPE_DATA
- ? "data"
- : "audio"));
- }
- else /* Play safe and set to unknown. */
- ataMediumTypeSet(s, ATA_MEDIA_TYPE_UNKNOWN);
- }
if (cbTransfer)
Log3(("ATAPI PT data read (%d): %.*Rhxs\n", cbTransfer, cbTransfer, s->CTX_SUFF(pbIOBuffer)));
}
@@ -2402,41 +2270,41 @@ static bool atapiReadDVDStructureSS(ATADevState *s)
switch (format)
{
case 0x0: /* Physical format information */
+ {
+ int layer = s->aATAPICmd[6];
+ uint64_t total_sectors;
+
+ if (layer != 0)
{
- int layer = s->aATAPICmd[6];
- uint64_t total_sectors;
-
- if (layer != 0)
- {
- uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
- break;
- }
-
- total_sectors = s->cTotalSectors;
- total_sectors >>= 2;
- if (total_sectors == 0)
- {
- uASC = -SCSI_ASC_MEDIUM_NOT_PRESENT;
- break;
- }
-
- buf[4] = 1; /* DVD-ROM, part version 1 */
- buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
- buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */
- buf[7] = 0; /* default densities */
-
- /* FIXME: 0x30000 per spec? */
- ataH2BE_U32(buf + 8, 0); /* start sector */
- ataH2BE_U32(buf + 12, total_sectors - 1); /* end sector */
- ataH2BE_U32(buf + 16, total_sectors - 1); /* l0 end sector */
-
- /* Size of buffer, not including 2 byte size field */
- ataH2BE_U32(&buf[0], 2048 + 2);
-
- /* 2k data + 4 byte header */
- uASC = (2048 + 4);
+ uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
+ break;
}
+
+ total_sectors = s->cTotalSectors;
+ total_sectors >>= 2;
+ if (total_sectors == 0)
+ {
+ uASC = -SCSI_ASC_MEDIUM_NOT_PRESENT;
+ break;
+ }
+
+ buf[4] = 1; /* DVD-ROM, part version 1 */
+ buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
+ buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */
+ buf[7] = 0; /* default densities */
+
+ /* FIXME: 0x30000 per spec? */
+ ataH2BE_U32(buf + 8, 0); /* start sector */
+ ataH2BE_U32(buf + 12, total_sectors - 1); /* end sector */
+ ataH2BE_U32(buf + 16, total_sectors - 1); /* l0 end sector */
+
+ /* Size of buffer, not including 2 byte size field */
+ ataH2BE_U32(&buf[0], 2048 + 2);
+
+ /* 2k data + 4 byte header */
+ uASC = (2048 + 4);
break;
+ }
case 0x01: /* DVD copyright information */
buf[4] = 0; /* no copyright data */
buf[5] = 0; /* no region restrictions */
@@ -2597,7 +2465,7 @@ static bool atapiReadTrackInformationSS(ATADevState *s)
return false;
}
-static size_t atapiGetConfigurationFillFeatureListProfiles(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
+static uint32_t atapiGetConfigurationFillFeatureListProfiles(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
{
if (cbBuf < 3*4)
return 0;
@@ -2615,7 +2483,7 @@ static size_t atapiGetConfigurationFillFeatureListProfiles(ATADevState *s, uint8
return 3*4; /* Header + 2 profiles entries */
}
-static size_t atapiGetConfigurationFillFeatureCore(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
+static uint32_t atapiGetConfigurationFillFeatureCore(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
{
if (cbBuf < 12)
return 0;
@@ -2630,7 +2498,7 @@ static size_t atapiGetConfigurationFillFeatureCore(ATADevState *s, uint8_t *pbBu
return 12;
}
-static size_t atapiGetConfigurationFillFeatureMorphing(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
+static uint32_t atapiGetConfigurationFillFeatureMorphing(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
{
if (cbBuf < 8)
return 0;
@@ -2644,7 +2512,7 @@ static size_t atapiGetConfigurationFillFeatureMorphing(ATADevState *s, uint8_t *
return 8;
}
-static size_t atapiGetConfigurationFillFeatureRemovableMedium(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
+static uint32_t atapiGetConfigurationFillFeatureRemovableMedium(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
{
if (cbBuf < 8)
return 0;
@@ -2659,7 +2527,7 @@ static size_t atapiGetConfigurationFillFeatureRemovableMedium(ATADevState *s, ui
return 8;
}
-static size_t atapiGetConfigurationFillFeatureRandomReadable(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
+static uint32_t atapiGetConfigurationFillFeatureRandomReadable(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
{
if (cbBuf < 12)
return 0;
@@ -2675,7 +2543,7 @@ static size_t atapiGetConfigurationFillFeatureRandomReadable(ATADevState *s, uin
return 12;
}
-static size_t atapiGetConfigurationFillFeatureCDRead(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
+static uint32_t atapiGetConfigurationFillFeatureCDRead(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
{
if (cbBuf < 8)
return 0;
@@ -2689,7 +2557,7 @@ static size_t atapiGetConfigurationFillFeatureCDRead(ATADevState *s, uint8_t *pb
return 8;
}
-static size_t atapiGetConfigurationFillFeaturePowerManagement(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
+static uint32_t atapiGetConfigurationFillFeaturePowerManagement(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
{
if (cbBuf < 4)
return 0;
@@ -2701,7 +2569,7 @@ static size_t atapiGetConfigurationFillFeaturePowerManagement(ATADevState *s, ui
return 4;
}
-static size_t atapiGetConfigurationFillFeatureTimeout(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
+static uint32_t atapiGetConfigurationFillFeatureTimeout(ATADevState *s, uint8_t *pbBuf, size_t cbBuf)
{
if (cbBuf < 8)
return 0;
@@ -2717,8 +2585,8 @@ static size_t atapiGetConfigurationFillFeatureTimeout(ATADevState *s, uint8_t *p
static bool atapiGetConfigurationSS(ATADevState *s)
{
uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
- size_t cbBuf = s->cbIOBuffer;
- size_t cbCopied = 0;
+ uint32_t cbBuf = s->cbIOBuffer;
+ uint32_t cbCopied = 0;
uint16_t u16Sfn = ataBE2H_U16(&s->aATAPICmd[2]);
Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
@@ -3201,37 +3069,37 @@ static void atapiParseCmdVirtualATAPI(ATADevState *s)
ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION, true);
break;
case SCSI_MODE_SENSE_10:
+ {
+ uint8_t uPageControl, uPageCode;
+ cbMax = ataBE2H_U16(pbPacket + 7);
+ uPageControl = pbPacket[2] >> 6;
+ uPageCode = pbPacket[2] & 0x3f;
+ switch (uPageControl)
{
- uint8_t uPageControl, uPageCode;
- cbMax = ataBE2H_U16(pbPacket + 7);
- uPageControl = pbPacket[2] >> 6;
- uPageCode = pbPacket[2] & 0x3f;
- switch (uPageControl)
- {
- case SCSI_PAGECONTROL_CURRENT:
- switch (uPageCode)
- {
- case SCSI_MODEPAGE_ERROR_RECOVERY:
- ataStartTransfer(s, RT_MIN(cbMax, 16), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY, true);
- break;
- case SCSI_MODEPAGE_CD_STATUS:
- ataStartTransfer(s, RT_MIN(cbMax, 40), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS, true);
- break;
- default:
- goto error_cmd;
- }
- break;
- case SCSI_PAGECONTROL_CHANGEABLE:
- goto error_cmd;
- case SCSI_PAGECONTROL_DEFAULT:
- goto error_cmd;
- default:
- case SCSI_PAGECONTROL_SAVED:
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
- break;
- }
+ case SCSI_PAGECONTROL_CURRENT:
+ switch (uPageCode)
+ {
+ case SCSI_MODEPAGE_ERROR_RECOVERY:
+ ataStartTransfer(s, RT_MIN(cbMax, 16), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY, true);
+ break;
+ case SCSI_MODEPAGE_CD_STATUS:
+ ataStartTransfer(s, RT_MIN(cbMax, 40), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS, true);
+ break;
+ default:
+ goto error_cmd;
+ }
+ break;
+ case SCSI_PAGECONTROL_CHANGEABLE:
+ goto error_cmd;
+ case SCSI_PAGECONTROL_DEFAULT:
+ goto error_cmd;
+ default:
+ case SCSI_PAGECONTROL_SAVED:
+ atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
+ break;
}
break;
+ }
case SCSI_REQUEST_SENSE:
cbMax = pbPacket[4];
ataStartTransfer(s, RT_MIN(cbMax, 18), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_REQUEST_SENSE, true);
@@ -3250,228 +3118,228 @@ static void atapiParseCmdVirtualATAPI(ATADevState *s)
break;
case SCSI_READ_10:
case SCSI_READ_12:
- {
- uint32_t cSectors, iATAPILBA;
+ {
+ uint32_t cSectors, iATAPILBA;
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- if (pbPacket[0] == SCSI_READ_10)
- cSectors = ataBE2H_U16(pbPacket + 7);
- else
- cSectors = ataBE2H_U32(pbPacket + 6);
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- if (cSectors == 0)
- {
- atapiCmdOK(s);
- break;
- }
- if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors)
+ if (s->cNotifiedMediaChange > 0)
+ {
+ s->cNotifiedMediaChange-- ;
+ atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
+ break;
+ }
+ else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
+ {
+ atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ if (pbPacket[0] == SCSI_READ_10)
+ cSectors = ataBE2H_U16(pbPacket + 7);
+ else
+ cSectors = ataBE2H_U32(pbPacket + 6);
+ iATAPILBA = ataBE2H_U32(pbPacket + 2);
+ if (cSectors == 0)
+ {
+ atapiCmdOK(s);
+ break;
+ }
+ if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors)
+ {
+ /* Rate limited logging, one log line per second. For
+ * guests that insist on reading from places outside the
+ * valid area this often generates too many release log
+ * entries otherwise. */
+ static uint64_t uLastLogTS = 0;
+ if (RTTimeMilliTS() >= uLastLogTS + 1000)
{
- /* Rate limited logging, one log line per second. For
- * guests that insist on reading from places outside the
- * valid area this often generates too many release log
- * entries otherwise. */
- static uint64_t uLastLogTS = 0;
- if (RTTimeMilliTS() >= uLastLogTS + 1000)
- {
- LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors));
- uLastLogTS = RTTimeMilliTS();
- }
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
- break;
+ LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors));
+ uLastLogTS = RTTimeMilliTS();
}
- atapiReadSectors(s, iATAPILBA, cSectors, 2048);
+ atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
+ break;
}
+ atapiReadSectors(s, iATAPILBA, cSectors, 2048);
break;
+ }
case SCSI_READ_CD:
- {
- uint32_t cSectors, iATAPILBA;
+ {
+ uint32_t cSectors, iATAPILBA;
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
+ if (s->cNotifiedMediaChange > 0)
+ {
+ s->cNotifiedMediaChange-- ;
+ atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
+ break;
+ }
+ else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
+ {
+ atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8];
+ iATAPILBA = ataBE2H_U32(pbPacket + 2);
+ if (cSectors == 0)
+ {
+ atapiCmdOK(s);
+ break;
+ }
+ if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors)
+ {
+ /* Rate limited logging, one log line per second. For
+ * guests that insist on reading from places outside the
+ * valid area this often generates too many release log
+ * entries otherwise. */
+ static uint64_t uLastLogTS = 0;
+ if (RTTimeMilliTS() >= uLastLogTS + 1000)
{
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
+ LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors));
+ uLastLogTS = RTTimeMilliTS();
}
- cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8];
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- if (cSectors == 0)
- {
+ atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
+ break;
+ }
+ switch (pbPacket[9] & 0xf8)
+ {
+ case 0x00:
+ /* nothing */
atapiCmdOK(s);
break;
- }
- if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors)
- {
- /* Rate limited logging, one log line per second. For
- * guests that insist on reading from places outside the
- * valid area this often generates too many release log
- * entries otherwise. */
- static uint64_t uLastLogTS = 0;
- if (RTTimeMilliTS() >= uLastLogTS + 1000)
- {
- LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors));
- uLastLogTS = RTTimeMilliTS();
- }
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
+ case 0x10:
+ /* normal read */
+ atapiReadSectors(s, iATAPILBA, cSectors, 2048);
+ break;
+ case 0xf8:
+ /* read all data */
+ atapiReadSectors(s, iATAPILBA, cSectors, 2352);
+ break;
+ default:
+ LogRel(("PIIX3 ATA: LUN#%d: CD-ROM sector format not supported (%#x)\n", s->iLUN, pbPacket[9] & 0xf8));
+ atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
break;
- }
- switch (pbPacket[9] & 0xf8)
- {
- case 0x00:
- /* nothing */
- atapiCmdOK(s);
- break;
- case 0x10:
- /* normal read */
- atapiReadSectors(s, iATAPILBA, cSectors, 2048);
- break;
- case 0xf8:
- /* read all data */
- atapiReadSectors(s, iATAPILBA, cSectors, 2352);
- break;
- default:
- LogRel(("PIIX3 ATA: LUN#%d: CD-ROM sector format not supported (%#x)\n", s->iLUN, pbPacket[9] & 0xf8));
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- break;
- }
}
break;
+ }
case SCSI_SEEK_10:
+ {
+ uint32_t iATAPILBA;
+ if (s->cNotifiedMediaChange > 0)
{
- uint32_t iATAPILBA;
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
- break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
- break;
- }
- iATAPILBA = ataBE2H_U32(pbPacket + 2);
- if (iATAPILBA > s->cTotalSectors)
+ s->cNotifiedMediaChange-- ;
+ atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
+ break;
+ }
+ else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
+ {
+ atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ iATAPILBA = ataBE2H_U32(pbPacket + 2);
+ if (iATAPILBA > s->cTotalSectors)
+ {
+ /* Rate limited logging, one log line per second. For
+ * guests that insist on seeking to places outside the
+ * valid area this often generates too many release log
+ * entries otherwise. */
+ static uint64_t uLastLogTS = 0;
+ if (RTTimeMilliTS() >= uLastLogTS + 1000)
{
- /* Rate limited logging, one log line per second. For
- * guests that insist on seeking to places outside the
- * valid area this often generates too many release log
- * entries otherwise. */
- static uint64_t uLastLogTS = 0;
- if (RTTimeMilliTS() >= uLastLogTS + 1000)
- {
- LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", s->iLUN, (uint64_t)iATAPILBA));
- uLastLogTS = RTTimeMilliTS();
- }
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
- break;
+ LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", s->iLUN, (uint64_t)iATAPILBA));
+ uLastLogTS = RTTimeMilliTS();
}
- atapiCmdOK(s);
- ataSetStatus(s, ATA_STAT_SEEK); /* Linux expects this. */
+ atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
+ break;
}
+ atapiCmdOK(s);
+ ataSetStatus(s, ATA_STAT_SEEK); /* Linux expects this. */
break;
+ }
case SCSI_START_STOP_UNIT:
+ {
+ int rc = VINF_SUCCESS;
+ switch (pbPacket[4] & 3)
{
- int rc = VINF_SUCCESS;
- switch (pbPacket[4] & 3)
+ case 0: /* 00 - Stop motor */
+ case 1: /* 01 - Start motor */
+ break;
+ case 2: /* 10 - Eject media */
{
- case 0: /* 00 - Stop motor */
- case 1: /* 01 - Start motor */
- break;
- case 2: /* 10 - Eject media */
+ /* This must be done from EMT. */
+ PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
+ PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s);
+ PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
+
+ PDMCritSectLeave(&pCtl->lock);
+ rc = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
+ (PFNRT)s->pDrvMount->pfnUnmount, 3,
+ s->pDrvMount, false /*=fForce*/, true /*=fEject*/);
+ Assert(RT_SUCCESS(rc) || rc == VERR_PDM_MEDIA_LOCKED || rc == VERR_PDM_MEDIA_NOT_MOUNTED);
+ if (RT_SUCCESS(rc) && pThis->pMediaNotify)
{
- /* This must be done from EMT. */
- PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
- PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s);
- PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
-
- PDMCritSectLeave(&pCtl->lock);
- rc = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
- (PFNRT)s->pDrvMount->pfnUnmount, 3,
- s->pDrvMount, false /*=fForce*/, true /*=fEject*/);
- Assert(RT_SUCCESS(rc) || (rc == VERR_PDM_MEDIA_LOCKED) || (rc = VERR_PDM_MEDIA_NOT_MOUNTED));
- if (RT_SUCCESS(rc) && pThis->pMediaNotify)
- {
- rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
- (PFNRT)pThis->pMediaNotify->pfnEjected, 2,
- pThis->pMediaNotify, s->iLUN);
- AssertRC(rc);
- }
- {
- STAM_PROFILE_START(&pCtl->StatLockWait, a);
- PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
- STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
- }
- break;
+ rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
+ (PFNRT)pThis->pMediaNotify->pfnEjected, 2,
+ pThis->pMediaNotify, s->iLUN);
+ AssertRC(rc);
}
- case 3: /* 11 - Load media */
- /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */
- break;
+ {
+ STAM_PROFILE_START(&pCtl->StatLockWait, a);
+ PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
+ STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
+ }
+ break;
}
- if (RT_SUCCESS(rc))
- atapiCmdOK(s);
- else
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED);
+ case 3: /* 11 - Load media */
+ /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */
+ break;
}
+ if (RT_SUCCESS(rc))
+ atapiCmdOK(s);
+ else
+ atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED);
break;
+ }
case SCSI_MECHANISM_STATUS:
- {
- cbMax = ataBE2H_U16(pbPacket + 8);
- ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MECHANISM_STATUS, true);
- }
+ {
+ cbMax = ataBE2H_U16(pbPacket + 8);
+ ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MECHANISM_STATUS, true);
break;
+ }
case SCSI_READ_TOC_PMA_ATIP:
- {
- uint8_t format;
+ {
+ uint8_t format;
- if (s->cNotifiedMediaChange > 0)
- {
- s->cNotifiedMediaChange-- ;
- atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
+ if (s->cNotifiedMediaChange > 0)
+ {
+ s->cNotifiedMediaChange-- ;
+ atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
+ break;
+ }
+ else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
+ {
+ atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ cbMax = ataBE2H_U16(pbPacket + 7);
+ /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits),
+ * but Linux kernel uses offset 9 (topmost 2 bits). Hope that
+ * the other field is clear... */
+ format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6);
+ switch (format)
+ {
+ case 0:
+ ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_NORMAL, true);
break;
- }
- else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
- {
- atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
+ case 1:
+ ataStartTransfer(s, RT_MIN(cbMax, 12), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_MULTI, true);
+ break;
+ case 2:
+ ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_RAW, true);
+ break;
+ default:
+ error_cmd:
+ atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
break;
- }
- cbMax = ataBE2H_U16(pbPacket + 7);
- /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits),
- * but Linux kernel uses offset 9 (topmost 2 bits). Hope that
- * the other field is clear... */
- format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6);
- switch (format)
- {
- case 0:
- ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_NORMAL, true);
- break;
- case 1:
- ataStartTransfer(s, RT_MIN(cbMax, 12), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_MULTI, true);
- break;
- case 2:
- ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_RAW, true);
- break;
- default:
- error_cmd:
- atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
- break;
- }
}
break;
+ }
case SCSI_READ_CAPACITY:
if (s->cNotifiedMediaChange > 0)
{
@@ -3648,11 +3516,20 @@ static void atapiParseCmdPassthrough(ATADevState *s)
switch ((pbPacket[1] >> 2) & 0x7)
{
case 0x0: /* All types. */
- if (ASMAtomicReadU32(&s->MediaTrackType) == ATA_MEDIA_TYPE_CDDA)
- s->cbATAPISector = 2352;
+ {
+ uint32_t iLbaStart;
+
+ if (pbPacket[0] == SCSI_READ_CD)
+ iLbaStart = ataBE2H_U32(&pbPacket[2]);
+ else
+ iLbaStart = ataMSF2LBA(&pbPacket[3]);
+
+ if (s->pTrackList)
+ s->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(s->pTrackList, iLbaStart);
else
s->cbATAPISector = 2048; /* Might be incorrect if we couldn't determine the type. */
break;
+ }
case 0x1: /* CD-DA */
s->cbATAPISector = 2352;
break;
@@ -3772,7 +3649,10 @@ static void atapiParseCmdPassthrough(ATADevState *s)
case SCSI_WRITE_AND_VERIFY_10:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U16(pbPacket + 7);
- s->cbATAPISector = atapiGetSectorSizeFromLba(s, iATAPILBA);
+ if (s->pTrackList)
+ s->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(s->pTrackList, iATAPILBA);
+ else
+ s->cbATAPISector = 2048;
Log2(("ATAPI PT: lba %d sectors %d sector size %d\n", iATAPILBA, cSectors, s->cbATAPISector));
cbTransfer = cSectors * s->cbATAPISector;
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
@@ -3780,7 +3660,10 @@ static void atapiParseCmdPassthrough(ATADevState *s)
case SCSI_WRITE_12:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U32(pbPacket + 6);
- s->cbATAPISector = atapiGetSectorSizeFromLba(s, iATAPILBA);
+ if (s->pTrackList)
+ s->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(s->pTrackList, iATAPILBA);
+ else
+ s->cbATAPISector = 2048;
Log2(("ATAPI PT: lba %d sectors %d sector size %d\n", iATAPILBA, cSectors, s->cbATAPISector));
cbTransfer = cSectors * s->cbATAPISector;
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
@@ -3822,7 +3705,7 @@ static void atapiParseCmdPassthrough(ATADevState *s)
sendcmd:
/*
* Send a command to the drive, passing data in/out as required.
- * Commands which exceed the I/O buffer size are splitted below
+ * Commands which exceed the I/O buffer size are split below
* or aborted if splitting is not implemented.
*/
Log2(("ATAPI PT: max size %d\n", cbTransfer));
@@ -3916,7 +3799,7 @@ static DECLCALLBACK(void) ataMountNotify(PPDMIMOUNTNOTIFY pInterface)
if (pIf->fATAPI)
pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 2048;
else
- pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 512;
+ pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / pIf->cbSector;
LogRel(("PIIX3 ATA: LUN#%d: CD/DVD, total number of sectors %Ld, passthrough unchanged\n", pIf->iLUN, pIf->cTotalSectors));
@@ -4003,8 +3886,8 @@ static int ataTrimSectors(ATADevState *s, uint64_t u64Sector, uint32_t cSectors,
PDMCritSectLeave(&pCtl->lock);
- TrimRange.offStart = u64Sector * 512;
- TrimRange.cbRange = cSectors * 512;
+ TrimRange.offStart = u64Sector * s->cbSector;
+ TrimRange.cbRange = cSectors * s->cbSector;
s->Led.Asserted.s.fWriting = s->Led.Actual.s.fWriting = 1;
rc = s->pDrvBlock->pfnDiscard(s->pDrvBlock, &TrimRange, 1);
@@ -4143,7 +4026,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd)
if (!s->pDrvBlock || s->fATAPI)
goto abort_cmd;
s->cSectorsPerIRQ = 1;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
+ ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
break;
case ATA_WRITE_SECTORS_EXT:
s->fLBA48 = true;
@@ -4152,7 +4035,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd)
if (!s->pDrvBlock || s->fATAPI)
goto abort_cmd;
s->cSectorsPerIRQ = 1;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
+ ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
break;
case ATA_READ_MULTIPLE_EXT:
s->fLBA48 = true;
@@ -4160,7 +4043,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd)
if (!s->pDrvBlock || !s->cMultSectors || s->fATAPI)
goto abort_cmd;
s->cSectorsPerIRQ = s->cMultSectors;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
+ ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
break;
case ATA_WRITE_MULTIPLE_EXT:
s->fLBA48 = true;
@@ -4168,7 +4051,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd)
if (!s->pDrvBlock || !s->cMultSectors || s->fATAPI)
goto abort_cmd;
s->cSectorsPerIRQ = s->cMultSectors;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
+ ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
break;
case ATA_READ_DMA_EXT:
s->fLBA48 = true;
@@ -4178,7 +4061,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd)
goto abort_cmd;
s->cSectorsPerIRQ = ATA_MAX_MULT_SECTORS;
s->fDMA = true;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
+ ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
break;
case ATA_WRITE_DMA_EXT:
s->fLBA48 = true;
@@ -4188,7 +4071,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd)
goto abort_cmd;
s->cSectorsPerIRQ = ATA_MAX_MULT_SECTORS;
s->fDMA = true;
- ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
+ ataStartTransfer(s, ataGetNSectors(s) * s->cbSector, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
break;
case ATA_READ_NATIVE_MAX_ADDRESS_EXT:
s->fLBA48 = true;
@@ -4330,7 +4213,7 @@ static void ataParseCmd(ATADevState *s, uint8_t cmd)
|| (s->uATARegFeature & ~UINT8_C(0x01)))
goto abort_cmd;
s->fDMA = true;
- ataStartTransfer(s, (s->uATARegNSectorHOB << 8 | s->uATARegNSector) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_NULL, ATAFN_SS_TRIM, false);
+ ataStartTransfer(s, (s->uATARegNSectorHOB << 8 | s->uATARegNSector) * s->cbSector, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_NULL, ATAFN_SS_TRIM, false);
break;
default:
abort_cmd:
@@ -4641,9 +4524,12 @@ static uint32_t ataStatusRead(PATACONTROLLER pCtl, uint32_t addr)
ATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
uint32_t val;
- if ((!pCtl->aIfs[0].pDrvBlock && !pCtl->aIfs[1].pDrvBlock) ||
- (pCtl->iSelectedIf == 1 && !s->pDrvBlock))
- val = 0;
+ //@todo: The handler should not be even registered if there
+ // is no device on an IDE channel.
+ if (!pCtl->aIfs[0].pDrvBlock && !pCtl->aIfs[1].pDrvBlock)
+ val = 0xff;
+ else if (pCtl->iSelectedIf == 1 && !s->pDrvBlock)
+ val = 0; /* Device 1 selected, Device 0 responding for it. */
else
val = s->uATARegStatus;
Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val));
@@ -5018,9 +4904,9 @@ static void ataDMATransfer(PATACONTROLLER pCtl)
PCIATAState *pATAState = PDMINS_2_DATA(pDevIns, PCIATAState *);
AssertPtr(pATAState);
if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
- PDMDevHlpPCIDevPhysWrite(&pATAState->dev, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen);
+ PDMDevHlpPCIPhysWrite(pDevIns, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen);
else
- PDMDevHlpPCIDevPhysRead(&pATAState->dev, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen);
+ PDMDevHlpPCIPhysRead(pDevIns, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen);
iIOBufferCur += dmalen;
cbTotalTransfer -= dmalen;
@@ -6221,120 +6107,6 @@ DECLINLINE(void) ataRelocBuffer(PPDMDEVINS pDevIns, ATADevState *s)
/**
- * @copydoc FNPDMDEVRELOCATE
- */
-static DECLCALLBACK(void) ataR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
-{
- PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
-
- for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
- {
- pThis->aCts[i].pDevInsRC += offDelta;
- pThis->aCts[i].aIfs[0].pDevInsRC += offDelta;
- pThis->aCts[i].aIfs[0].pControllerRC += offDelta;
- ataRelocBuffer(pDevIns, &pThis->aCts[i].aIfs[0]);
- pThis->aCts[i].aIfs[1].pDevInsRC += offDelta;
- pThis->aCts[i].aIfs[1].pControllerRC += offDelta;
- ataRelocBuffer(pDevIns, &pThis->aCts[i].aIfs[1]);
- }
-}
-
-
-/**
- * Destroy a driver instance.
- *
- * Most VM resources are freed by the VM. This callback is provided so that any non-VM
- * resources can be freed correctly.
- *
- * @param pDevIns The device instance data.
- */
-static DECLCALLBACK(int) ataR3Destruct(PPDMDEVINS pDevIns)
-{
- PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
- int rc;
-
- Log(("ataR3Destruct\n"));
- PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
-
- /*
- * Tell the async I/O threads to terminate.
- */
- for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
- {
- if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD)
- {
- ASMAtomicWriteU32(&pThis->aCts[i].fShutdown, true);
- rc = RTSemEventSignal(pThis->aCts[i].AsyncIOSem);
- AssertRC(rc);
- rc = RTSemEventSignal(pThis->aCts[i].SuspendIOSem);
- AssertRC(rc);
- }
- }
-
- /*
- * Wait for the threads to terminate before destroying their resources.
- */
- for (unsigned i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
- {
- if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD)
- {
- rc = RTThreadWait(pThis->aCts[i].AsyncIOThread, 30000 /* 30 s*/, NULL);
- if (RT_SUCCESS(rc))
- pThis->aCts[i].AsyncIOThread = NIL_RTTHREAD;
- else
- LogRel(("PIIX3 ATA Dtor: Ctl#%u is still executing, DevSel=%d AIOIf=%d CmdIf0=%#04x CmdIf1=%#04x rc=%Rrc\n",
- i, pThis->aCts[i].iSelectedIf, pThis->aCts[i].iAIOIf,
- pThis->aCts[i].aIfs[0].uATARegCommand, pThis->aCts[i].aIfs[1].uATARegCommand, rc));
- }
- }
-
- /*
- * Free resources.
- */
- for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
- {
- if (pThis->aCts[i].AsyncIORequestMutex != NIL_RTSEMMUTEX)
- {
- RTSemMutexDestroy(pThis->aCts[i].AsyncIORequestMutex);
- pThis->aCts[i].AsyncIORequestMutex = NIL_RTSEMMUTEX;
- }
- if (pThis->aCts[i].AsyncIOSem != NIL_RTSEMEVENT)
- {
- RTSemEventDestroy(pThis->aCts[i].AsyncIOSem);
- pThis->aCts[i].AsyncIOSem = NIL_RTSEMEVENT;
- }
- if (pThis->aCts[i].SuspendIOSem != NIL_RTSEMEVENT)
- {
- RTSemEventDestroy(pThis->aCts[i].SuspendIOSem);
- pThis->aCts[i].SuspendIOSem = NIL_RTSEMEVENT;
- }
-
- /* try one final time */
- if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD)
- {
- rc = RTThreadWait(pThis->aCts[i].AsyncIOThread, 1 /*ms*/, NULL);
- if (RT_SUCCESS(rc))
- {
- pThis->aCts[i].AsyncIOThread = NIL_RTTHREAD;
- LogRel(("PIIX3 ATA Dtor: Ctl#%u actually completed.\n", i));
- }
- }
-
- for (uint32_t iIf = 0; iIf < RT_ELEMENTS(pThis->aCts[i].aIfs); iIf++)
- {
- if (pThis->aCts[i].aIfs[iIf].pbCueSheet)
- {
- RTMemFree(pThis->aCts[i].aIfs[iIf].pbCueSheet);
- pThis->aCts[i].aIfs[iIf].pbCueSheet = NULL;
- }
- }
- }
-
- return VINF_SUCCESS;
-}
-
-
-/**
* Detach notification.
*
* The DVD drive has been unplugged.
@@ -6434,6 +6206,11 @@ static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf)
/*
* Allocate I/O buffer.
*/
+ if (pIf->fATAPI)
+ pIf->cbSector = 2048;
+ else
+ pIf->cbSector = pIf->pDrvBlock->pfnGetSectorSize(pIf->pDrvBlock);
+
PVM pVM = PDMDevHlpGetVM(pDevIns);
if (pIf->cbIOBuffer)
{
@@ -6442,7 +6219,7 @@ static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf)
if (pIf->fATAPI)
AssertRelease(pIf->cbIOBuffer == _128K);
else
- AssertRelease(pIf->cbIOBuffer == ATA_MAX_MULT_SECTORS * 512);
+ AssertRelease(pIf->cbIOBuffer == ATA_MAX_MULT_SECTORS * pIf->cbSector);
Assert(pIf->pbIOBufferR3);
Assert(pIf->pbIOBufferR0 == MMHyperR3ToR0(pVM, pIf->pbIOBufferR3));
Assert(pIf->pbIOBufferRC == MMHyperR3ToRC(pVM, pIf->pbIOBufferR3));
@@ -6452,7 +6229,7 @@ static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf)
if (pIf->fATAPI)
pIf->cbIOBuffer = _128K;
else
- pIf->cbIOBuffer = ATA_MAX_MULT_SECTORS * 512;
+ pIf->cbIOBuffer = ATA_MAX_MULT_SECTORS * pIf->cbSector;
Assert(!pIf->pbIOBufferR3);
rc = MMR3HyperAllocOnceNoRel(pVM, pIf->cbIOBuffer, 0, MM_TAG_PDM_DEVICE_USER, (void **)&pIf->pbIOBufferR3);
if (RT_FAILURE(rc))
@@ -6466,7 +6243,7 @@ static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf)
*/
if (pIf->fATAPI)
{
- pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 2048;
+ pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / pIf->cbSector;
pIf->PCHSGeometry.cCylinders = 0; /* dummy */
pIf->PCHSGeometry.cHeads = 0; /* dummy */
pIf->PCHSGeometry.cSectors = 0; /* dummy */
@@ -6474,7 +6251,7 @@ static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf)
}
else
{
- pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 512;
+ pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / pIf->cbSector;
rc = pIf->pDrvBlockBios->pfnGetPCHSGeometry(pIf->pDrvBlockBios,
&pIf->PCHSGeometry);
if (rc == VERR_PDM_MEDIA_NOT_MOUNTED)
@@ -6639,183 +6416,6 @@ static bool ataR3AllAsyncIOIsIdle(PPDMDEVINS pDevIns)
return true;
}
-
-/**
- * Callback employed by ataSuspend and ataR3PowerOff.
- *
- * @returns true if we've quiesced, false if we're still working.
- * @param pDevIns The device instance.
- */
-static DECLCALLBACK(bool) ataR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
-{
- return ataR3AllAsyncIOIsIdle(pDevIns);
-}
-
-
-/**
- * Common worker for ataSuspend and ataR3PowerOff.
- */
-static void ataR3SuspendOrPowerOff(PPDMDEVINS pDevIns)
-{
- if (!ataR3AllAsyncIOIsIdle(pDevIns))
- PDMDevHlpSetAsyncNotification(pDevIns, ataR3IsAsyncSuspendOrPowerOffDone);
-}
-
-
-/**
- * Power Off notification.
- *
- * @returns VBox status.
- * @param pDevIns The device instance data.
- */
-static DECLCALLBACK(void) ataR3PowerOff(PPDMDEVINS pDevIns)
-{
- Log(("%s:\n", __FUNCTION__));
- ataR3SuspendOrPowerOff(pDevIns);
-}
-
-
-/**
- * Suspend notification.
- *
- * @returns VBox status.
- * @param pDevIns The device instance data.
- */
-static DECLCALLBACK(void) ataR3Suspend(PPDMDEVINS pDevIns)
-{
- Log(("%s:\n", __FUNCTION__));
- ataR3SuspendOrPowerOff(pDevIns);
-}
-
-
-/**
- * Callback employed by ataR3Reset.
- *
- * @returns true if we've quiesced, false if we're still working.
- * @param pDevIns The device instance.
- */
-static DECLCALLBACK(bool) ataR3IsAsyncResetDone(PPDMDEVINS pDevIns)
-{
- PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
-
- if (!ataR3AllAsyncIOIsIdle(pDevIns))
- return false;
-
- for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
- {
- PDMCritSectEnter(&pThis->aCts[i].lock, VERR_INTERNAL_ERROR);
- for (uint32_t j = 0; j < RT_ELEMENTS(pThis->aCts[i].aIfs); j++)
- ataResetDevice(&pThis->aCts[i].aIfs[j]);
- PDMCritSectLeave(&pThis->aCts[i].lock);
- }
- return true;
-}
-
-
-/**
- * Common reset worker for ataR3Reset and ataR3Construct.
- *
- * @returns VBox status.
- * @param pDevIns The device instance data.
- * @param fConstruct Indicates who is calling.
- */
-static int ataR3ResetCommon(PPDMDEVINS pDevIns, bool fConstruct)
-{
- PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
-
- for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
- {
- PDMCritSectEnter(&pThis->aCts[i].lock, VERR_INTERNAL_ERROR);
-
- pThis->aCts[i].iSelectedIf = 0;
- pThis->aCts[i].iAIOIf = 0;
- pThis->aCts[i].BmDma.u8Cmd = 0;
- /* Report that both drives present on the bus are in DMA mode. This
- * pretends that there is a BIOS that has set it up. Normal reset
- * default is 0x00. */
- pThis->aCts[i].BmDma.u8Status = (pThis->aCts[i].aIfs[0].pDrvBase != NULL ? BM_STATUS_D0DMA : 0)
- | (pThis->aCts[i].aIfs[1].pDrvBase != NULL ? BM_STATUS_D1DMA : 0);
- pThis->aCts[i].BmDma.pvAddr = 0;
-
- pThis->aCts[i].fReset = true;
- pThis->aCts[i].fRedo = false;
- pThis->aCts[i].fRedoIdle = false;
- ataAsyncIOClearRequests(&pThis->aCts[i]);
- Log2(("%s: Ctl#%d: message to async I/O thread, reset controller\n", __FUNCTION__, i));
- ataAsyncIOPutRequest(&pThis->aCts[i], &g_ataResetARequest);
- ataAsyncIOPutRequest(&pThis->aCts[i], &g_ataResetCRequest);
-
- PDMCritSectLeave(&pThis->aCts[i].lock);
- }
-
- int rcRet = VINF_SUCCESS;
- if (!fConstruct)
- {
- /*
- * Setup asynchronous notification completion if the requests haven't
- * completed yet.
- */
- if (!ataR3IsAsyncResetDone(pDevIns))
- PDMDevHlpSetAsyncNotification(pDevIns, ataR3IsAsyncResetDone);
- }
- else
- {
- /*
- * Wait for the requests for complete.
- *
- * Would be real nice if we could do it all from EMT(0) and not
- * involve the worker threads, then we could dispense with all the
- * waiting and semaphore ping-pong here...
- */
- for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
- {
- if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD)
- {
- int rc = RTSemMutexRequest(pThis->aCts[i].AsyncIORequestMutex, RT_INDEFINITE_WAIT);
- AssertRC(rc);
-
- ASMAtomicWriteBool(&pThis->aCts[i].fSignalIdle, true);
- rc = RTThreadUserReset(pThis->aCts[i].AsyncIOThread);
- AssertRC(rc);
-
- rc = RTSemMutexRelease(pThis->aCts[i].AsyncIORequestMutex);
- AssertRC(rc);
-
- if (!ataAsyncIOIsIdle(&pThis->aCts[i], false /*fStrict*/))
- {
- rc = RTThreadUserWait(pThis->aCts[i].AsyncIOThread, 30*1000 /*ms*/);
- if (RT_FAILURE(rc))
- rc = RTThreadUserWait(pThis->aCts[i].AsyncIOThread, 1000 /*ms*/);
- if (RT_FAILURE(rc))
- {
- AssertRC(rc);
- rcRet = rc;
- }
- }
- }
- ASMAtomicWriteBool(&pThis->aCts[i].fSignalIdle, false);
- }
- if (RT_SUCCESS(rcRet))
- {
- rcRet = ataR3IsAsyncResetDone(pDevIns) ? VINF_SUCCESS : VERR_INTERNAL_ERROR;
- AssertRC(rcRet);
- }
- }
- return rcRet;
-}
-
-
-/**
- * Reset notification.
- *
- * @param pDevIns The device instance data.
- */
-static DECLCALLBACK(void) ataR3Reset(PPDMDEVINS pDevIns)
-{
- ataR3ResetCommon(pDevIns, false /*fConstruct*/);
-}
-
-
/**
* Prepare state save and load operation.
*
@@ -6858,7 +6458,6 @@ static DECLCALLBACK(int) ataLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32
return VINF_SSM_DONT_CALL_AGAIN;
}
-
/**
* @copydoc FNSSMDEVSAVEEXEC
*/
@@ -7091,6 +6690,12 @@ static DECLCALLBACK(int) ataLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32
SSMR3GetBool(pSSM, &pThis->aCts[i].aIfs[j].fATAPITransfer);
SSMR3GetU32(pSSM, &pThis->aCts[i].aIfs[j].cbTotalTransfer);
SSMR3GetU32(pSSM, &pThis->aCts[i].aIfs[j].cbElementaryTransfer);
+ /* NB: cbPIOTransferLimit could be saved/restored but it's sufficient
+ * to re-calculate it here, with a tiny risk that it could be
+ * unnecessarily low for the current transfer only. Could be changed
+ * when changing the saved state in the future.
+ */
+ pThis->aCts[i].aIfs[j].cbPIOTransferLimit = (pThis->aCts[i].aIfs[j].uATARegHCyl << 8) | pThis->aCts[i].aIfs[j].uATARegLCyl;
SSMR3GetU32(pSSM, &pThis->aCts[i].aIfs[j].iIOBufferCur);
SSMR3GetU32(pSSM, &pThis->aCts[i].aIfs[j].iIOBufferEnd);
SSMR3GetU32(pSSM, &pThis->aCts[i].aIfs[j].iIOBufferPIODataStart);
@@ -7158,6 +6763,293 @@ static DECLCALLBACK(int) ataLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32
return VINF_SUCCESS;
}
+
+/**
+ * Callback employed by ataSuspend and ataR3PowerOff.
+ *
+ * @returns true if we've quiesced, false if we're still working.
+ * @param pDevIns The device instance.
+ */
+static DECLCALLBACK(bool) ataR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
+{
+ return ataR3AllAsyncIOIsIdle(pDevIns);
+}
+
+
+/**
+ * Common worker for ataSuspend and ataR3PowerOff.
+ */
+static void ataR3SuspendOrPowerOff(PPDMDEVINS pDevIns)
+{
+ if (!ataR3AllAsyncIOIsIdle(pDevIns))
+ PDMDevHlpSetAsyncNotification(pDevIns, ataR3IsAsyncSuspendOrPowerOffDone);
+}
+
+
+/**
+ * Power Off notification.
+ *
+ * @returns VBox status.
+ * @param pDevIns The device instance data.
+ */
+static DECLCALLBACK(void) ataR3PowerOff(PPDMDEVINS pDevIns)
+{
+ Log(("%s:\n", __FUNCTION__));
+ ataR3SuspendOrPowerOff(pDevIns);
+}
+
+
+/**
+ * Suspend notification.
+ *
+ * @returns VBox status.
+ * @param pDevIns The device instance data.
+ */
+static DECLCALLBACK(void) ataR3Suspend(PPDMDEVINS pDevIns)
+{
+ Log(("%s:\n", __FUNCTION__));
+ ataR3SuspendOrPowerOff(pDevIns);
+}
+
+
+/**
+ * Callback employed by ataR3Reset.
+ *
+ * @returns true if we've quiesced, false if we're still working.
+ * @param pDevIns The device instance.
+ */
+static DECLCALLBACK(bool) ataR3IsAsyncResetDone(PPDMDEVINS pDevIns)
+{
+ PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
+
+ if (!ataR3AllAsyncIOIsIdle(pDevIns))
+ return false;
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
+ {
+ PDMCritSectEnter(&pThis->aCts[i].lock, VERR_INTERNAL_ERROR);
+ for (uint32_t j = 0; j < RT_ELEMENTS(pThis->aCts[i].aIfs); j++)
+ ataResetDevice(&pThis->aCts[i].aIfs[j]);
+ PDMCritSectLeave(&pThis->aCts[i].lock);
+ }
+ return true;
+}
+
+
+/**
+ * Common reset worker for ataR3Reset and ataR3Construct.
+ *
+ * @returns VBox status.
+ * @param pDevIns The device instance data.
+ * @param fConstruct Indicates who is calling.
+ */
+static int ataR3ResetCommon(PPDMDEVINS pDevIns, bool fConstruct)
+{
+ PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
+ {
+ PDMCritSectEnter(&pThis->aCts[i].lock, VERR_INTERNAL_ERROR);
+
+ pThis->aCts[i].iSelectedIf = 0;
+ pThis->aCts[i].iAIOIf = 0;
+ pThis->aCts[i].BmDma.u8Cmd = 0;
+ /* Report that both drives present on the bus are in DMA mode. This
+ * pretends that there is a BIOS that has set it up. Normal reset
+ * default is 0x00. */
+ pThis->aCts[i].BmDma.u8Status = (pThis->aCts[i].aIfs[0].pDrvBase != NULL ? BM_STATUS_D0DMA : 0)
+ | (pThis->aCts[i].aIfs[1].pDrvBase != NULL ? BM_STATUS_D1DMA : 0);
+ pThis->aCts[i].BmDma.pvAddr = 0;
+
+ pThis->aCts[i].fReset = true;
+ pThis->aCts[i].fRedo = false;
+ pThis->aCts[i].fRedoIdle = false;
+ ataAsyncIOClearRequests(&pThis->aCts[i]);
+ Log2(("%s: Ctl#%d: message to async I/O thread, reset controller\n", __FUNCTION__, i));
+ ataAsyncIOPutRequest(&pThis->aCts[i], &g_ataResetARequest);
+ ataAsyncIOPutRequest(&pThis->aCts[i], &g_ataResetCRequest);
+
+ PDMCritSectLeave(&pThis->aCts[i].lock);
+ }
+
+ int rcRet = VINF_SUCCESS;
+ if (!fConstruct)
+ {
+ /*
+ * Setup asynchronous notification completion if the requests haven't
+ * completed yet.
+ */
+ if (!ataR3IsAsyncResetDone(pDevIns))
+ PDMDevHlpSetAsyncNotification(pDevIns, ataR3IsAsyncResetDone);
+ }
+ else
+ {
+ /*
+ * Wait for the requests for complete.
+ *
+ * Would be real nice if we could do it all from EMT(0) and not
+ * involve the worker threads, then we could dispense with all the
+ * waiting and semaphore ping-pong here...
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
+ {
+ if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD)
+ {
+ int rc = RTSemMutexRequest(pThis->aCts[i].AsyncIORequestMutex, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+
+ ASMAtomicWriteBool(&pThis->aCts[i].fSignalIdle, true);
+ rc = RTThreadUserReset(pThis->aCts[i].AsyncIOThread);
+ AssertRC(rc);
+
+ rc = RTSemMutexRelease(pThis->aCts[i].AsyncIORequestMutex);
+ AssertRC(rc);
+
+ if (!ataAsyncIOIsIdle(&pThis->aCts[i], false /*fStrict*/))
+ {
+ rc = RTThreadUserWait(pThis->aCts[i].AsyncIOThread, 30*1000 /*ms*/);
+ if (RT_FAILURE(rc))
+ rc = RTThreadUserWait(pThis->aCts[i].AsyncIOThread, 1000 /*ms*/);
+ if (RT_FAILURE(rc))
+ {
+ AssertRC(rc);
+ rcRet = rc;
+ }
+ }
+ }
+ ASMAtomicWriteBool(&pThis->aCts[i].fSignalIdle, false);
+ }
+ if (RT_SUCCESS(rcRet))
+ {
+ rcRet = ataR3IsAsyncResetDone(pDevIns) ? VINF_SUCCESS : VERR_INTERNAL_ERROR;
+ AssertRC(rcRet);
+ }
+ }
+ return rcRet;
+}
+
+/**
+ * Reset notification.
+ *
+ * @param pDevIns The device instance data.
+ */
+static DECLCALLBACK(void) ataR3Reset(PPDMDEVINS pDevIns)
+{
+ ataR3ResetCommon(pDevIns, false /*fConstruct*/);
+}
+
+/**
+ * @copydoc FNPDMDEVRELOCATE
+ */
+static DECLCALLBACK(void) ataR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+ PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
+ {
+ pThis->aCts[i].pDevInsRC += offDelta;
+ pThis->aCts[i].aIfs[0].pDevInsRC += offDelta;
+ pThis->aCts[i].aIfs[0].pControllerRC += offDelta;
+ ataRelocBuffer(pDevIns, &pThis->aCts[i].aIfs[0]);
+ pThis->aCts[i].aIfs[1].pDevInsRC += offDelta;
+ pThis->aCts[i].aIfs[1].pControllerRC += offDelta;
+ ataRelocBuffer(pDevIns, &pThis->aCts[i].aIfs[1]);
+ }
+}
+
+/**
+ * Destroy a driver instance.
+ *
+ * Most VM resources are freed by the VM. This callback is provided so that any non-VM
+ * resources can be freed correctly.
+ *
+ * @param pDevIns The device instance data.
+ */
+static DECLCALLBACK(int) ataR3Destruct(PPDMDEVINS pDevIns)
+{
+ PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
+ int rc;
+
+ Log(("ataR3Destruct\n"));
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+
+ /*
+ * Tell the async I/O threads to terminate.
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
+ {
+ if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD)
+ {
+ ASMAtomicWriteU32(&pThis->aCts[i].fShutdown, true);
+ rc = RTSemEventSignal(pThis->aCts[i].AsyncIOSem);
+ AssertRC(rc);
+ rc = RTSemEventSignal(pThis->aCts[i].SuspendIOSem);
+ AssertRC(rc);
+ }
+ }
+
+ /*
+ * Wait for the threads to terminate before destroying their resources.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
+ {
+ if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD)
+ {
+ rc = RTThreadWait(pThis->aCts[i].AsyncIOThread, 30000 /* 30 s*/, NULL);
+ if (RT_SUCCESS(rc))
+ pThis->aCts[i].AsyncIOThread = NIL_RTTHREAD;
+ else
+ LogRel(("PIIX3 ATA Dtor: Ctl#%u is still executing, DevSel=%d AIOIf=%d CmdIf0=%#04x CmdIf1=%#04x rc=%Rrc\n",
+ i, pThis->aCts[i].iSelectedIf, pThis->aCts[i].iAIOIf,
+ pThis->aCts[i].aIfs[0].uATARegCommand, pThis->aCts[i].aIfs[1].uATARegCommand, rc));
+ }
+ }
+
+ /*
+ * Free resources.
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
+ {
+ if (pThis->aCts[i].AsyncIORequestMutex != NIL_RTSEMMUTEX)
+ {
+ RTSemMutexDestroy(pThis->aCts[i].AsyncIORequestMutex);
+ pThis->aCts[i].AsyncIORequestMutex = NIL_RTSEMMUTEX;
+ }
+ if (pThis->aCts[i].AsyncIOSem != NIL_RTSEMEVENT)
+ {
+ RTSemEventDestroy(pThis->aCts[i].AsyncIOSem);
+ pThis->aCts[i].AsyncIOSem = NIL_RTSEMEVENT;
+ }
+ if (pThis->aCts[i].SuspendIOSem != NIL_RTSEMEVENT)
+ {
+ RTSemEventDestroy(pThis->aCts[i].SuspendIOSem);
+ pThis->aCts[i].SuspendIOSem = NIL_RTSEMEVENT;
+ }
+
+ /* try one final time */
+ if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD)
+ {
+ rc = RTThreadWait(pThis->aCts[i].AsyncIOThread, 1 /*ms*/, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->aCts[i].AsyncIOThread = NIL_RTTHREAD;
+ LogRel(("PIIX3 ATA Dtor: Ctl#%u actually completed.\n", i));
+ }
+ }
+
+ for (uint32_t iIf = 0; iIf < RT_ELEMENTS(pThis->aCts[i].aIfs); iIf++)
+ {
+ if (pThis->aCts[i].aIfs[iIf].pTrackList)
+ {
+ ATAPIPassthroughTrackListDestroy(pThis->aCts[i].aIfs[iIf].pTrackList);
+ pThis->aCts[i].aIfs[iIf].pTrackList = NULL;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
/**
* Convert config value to DEVPCBIOSBOOT.
*
@@ -7190,7 +7082,6 @@ static int ataControllerFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfg, CHIPSET *pen
return rc;
}
-
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
@@ -7346,6 +7237,12 @@ static DECLCALLBACK(int) ataR3Construct(PPDMDEVINS pDevIns, int iInstance, PCF
pThis->aCts[1].IOPortBase2 = 0x376;
/*
+ * Set the default critical section to NOP as we lock on controller level.
+ */
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ /*
* Register the PCI device.
* N.B. There's a hack in the PIIX3 PCI bridge device to assign this
* device the slot next to itself.
@@ -7714,7 +7611,7 @@ const PDMDEVREG g_DevicePIIX3IDE =
ataR3Destruct,
/* pfnRelocate */
ataR3Relocate,
- /* pfnIOCtl */
+ /* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
diff --git a/src/VBox/Devices/Storage/DevBusLogic.cpp b/src/VBox/Devices/Storage/DevBusLogic.cpp
index b4932a55..b0b52a12 100644
--- a/src/VBox/Devices/Storage/DevBusLogic.cpp
+++ b/src/VBox/Devices/Storage/DevBusLogic.cpp
@@ -1,10 +1,12 @@
/* $Id: DevBusLogic.cpp $ */
/** @file
- * VBox storage devices: BusLogic SCSI host adapter BT-958.
+ * VBox storage devices - BusLogic SCSI host adapter BT-958.
+ *
+ * Based on the Multi-Master Ultra SCSI Systems Technical Reference Manual.
*/
/*
- * Copyright (C) 2006-2012 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -15,14 +17,10 @@
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
-/* Implemented looking at the driver source in the linux kernel (drivers/scsi/BusLogic.[ch]).
- * See also: http://www.drdobbs.com/184410111
- */
/*******************************************************************************
* Header Files *
*******************************************************************************/
-//#define DEBUG
#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC
#include <VBox/vmm/pdmdev.h>
#include <VBox/vmm/pdmifs.h>
@@ -42,32 +40,50 @@
#include "VBoxSCSI.h"
#include "VBoxDD.h"
-/* Maximum number of attached devices the adapter can handle. */
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+/** Maximum number of attached devices the adapter can handle. */
#define BUSLOGIC_MAX_DEVICES 16
-/* Maximum number of scatter gather elements this device can handle. */
+/** Maximum number of scatter gather elements this device can handle. */
#define BUSLOGIC_MAX_SCATTER_GATHER_LIST_SIZE 128
-/* Size of the command buffer. */
-#define BUSLOGIC_COMMAND_SIZE_MAX 5
+/** Size of the command buffer. */
+#define BUSLOGIC_COMMAND_SIZE_MAX 53
-/* Size of the reply buffer. */
-#define BUSLOGIC_REPLY_SIZE_MAX 64
+/** Size of the reply buffer. */
+#define BUSLOGIC_REPLY_SIZE_MAX 64
-/*
- * Custom fixed I/O ports for BIOS controller access. Note that these should
- * not be in the ISA range (below 400h) to avoid conflicts with ISA device
- * probing. Addresses in the 300h-340h range should be especially avoided.
+/** Custom fixed I/O ports for BIOS controller access.
+ * Note that these should not be in the ISA range (below 400h) to avoid
+ * conflicts with ISA device probing. Addresses in the 300h-340h range should be
+ * especially avoided.
*/
-
-#define BUSLOGIC_BIOS_IO_PORT 0x330
+#define BUSLOGIC_BIOS_IO_PORT 0x430
/** State saved version. */
-#define BUSLOGIC_SAVED_STATE_MINOR_VERSION 2
+#define BUSLOGIC_SAVED_STATE_MINOR_VERSION 4
/** Saved state version before the suspend on error feature was implemented. */
#define BUSLOGIC_SAVED_STATE_MINOR_PRE_ERROR_HANDLING 1
+/** Saved state version before 24-bit mailbox support was implemented. */
+#define BUSLOGIC_SAVED_STATE_MINOR_PRE_24BIT_MBOX 2
+/** Saved state version before command buffer size was raised. */
+#define BUSLOGIC_SAVED_STATE_MINOR_PRE_CMDBUF_RESIZE 3
+
+/** Command buffer size in old saved states. */
+#define BUSLOGIC_COMMAND_SIZE_OLD 5
+
+/** The duration of software-initiated reset (in nano seconds).
+ * Not documented, set to 50 ms. */
+#define BUSLOGIC_RESET_DURATION_NS UINT64_C(50000000)
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
/**
* State of a device attached to the buslogic host adapter.
*
@@ -115,12 +131,12 @@ typedef struct BUSLOGICDEVICE
} BUSLOGICDEVICE, *PBUSLOGICDEVICE;
-/*
+/**
* Commands the BusLogic adapter supports.
*/
enum BUSLOGICCOMMAND
{
- BUSLOGICCOMMAND_TEST_COMMAND_COMPLETE_INTERRUPT = 0x00,
+ BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT = 0x00,
BUSLOGICCOMMAND_INITIALIZE_MAILBOX = 0x01,
BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND = 0x02,
BUSLOGICCOMMAND_EXECUTE_BIOS_COMMAND = 0x03,
@@ -180,35 +196,35 @@ typedef struct AutoSCSIRam
uint8_t cbInformation;
uint8_t aHostAdaptertype[6];
uint8_t uReserved1;
- bool fFloppyEnabled: 1;
- bool fFloppySecondary: 1;
- bool fLevelSensitiveInterrupt: 1;
- unsigned char uReserved2: 2;
- unsigned char uSystemRAMAreForBIOS: 3;
- unsigned char uDMAChannel: 7;
- bool fDMAAutoConfiguration: 1;
- unsigned char uIrqChannel: 7;
- bool fIrqAutoConfiguration: 1;
+ bool fFloppyEnabled : 1;
+ bool fFloppySecondary : 1;
+ bool fLevelSensitiveInterrupt : 1;
+ unsigned char uReserved2 : 2;
+ unsigned char uSystemRAMAreForBIOS : 3;
+ unsigned char uDMAChannel : 7;
+ bool fDMAAutoConfiguration : 1;
+ unsigned char uIrqChannel : 7;
+ bool fIrqAutoConfiguration : 1;
uint8_t uDMATransferRate;
uint8_t uSCSIId;
- bool fLowByteTerminated: 1;
- bool fParityCheckingEnabled: 1;
- bool fHighByteTerminated: 1;
- bool fNoisyCablingEnvironment: 1;
- bool fFastSynchronousNeogtiation: 1;
- bool fBusResetEnabled: 1;
- bool fReserved3: 1;
- bool fActiveNegotiationEnabled: 1;
+ bool fLowByteTerminated : 1;
+ bool fParityCheckingEnabled : 1;
+ bool fHighByteTerminated : 1;
+ bool fNoisyCablingEnvironment : 1;
+ bool fFastSynchronousNeogtiation : 1;
+ bool fBusResetEnabled : 1;
+ bool fReserved3 : 1;
+ bool fActiveNegotiationEnabled : 1;
uint8_t uBusOnDelay;
uint8_t uBusOffDelay;
- bool fHostAdapterBIOSEnabled: 1;
- bool fBIOSRedirectionOfInt19: 1;
- bool fExtendedTranslation: 1;
- bool fMapRemovableAsFixed: 1;
- bool fReserved4: 1;
- bool fBIOSSupportsMoreThan2Drives: 1;
- bool fBIOSInterruptMode: 1;
- bool fFlopticalSupport: 1;
+ bool fHostAdapterBIOSEnabled : 1;
+ bool fBIOSRedirectionOfInt19 : 1;
+ bool fExtendedTranslation : 1;
+ bool fMapRemovableAsFixed : 1;
+ bool fReserved4 : 1;
+ bool fBIOSSupportsMoreThan2Drives : 1;
+ bool fBIOSInterruptMode : 1;
+ bool fFlopticalSupport : 1;
uint16_t u16DeviceEnabledMask;
uint16_t u16WidePermittedMask;
uint16_t u16FastPermittedMask;
@@ -216,29 +232,29 @@ typedef struct AutoSCSIRam
uint16_t u16DisconnectPermittedMask;
uint16_t u16SendStartUnitCommandMask;
uint16_t u16IgnoreInBIOSScanMask;
- unsigned char uPCIInterruptPin: 2;
- unsigned char uHostAdapterIoPortAddress: 2;
- bool fStrictRoundRobinMode: 1;
- bool fVesaBusSpeedGreaterThan33MHz: 1;
- bool fVesaBurstWrite: 1;
- bool fVesaBurstRead: 1;
+ unsigned char uPCIInterruptPin : 2;
+ unsigned char uHostAdapterIoPortAddress : 2;
+ bool fStrictRoundRobinMode : 1;
+ bool fVesaBusSpeedGreaterThan33MHz : 1;
+ bool fVesaBurstWrite : 1;
+ bool fVesaBurstRead : 1;
uint16_t u16UltraPermittedMask;
uint32_t uReserved5;
uint8_t uReserved6;
uint8_t uAutoSCSIMaximumLUN;
- bool fReserved7: 1;
- bool fSCAMDominant: 1;
- bool fSCAMenabled: 1;
- bool fSCAMLevel2: 1;
- unsigned char uReserved8: 4;
- bool fInt13Extension: 1;
- bool fReserved9: 1;
- bool fCDROMBoot: 1;
- unsigned char uReserved10: 5;
- unsigned char uBootTargetId: 4;
- unsigned char uBootChannel: 4;
- bool fForceBusDeviceScanningOrder: 1;
- unsigned char uReserved11: 7;
+ bool fReserved7 : 1;
+ bool fSCAMDominant : 1;
+ bool fSCAMenabled : 1;
+ bool fSCAMLevel2 : 1;
+ unsigned char uReserved8 : 4;
+ bool fInt13Extension : 1;
+ bool fReserved9 : 1;
+ bool fCDROMBoot : 1;
+ unsigned char uReserved10 : 5;
+ unsigned char uBootTargetId : 4;
+ unsigned char uBootChannel : 4;
+ bool fForceBusDeviceScanningOrder : 1;
+ unsigned char uReserved11 : 7;
uint16_t u16NonTaggedToAlternateLunPermittedMask;
uint16_t u16RenegotiateSyncAfterCheckConditionMask;
uint8_t aReserved12[10];
@@ -248,15 +264,14 @@ typedef struct AutoSCSIRam
AssertCompileSize(AutoSCSIRam, 64);
#pragma pack()
-#pragma pack(1)
/**
* The local Ram.
*/
typedef union HostAdapterLocalRam
{
- /* Byte view. */
+ /** Byte view. */
uint8_t u8View[256];
- /* Structured view. */
+ /** Structured view. */
struct
{
/** Offset 0 - 63 is for BIOS. */
@@ -266,7 +281,33 @@ typedef union HostAdapterLocalRam
} structured;
} HostAdapterLocalRam, *PHostAdapterLocalRam;
AssertCompileSize(HostAdapterLocalRam, 256);
-#pragma pack()
+
+
+/** Ugly 24-bit big-endian addressing. */
+typedef struct
+{
+ uint8_t hi;
+ uint8_t mid;
+ uint8_t lo;
+} Addr24, Len24;
+AssertCompileSize(Addr24, 3);
+
+#define ADDR_TO_U32(x) (((x).hi << 16) | ((x).mid << 8) | (x).lo)
+#define LEN_TO_U32 ADDR_TO_U32
+#define U32_TO_ADDR(a, x) do {(a).hi = (x) >> 16; (a).mid = (x) >> 8; (a).lo = (x);} while(0)
+#define U32_TO_LEN U32_TO_ADDR
+
+/** @name Compatible ISA base I/O port addresses. Disabled if zero.
+ * @{ */
+#define NUM_ISA_BASES 8
+#define MAX_ISA_BASE (NUM_ISA_BASES - 1)
+#define ISA_BASE_DISABLED 6
+
+static uint16_t const g_aISABases[NUM_ISA_BASES] =
+{
+ 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0, 0
+};
+/** @} */
/** Pointer to a task state structure. */
typedef struct BUSLOGICTASKSTATE *PBUSLOGICTASKSTATE;
@@ -303,6 +344,8 @@ typedef struct BUSLOGIC
volatile uint8_t regInterrupt;
/** Geometry register - Readonly. */
volatile uint8_t regGeometry;
+ /** Pending (delayed) interrupt. */
+ uint8_t uPendingIntr;
/** Local RAM for the fetch hostadapter local RAM request.
* I don't know how big the buffer really is but the maximum
@@ -334,8 +377,17 @@ typedef struct BUSLOGIC
/** Flag whether IRQs are enabled. */
bool fIRQEnabled;
/** Flag whether the ISA I/O port range is disabled
- * to prevent the BIOs to access the device. */
- bool fISAEnabled;
+ * to prevent the BIOS to access the device. */
+ bool fISAEnabled; /**< @todo unused, to be removed */
+ /** Flag whether 24-bit mailboxes are in use (default is 32-bit). */
+ bool fMbxIs24Bit;
+ /** ISA I/O port base (encoded in FW-compatible format). */
+ uint8_t uISABaseCode;
+
+ /** ISA I/O port base (disabled if zero). */
+ RTIOPORT IOISABase;
+ /** Default ISA I/O port base in FW-compatible format. */
+ uint8_t uDefaultISABaseCode;
/** Number of mailboxes the guest set up. */
uint32_t cMailbox;
@@ -344,6 +396,8 @@ typedef struct BUSLOGIC
uint32_t Alignment0;
#endif
+ /** Time when HBA reset was last initiated. */ /**< @todo does this need to be saved? */
+ uint64_t u64ResetTime;
/** Physical base address of the outgoing mailboxes. */
RTGCPHYS GCPhysAddrMailboxOutgoingBase;
/** Current outgoing mailbox position. */
@@ -419,14 +473,14 @@ typedef struct BUSLOGIC
} BUSLOGIC, *PBUSLOGIC;
/** Register offsets in the I/O port space. */
-#define BUSLOGIC_REGISTER_CONTROL 0 /* Writeonly */
+#define BUSLOGIC_REGISTER_CONTROL 0 /**< Writeonly */
/** Fields for the control register. */
# define BUSLOGIC_REGISTER_CONTROL_SCSI_BUSRESET RT_BIT(4)
# define BUSLOGIC_REGISTER_CONTROL_INTERRUPT_RESET RT_BIT(5)
# define BUSLOGIC_REGISTER_CONTROL_SOFT_RESET RT_BIT(6)
# define BUSLOGIC_REGISTER_CONTROL_HARD_RESET RT_BIT(7)
-#define BUSLOGIC_REGISTER_STATUS 0 /* Readonly */
+#define BUSLOGIC_REGISTER_STATUS 0 /**< Readonly */
/** Fields for the status register. */
# define BUSLOGIC_REGISTER_STATUS_COMMAND_INVALID RT_BIT(0)
# define BUSLOGIC_REGISTER_STATUS_DATA_IN_REGISTER_READY RT_BIT(2)
@@ -436,12 +490,12 @@ typedef struct BUSLOGIC
# define BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_FAILURE RT_BIT(6)
# define BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE RT_BIT(7)
-#define BUSLOGIC_REGISTER_COMMAND 1 /* Writeonly */
-#define BUSLOGIC_REGISTER_DATAIN 1 /* Readonly */
-#define BUSLOGIC_REGISTER_INTERRUPT 2 /* Readonly */
+#define BUSLOGIC_REGISTER_COMMAND 1 /**< Writeonly */
+#define BUSLOGIC_REGISTER_DATAIN 1 /**< Readonly */
+#define BUSLOGIC_REGISTER_INTERRUPT 2 /**< Readonly */
/** Fields for the interrupt register. */
# define BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED RT_BIT(0)
-# define BUSLOGIC_REGISTER_INTERRUPT_OUTCOMING_MAILBOX_AVAILABLE RT_BIT(1)
+# define BUSLOGIC_REGISTER_INTERRUPT_OUTGOING_MAILBOX_AVAILABLE RT_BIT(1)
# define BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE RT_BIT(2)
# define BUSLOGIC_REGISTER_INTERRUPT_EXTERNAL_BUS_RESET RT_BIT(3)
# define BUSLOGIC_REGISTER_INTERRUPT_INTERRUPT_VALID RT_BIT(7)
@@ -449,69 +503,62 @@ typedef struct BUSLOGIC
#define BUSLOGIC_REGISTER_GEOMETRY 3 /* Readonly */
# define BUSLOGIC_REGISTER_GEOMETRY_EXTENTED_TRANSLATION_ENABLED RT_BIT(7)
-/* Structure for the INQUIRE_PCI_HOST_ADAPTER_INFORMATION reply. */
-#pragma pack(1)
+/** Structure for the INQUIRE_PCI_HOST_ADAPTER_INFORMATION reply. */
typedef struct ReplyInquirePCIHostAdapterInformation
{
uint8_t IsaIOPort;
uint8_t IRQ;
- unsigned char LowByteTerminated:1;
- unsigned char HighByteTerminated:1;
- unsigned char uReserved:2; /* Reserved. */
- unsigned char JP1:1; /* Whatever that means. */
- unsigned char JP2:1; /* Whatever that means. */
- unsigned char JP3:1; /* Whatever that means. */
+ unsigned char LowByteTerminated : 1;
+ unsigned char HighByteTerminated : 1;
+ unsigned char uReserved : 2; /* Reserved. */
+ unsigned char JP1 : 1; /* Whatever that means. */
+ unsigned char JP2 : 1; /* Whatever that means. */
+ unsigned char JP3 : 1; /* Whatever that means. */
/** Whether the provided info is valid. */
unsigned char InformationIsValid: 1;
uint8_t uReserved2; /* Reserved. */
} ReplyInquirePCIHostAdapterInformation, *PReplyInquirePCIHostAdapterInformation;
AssertCompileSize(ReplyInquirePCIHostAdapterInformation, 4);
-#pragma pack()
-/* Structure for the INQUIRE_CONFIGURATION reply. */
-#pragma pack(1)
+/** Structure for the INQUIRE_CONFIGURATION reply. */
typedef struct ReplyInquireConfiguration
{
- unsigned char uReserved1: 5;
- bool fDmaChannel5: 1;
- bool fDmaChannel6: 1;
- bool fDmaChannel7: 1;
- bool fIrqChannel9: 1;
- bool fIrqChannel10: 1;
- bool fIrqChannel11: 1;
- bool fIrqChannel12: 1;
- unsigned char uReserved2: 1;
- bool fIrqChannel14: 1;
- bool fIrqChannel15: 1;
- unsigned char uReserved3: 1;
- unsigned char uHostAdapterId: 4;
- unsigned char uReserved4: 4;
+ unsigned char uReserved1 : 5;
+ bool fDmaChannel5 : 1;
+ bool fDmaChannel6 : 1;
+ bool fDmaChannel7 : 1;
+ bool fIrqChannel9 : 1;
+ bool fIrqChannel10 : 1;
+ bool fIrqChannel11 : 1;
+ bool fIrqChannel12 : 1;
+ unsigned char uReserved2 : 1;
+ bool fIrqChannel14 : 1;
+ bool fIrqChannel15 : 1;
+ unsigned char uReserved3 : 1;
+ unsigned char uHostAdapterId : 4;
+ unsigned char uReserved4 : 4;
} ReplyInquireConfiguration, *PReplyInquireConfiguration;
AssertCompileSize(ReplyInquireConfiguration, 3);
-#pragma pack()
-/* Structure for the INQUIRE_SETUP_INFORMATION reply. */
-#pragma pack(1)
+/** Structure for the INQUIRE_SETUP_INFORMATION reply. */
typedef struct ReplyInquireSetupInformationSynchronousValue
{
- unsigned char uOffset: 4;
- unsigned char uTransferPeriod: 3;
- bool fSynchronous: 1;
+ unsigned char uOffset : 4;
+ unsigned char uTransferPeriod : 3;
+ bool fSynchronous : 1;
}ReplyInquireSetupInformationSynchronousValue, *PReplyInquireSetupInformationSynchronousValue;
AssertCompileSize(ReplyInquireSetupInformationSynchronousValue, 1);
-#pragma pack()
-#pragma pack(1)
typedef struct ReplyInquireSetupInformation
{
- bool fSynchronousInitiationEnabled: 1;
- bool fParityCheckingEnabled: 1;
- unsigned char uReserved1: 6;
+ bool fSynchronousInitiationEnabled : 1;
+ bool fParityCheckingEnabled : 1;
+ unsigned char uReserved1 : 6;
uint8_t uBusTransferRate;
uint8_t uPreemptTimeOnBus;
uint8_t uTimeOffBus;
uint8_t cMailbox;
- uint8_t MailboxAddress[3];
+ Addr24 MailboxAddress;
ReplyInquireSetupInformationSynchronousValue SynchronousValuesId0To7[8];
uint8_t uDisconnectPermittedId0To7;
uint8_t uSignature;
@@ -526,9 +573,8 @@ typedef struct ReplyInquireSetupInformation
uint8_t uWideTransfersActiveId8To15;
} ReplyInquireSetupInformation, *PReplyInquireSetupInformation;
AssertCompileSize(ReplyInquireSetupInformation, 34);
-#pragma pack()
-/* Structure for the INQUIRE_EXTENDED_SETUP_INFORMATION. */
+/** Structure for the INQUIRE_EXTENDED_SETUP_INFORMATION. */
#pragma pack(1)
typedef struct ReplyInquireExtendedSetupInformation
{
@@ -537,23 +583,23 @@ typedef struct ReplyInquireExtendedSetupInformation
uint16_t u16ScatterGatherLimit;
uint8_t cMailbox;
uint32_t uMailboxAddressBase;
- unsigned char uReserved1: 2;
- bool fFastEISA: 1;
- unsigned char uReserved2: 3;
- bool fLevelSensitiveInterrupt: 1;
- unsigned char uReserved3: 1;
+ unsigned char uReserved1 : 2;
+ bool fFastEISA : 1;
+ unsigned char uReserved2 : 3;
+ bool fLevelSensitiveInterrupt : 1;
+ unsigned char uReserved3 : 1;
unsigned char aFirmwareRevision[3];
- bool fHostWideSCSI: 1;
- bool fHostDifferentialSCSI: 1;
- bool fHostSupportsSCAM: 1;
- bool fHostUltraSCSI: 1;
- bool fHostSmartTermination: 1;
- unsigned char uReserved4: 3;
+ bool fHostWideSCSI : 1;
+ bool fHostDifferentialSCSI : 1;
+ bool fHostSupportsSCAM : 1;
+ bool fHostUltraSCSI : 1;
+ bool fHostSmartTermination : 1;
+ unsigned char uReserved4 : 3;
} ReplyInquireExtendedSetupInformation, *PReplyInquireExtendedSetupInformation;
AssertCompileSize(ReplyInquireExtendedSetupInformation, 14);
#pragma pack()
-/* Structure for the INITIALIZE EXTENDED MAILBOX request. */
+/** Structure for the INITIALIZE EXTENDED MAILBOX request. */
#pragma pack(1)
typedef struct RequestInitializeExtendedMailbox
{
@@ -565,7 +611,17 @@ typedef struct RequestInitializeExtendedMailbox
AssertCompileSize(RequestInitializeExtendedMailbox, 5);
#pragma pack()
-/*
+/** Structure for the INITIALIZE MAILBOX request. */
+typedef struct
+{
+ /** Number of mailboxes to set up. */
+ uint8_t cMailbox;
+ /** Physical address of the first mailbox. */
+ Addr24 aMailboxBaseAddr;
+} RequestInitMbx, *PRequestInitMbx;
+AssertCompileSize(RequestInitMbx, 4);
+
+/**
* Structure of a mailbox in guest memory.
* The incoming and outgoing mailbox have the same size
* but the incoming one has some more fields defined which
@@ -575,8 +631,7 @@ AssertCompileSize(RequestInitializeExtendedMailbox, 5);
* for incoming ones the completion status code for the task.
* We use one structure for both types.
*/
-#pragma pack(1)
-typedef struct Mailbox
+typedef struct Mailbox32
{
/** Physical address of the CCB structure in the guest memory. */
uint32_t u32PhysAddrCCB;
@@ -604,11 +659,20 @@ typedef struct Mailbox
uint8_t uCompletionCode;
} in;
} u;
-} Mailbox, *PMailbox;
-AssertCompileSize(Mailbox, 8);
-#pragma pack()
+} Mailbox32, *PMailbox32;
+AssertCompileSize(Mailbox32, 8);
-/*
+/** Old style 24-bit mailbox entry. */
+typedef struct Mailbox24
+{
+ /** Mailbox command (incoming) or state (outgoing). */
+ uint8_t uCmdState;
+ /** Physical address of the CCB structure in the guest memory. */
+ Addr24 aPhysAddrCCB;
+} Mailbox24, *PMailbox24;
+AssertCompileSize(Mailbox24, 4);
+
+/**
* Action codes for outgoing mailboxes.
*/
enum BUSLOGIC_MAILBOX_OUTGOING_ACTION
@@ -618,7 +682,7 @@ enum BUSLOGIC_MAILBOX_OUTGOING_ACTION
BUSLOGIC_MAILBOX_OUTGOING_ACTION_ABORT_COMMAND = 0x02
};
-/*
+/**
* Completion codes for incoming mailboxes.
*/
enum BUSLOGIC_MAILBOX_INCOMING_COMPLETION
@@ -631,7 +695,7 @@ enum BUSLOGIC_MAILBOX_INCOMING_COMPLETION
BUSLOGIC_MAILBOX_INCOMING_COMPLETION_INVALID_CCB = 0x05
};
-/*
+/**
* Host adapter status for incoming mailboxes.
*/
enum BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS
@@ -663,7 +727,7 @@ enum BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS
BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_PARITY_ERROR_DETECTED = 0x34
};
-/*
+/**
* Device status codes for incoming mailboxes.
*/
enum BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS
@@ -673,7 +737,7 @@ enum BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS
BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_DEVICE_BUSY = 0x08
};
-/*
+/**
* Opcode types for CCB.
*/
enum BUSLOGIC_CCB_OPCODE
@@ -686,7 +750,7 @@ enum BUSLOGIC_CCB_OPCODE
BUSLOGIC_CCB_OPCODE_BUS_DEVICE_RESET = 0x81
};
-/*
+/**
* Data transfer direction.
*/
enum BUSLOGIC_CCB_DIRECTION
@@ -697,22 +761,21 @@ enum BUSLOGIC_CCB_DIRECTION
BUSLOGIC_CCB_DIRECTION_NO_DATA = 0x03
};
-/*
+/**
* The command control block for a SCSI request.
*/
-#pragma pack(1)
-typedef struct CommandControlBlock
+typedef struct CCB32
{
/** Opcode. */
uint8_t uOpcode;
/** Reserved */
- unsigned char uReserved1: 3;
+ unsigned char uReserved1 : 3;
/** Data direction for the request. */
- unsigned char uDataDirection: 2;
+ unsigned char uDataDirection : 2;
/** Whether the request is tag queued. */
- bool fTagQueued: 1;
+ bool fTagQueued : 1;
/** Queue tag mode. */
- unsigned char uQueueTag: 2;
+ unsigned char uQueueTag : 2;
/** Length of the SCSI CDB. */
uint8_t cbCDB;
/** Sense data length. */
@@ -729,34 +792,153 @@ typedef struct CommandControlBlock
uint8_t uHostAdapterStatus;
/** Device adapter status. */
uint8_t uDeviceStatus;
- /** The device the request is send to. */
+ /** The device the request is sent to. */
uint8_t uTargetId;
/**The LUN in the device. */
- unsigned char uLogicalUnit: 5;
+ unsigned char uLogicalUnit : 5;
/** Legacy tag. */
- bool fLegacyTagEnable: 1;
+ bool fLegacyTagEnable : 1;
/** Legacy queue tag. */
- unsigned char uLegacyQueueTag: 2;
- /** The SCSI CDB. */
- uint8_t aCDB[12]; /* A CDB can be 12 bytes long. */
+ unsigned char uLegacyQueueTag : 2;
+ /** The SCSI CDB. (A CDB can be 12 bytes long.) */
+ uint8_t abCDB[12];
/** Reserved. */
uint8_t uReserved3[6];
/** Sense data pointer. */
uint32_t u32PhysAddrSenseData;
-} CommandControlBlock, *PCommandControlBlock;
-AssertCompileSize(CommandControlBlock, 40);
-#pragma pack()
+} CCB32, *PCCB32;
+AssertCompileSize(CCB32, 40);
-#pragma pack(1)
-typedef struct ScatterGatherEntry
+
+/**
+ * The 24-bit command control block.
+ */
+typedef struct CCB24
+{
+ /** Opcode. */
+ uint8_t uOpcode;
+ /** The LUN in the device. */
+ unsigned char uLogicalUnit : 3;
+ /** Data direction for the request. */
+ unsigned char uDataDirection : 2;
+ /** The target device ID. */
+ unsigned char uTargetId : 3;
+ /** Length of the SCSI CDB. */
+ uint8_t cbCDB;
+ /** Sense data length. */
+ uint8_t cbSenseData;
+ /** Data length. */
+ Len24 acbData;
+ /** Data pointer.
+ * This points to the data region or a scatter gather list based on the opc
+ */
+ Addr24 aPhysAddrData;
+ /** Pointer to next CCB for linked commands. */
+ Addr24 aPhysAddrLink;
+ /** Command linking identifier. */
+ uint8_t uLinkId;
+ /** Host adapter status. */
+ uint8_t uHostAdapterStatus;
+ /** Device adapter status. */
+ uint8_t uDeviceStatus;
+ /** Two unused bytes. */
+ uint8_t aReserved[2];
+ /** The SCSI CDB. (A CDB can be 12 bytes long.) */
+ uint8_t abCDB[12];
+} CCB24, *PCCB24;
+AssertCompileSize(CCB24, 30);
+
+/**
+ * The common 24-bit/32-bit command control block. The 32-bit CCB is laid out
+ * such that many fields are in the same location as in the older 24-bit CCB.
+ */
+typedef struct CCBC
+{
+ /** Opcode. */
+ uint8_t uOpcode;
+ /** The LUN in the device. */
+ unsigned char uPad1 : 3;
+ /** Data direction for the request. */
+ unsigned char uDataDirection : 2;
+ /** The target device ID. */
+ unsigned char uPad2 : 3;
+ /** Length of the SCSI CDB. */
+ uint8_t cbCDB;
+ /** Sense data length. */
+ uint8_t cbSenseData;
+ uint8_t aPad1[10];
+ /** Host adapter status. */
+ uint8_t uHostAdapterStatus;
+ /** Device adapter status. */
+ uint8_t uDeviceStatus;
+ uint8_t aPad2[2];
+ /** The SCSI CDB (up to 12 bytes). */
+ uint8_t abCDB[12];
+} CCBC, *PCCBC;
+AssertCompileSize(CCB24, 30);
+
+/* Make sure that the 24-bit/32-bit/common CCB offsets match. */
+AssertCompileMemberOffset(CCBC, cbCDB, 2);
+AssertCompileMemberOffset(CCB24, cbCDB, 2);
+AssertCompileMemberOffset(CCB32, cbCDB, 2);
+AssertCompileMemberOffset(CCBC, uHostAdapterStatus, 14);
+AssertCompileMemberOffset(CCB24, uHostAdapterStatus, 14);
+AssertCompileMemberOffset(CCB32, uHostAdapterStatus, 14);
+AssertCompileMemberOffset(CCBC, abCDB, 18);
+AssertCompileMemberOffset(CCB24, abCDB, 18);
+AssertCompileMemberOffset(CCB32, abCDB, 18);
+
+/** A union of all CCB types (24-bit/32-bit/common). */
+typedef union CCBU
+{
+ CCB32 n; /**< New 32-bit CCB. */
+ CCB24 o; /**< Old 24-bit CCB. */
+ CCBC c; /**< Common CCB subset. */
+} CCBU, *PCCBU;
+
+/** 32-bit scatter-gather list entry. */
+typedef struct SGE32
{
uint32_t cbSegment;
uint32_t u32PhysAddrSegmentBase;
-} ScatterGatherEntry, *PScatterGatherEntry;
-AssertCompileSize(ScatterGatherEntry, 8);
-#pragma pack()
+} SGE32, *PSGE32;
+AssertCompileSize(SGE32, 8);
-/*
+/** 24-bit scatter-gather list entry. */
+typedef struct SGE24
+{
+ Len24 acbSegment;
+ Addr24 aPhysAddrSegmentBase;
+} SGE24, *PSGE24;
+AssertCompileSize(SGE24, 6);
+
+/**
+ * The structure for the "Execute SCSI Command" command.
+ */
+typedef struct ESCMD
+{
+ /** Data length. */
+ uint32_t cbData;
+ /** Data pointer. */
+ uint32_t u32PhysAddrData;
+ /** The device the request is sent to. */
+ uint8_t uTargetId;
+ /** The LUN in the device. */
+ uint8_t uLogicalUnit;
+ /** Reserved */
+ unsigned char uReserved1 : 3;
+ /** Data direction for the request. */
+ unsigned char uDataDirection : 2;
+ /** Reserved */
+ unsigned char uReserved2 : 3;
+ /** Length of the SCSI CDB. */
+ uint8_t cbCDB;
+ /** The SCSI CDB. (A CDB can be 12 bytes long.) */
+ uint8_t abCDB[12];
+} ESCMD, *PESCMD;
+AssertCompileSize(ESCMD, 24);
+
+/**
* Task state for a CCB request.
*/
typedef struct BUSLOGICTASKSTATE
@@ -766,9 +948,9 @@ typedef struct BUSLOGICTASKSTATE
/** Device this task is assigned to. */
R3PTRTYPE(PBUSLOGICDEVICE) pTargetDeviceR3;
/** The command control block from the guest. */
- CommandControlBlock CommandControlBlockGuest;
+ CCBU CommandControlBlockGuest;
/** Mailbox read from guest memory. */
- Mailbox MailboxGuest;
+ Mailbox32 MailboxGuest;
/** The SCSI request we pass to the underlying SCSI engine. */
PDMSCSIREQUEST PDMScsiRequest;
/** Data buffer segment */
@@ -777,6 +959,10 @@ typedef struct BUSLOGICTASKSTATE
uint8_t *pbSenseBuffer;
/** Flag whether this is a request from the BIOS. */
bool fBIOS;
+ /** 24-bit request flag (default is 32-bit). */
+ bool fIs24Bit;
+ /** S/G entry size (depends on the above flag). */
+ uint8_t cbSGEntry;
} BUSLOGICTASKSTATE;
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
@@ -787,18 +973,11 @@ typedef struct BUSLOGICTASKSTATE
#define PDMIBASE_2_PBUSLOGIC(pInterface) ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, IBase)) )
#define PDMILEDPORTS_2_PBUSLOGIC(pInterface) ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, ILeds)) )
-/**
- * Deasserts the interrupt line of the BusLogic adapter.
- *
- * @returns nothing
- * @param pBuslogic Pointer to the BusLogic device instance.
- */
-static void buslogicClearInterrupt(PBUSLOGIC pBusLogic)
-{
- LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
- pBusLogic->regInterrupt = 0;
- PDMDevHlpPCISetIrq(pBusLogic->CTX_SUFF(pDevIns), 0, 0);
-}
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static int buslogicR3RegisterISARange(PBUSLOGIC pBusLogic, uint8_t uBaseCode);
+
/**
* Assert IRQ line of the BusLogic adapter.
@@ -806,31 +985,61 @@ static void buslogicClearInterrupt(PBUSLOGIC pBusLogic)
* @returns nothing.
* @param pBusLogic Pointer to the BusLogic device instance.
* @param fSuppressIrq Flag to suppress IRQ generation regardless of fIRQEnabled
+ * @param uFlag Type of interrupt being generated.
*/
-static void buslogicSetInterrupt(PBUSLOGIC pBusLogic, bool fSuppressIrq)
+static void buslogicSetInterrupt(PBUSLOGIC pBusLogic, bool fSuppressIrq, uint8_t uIrqType)
{
LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
+
+ /* The CMDC interrupt has priority over IMBL and MBOR. */
+ if (uIrqType & (BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED | BUSLOGIC_REGISTER_INTERRUPT_OUTGOING_MAILBOX_AVAILABLE))
+ {
+ if (!(pBusLogic->regInterrupt & BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE))
+ pBusLogic->regInterrupt |= uIrqType; /* Report now. */
+ else
+ pBusLogic->uPendingIntr |= uIrqType; /* Report later. */
+ }
+ else if (uIrqType & BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE)
+ {
+ Assert(!pBusLogic->regInterrupt);
+ pBusLogic->regInterrupt |= uIrqType;
+ }
+ else
+ AssertMsgFailed(("Invalid interrupt state!\n"));
+
pBusLogic->regInterrupt |= BUSLOGIC_REGISTER_INTERRUPT_INTERRUPT_VALID;
if (pBusLogic->fIRQEnabled && !fSuppressIrq)
PDMDevHlpPCISetIrq(pBusLogic->CTX_SUFF(pDevIns), 0, 1);
}
-#if defined(IN_RING3)
-
/**
- * Advances the mailbox pointer to the next slot.
+ * Deasserts the interrupt line of the BusLogic adapter.
+ *
+ * @returns nothing
+ * @param pBuslogic Pointer to the BusLogic device instance.
*/
-DECLINLINE(void) buslogicOutgoingMailboxAdvance(PBUSLOGIC pBusLogic)
+static void buslogicClearInterrupt(PBUSLOGIC pBusLogic)
{
- pBusLogic->uMailboxOutgoingPositionCurrent = (pBusLogic->uMailboxOutgoingPositionCurrent + 1) % pBusLogic->cMailbox;
+ LogFlowFunc(("pBusLogic=%#p, clearing %#02x (pending %#02x)\n",
+ pBusLogic, pBusLogic->regInterrupt, pBusLogic->uPendingIntr));
+ pBusLogic->regInterrupt = 0;
+ PDMDevHlpPCISetIrq(pBusLogic->CTX_SUFF(pDevIns), 0, 0);
+ /* If there's another pending interrupt, report it now. */
+ if (pBusLogic->uPendingIntr)
+ {
+ buslogicSetInterrupt(pBusLogic, false, pBusLogic->uPendingIntr);
+ pBusLogic->uPendingIntr = 0;
+ }
}
+#if defined(IN_RING3)
+
/**
- * Returns the physical address of the next outgoing mailbox to process.
+ * Advances the mailbox pointer to the next slot.
*/
-DECLINLINE(RTGCPHYS) buslogicOutgoingMailboxGetGCPhys(PBUSLOGIC pBusLogic)
+DECLINLINE(void) buslogicR3OutgoingMailboxAdvance(PBUSLOGIC pBusLogic)
{
- return pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox));
+ pBusLogic->uMailboxOutgoingPositionCurrent = (pBusLogic->uMailboxOutgoingPositionCurrent + 1) % pBusLogic->cMailbox;
}
/**
@@ -839,7 +1048,7 @@ DECLINLINE(RTGCPHYS) buslogicOutgoingMailboxGetGCPhys(PBUSLOGIC pBusLogic)
* @returns nothing.
* @param pBusLogic.
*/
-static void buslogicInitializeLocalRam(PBUSLOGIC pBusLogic)
+static void buslogicR3InitializeLocalRam(PBUSLOGIC pBusLogic)
{
/*
* These values are mostly from what I think is right
@@ -859,7 +1068,7 @@ static void buslogicInitializeLocalRam(PBUSLOGIC pBusLogic)
pBusLogic->LocalRam.structured.autoSCSIData.u16DisconnectPermittedMask = ~0;
pBusLogic->LocalRam.structured.autoSCSIData.fStrictRoundRobinMode = pBusLogic->fStrictRoundRobinMode;
pBusLogic->LocalRam.structured.autoSCSIData.u16UltraPermittedMask = ~0;
- /* @todo calculate checksum? */
+ /** @todo calculate checksum? */
}
/**
@@ -867,29 +1076,40 @@ static void buslogicInitializeLocalRam(PBUSLOGIC pBusLogic)
*
* @returns VBox status code.
* @param pBusLogic Pointer to the BusLogic device instance.
+ * @param fResetIO Flag determining whether ISA I/O should be reset.
*/
-static int buslogicHwReset(PBUSLOGIC pBusLogic)
+static int buslogicR3HwReset(PBUSLOGIC pBusLogic, bool fResetIO)
{
LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
- /* Reset registers to default value. */
+ /* Reset registers to default values. */
pBusLogic->regStatus = BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY | BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED;
- pBusLogic->regInterrupt = 0;
pBusLogic->regGeometry = BUSLOGIC_REGISTER_GEOMETRY_EXTENTED_TRANSLATION_ENABLED;
pBusLogic->uOperationCode = 0xff; /* No command executing. */
pBusLogic->iParameter = 0;
pBusLogic->cbCommandParametersLeft = 0;
pBusLogic->fIRQEnabled = true;
- pBusLogic->fISAEnabled = true;
+ pBusLogic->fStrictRoundRobinMode = false;
+ pBusLogic->fExtendedLunCCBFormat = false;
pBusLogic->uMailboxOutgoingPositionCurrent = 0;
pBusLogic->uMailboxIncomingPositionCurrent = 0;
- buslogicInitializeLocalRam(pBusLogic);
+ /* Clear any active/pending interrupts. */
+ pBusLogic->uPendingIntr = 0;
+ buslogicClearInterrupt(pBusLogic);
+
+ /* Guest-initiated HBA reset does not affect ISA port I/O. */
+ if (fResetIO)
+ {
+ buslogicR3RegisterISARange(pBusLogic, pBusLogic->uDefaultISABaseCode);
+ }
+ buslogicR3InitializeLocalRam(pBusLogic);
vboxscsiInitialize(&pBusLogic->VBoxSCSI);
return VINF_SUCCESS;
}
-#endif
+
+#endif /* IN_RING3 */
/**
* Resets the command state machine for the next command and notifies the guest.
@@ -911,9 +1131,7 @@ static void buslogicCommandComplete(PBUSLOGIC pBusLogic, bool fSuppressIrq)
{
/* Notify that the command is complete. */
pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_DATA_IN_REGISTER_READY;
- pBusLogic->regInterrupt |= BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE;
-
- buslogicSetInterrupt(pBusLogic, fSuppressIrq);
+ buslogicSetInterrupt(pBusLogic, fSuppressIrq, BUSLOGIC_REGISTER_INTERRUPT_COMMAND_COMPLETE);
}
pBusLogic->uOperationCode = 0xff;
@@ -921,20 +1139,29 @@ static void buslogicCommandComplete(PBUSLOGIC pBusLogic, bool fSuppressIrq)
}
#if defined(IN_RING3)
+
/**
* Initiates a hard reset which was issued from the guest.
*
* @returns nothing
* @param pBusLogic Pointer to the BusLogic device instance.
+ * @param fHardReset Flag initiating a hard (vs. soft) reset.
*/
-static void buslogicIntiateHardReset(PBUSLOGIC pBusLogic)
+static void buslogicR3InitiateReset(PBUSLOGIC pBusLogic, bool fHardReset)
{
- LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
+ LogFlowFunc(("pBusLogic=%#p fHardReset=%d\n", pBusLogic, fHardReset));
- buslogicHwReset(pBusLogic);
+ buslogicR3HwReset(pBusLogic, false);
- /* We set the diagnostic active in the status register. */
- pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE;
+ if (fHardReset)
+ {
+ /* Set the diagnostic active bit in the status register and clear the ready state. */
+ pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE;
+ pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY;
+
+ /* Remember when the guest initiated a reset (after we're done resetting). */
+ pBusLogic->u64ResetTime = PDMDevHlpTMTimeVirtGetNano(pBusLogic->CTX_SUFF(pDevIns));
+ }
}
/**
@@ -947,9 +1174,9 @@ static void buslogicIntiateHardReset(PBUSLOGIC pBusLogic)
* @param uDeviceStatus The target device status to set.
* @param uMailboxCompletionCode Completion status code to set in the mailbox.
*/
-static void buslogicSendIncomingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState,
- uint8_t uHostAdapterStatus, uint8_t uDeviceStatus,
- uint8_t uMailboxCompletionCode)
+static void buslogicR3SendIncomingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState,
+ uint8_t uHostAdapterStatus, uint8_t uDeviceStatus,
+ uint8_t uMailboxCompletionCode)
{
pTaskState->MailboxGuest.u.in.uHostAdapterStatus = uHostAdapterStatus;
pTaskState->MailboxGuest.u.in.uTargetDeviceStatus = uDeviceStatus;
@@ -957,42 +1184,65 @@ static void buslogicSendIncomingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE
int rc = PDMCritSectEnter(&pBusLogic->CritSectIntr, VINF_SUCCESS);
AssertRC(rc);
- RTGCPHYS GCPhysAddrMailboxIncoming = pBusLogic->GCPhysAddrMailboxIncomingBase + (pBusLogic->uMailboxIncomingPositionCurrent * sizeof(Mailbox));
- RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB;
- LogFlowFunc(("Completing CCB %RGp\n", GCPhysAddrCCB));
+ RTGCPHYS GCPhysAddrMailboxIncoming = pBusLogic->GCPhysAddrMailboxIncomingBase
+ + ( pBusLogic->uMailboxIncomingPositionCurrent
+ * (pTaskState->fIs24Bit ? sizeof(Mailbox24) : sizeof(Mailbox32)) );
- /* Update CCB. */
- pTaskState->CommandControlBlockGuest.uHostAdapterStatus = uHostAdapterStatus;
- pTaskState->CommandControlBlockGuest.uDeviceStatus = uDeviceStatus;
- /* @todo: this is wrong - writing too much! */
- PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB, &pTaskState->CommandControlBlockGuest, sizeof(CommandControlBlock));
+ if (uMailboxCompletionCode != BUSLOGIC_MAILBOX_INCOMING_COMPLETION_ABORTED_NOT_FOUND)
+ {
+ RTGCPHYS GCPhysAddrCCB = pTaskState->MailboxGuest.u32PhysAddrCCB;
+ LogFlowFunc(("Completing CCB %RGp hstat=%u, dstat=%u, outgoing mailbox at %RGp\n", GCPhysAddrCCB,
+ uHostAdapterStatus, uDeviceStatus, GCPhysAddrMailboxIncoming));
+
+ /* Update CCB. */
+ pTaskState->CommandControlBlockGuest.c.uHostAdapterStatus = uHostAdapterStatus;
+ pTaskState->CommandControlBlockGuest.c.uDeviceStatus = uDeviceStatus;
+ /* Rewrite CCB up to the CDB; perhaps more than necessary. */
+ PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB,
+ &pTaskState->CommandControlBlockGuest, RT_OFFSETOF(CCBC, abCDB));
+ }
-#ifdef RT_STRICT
- Mailbox Tmp;
- PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &Tmp, sizeof(Mailbox));
- Assert(Tmp.u.in.uCompletionCode == BUSLOGIC_MAILBOX_INCOMING_COMPLETION_FREE);
-#endif
+# ifdef RT_STRICT
+ uint8_t uCode;
+ unsigned uCodeOffs = pTaskState->fIs24Bit ? RT_OFFSETOF(Mailbox24, uCmdState) : RT_OFFSETOF(Mailbox32, u.out.uActionCode);
+ PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming + uCodeOffs, &uCode, sizeof(uCode));
+ Assert(uCode == BUSLOGIC_MAILBOX_INCOMING_COMPLETION_FREE);
+# endif
/* Update mailbox. */
- PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &pTaskState->MailboxGuest, sizeof(Mailbox));
+ if (pTaskState->fIs24Bit)
+ {
+ Mailbox24 Mbx24;
+
+ Mbx24.uCmdState = pTaskState->MailboxGuest.u.in.uCompletionCode;
+ U32_TO_ADDR(Mbx24.aPhysAddrCCB, pTaskState->MailboxGuest.u32PhysAddrCCB);
+ Log(("24-bit mailbox: completion code=%u, CCB at %RGp\n", Mbx24.uCmdState, (RTGCPHYS)ADDR_TO_U32(Mbx24.aPhysAddrCCB)));
+ PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming, &Mbx24, sizeof(Mailbox24));
+ }
+ else
+ {
+ Log(("32-bit mailbox: completion code=%u, CCB at %RGp\n", pTaskState->MailboxGuest.u.in.uCompletionCode, (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB));
+ PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxIncoming,
+ &pTaskState->MailboxGuest, sizeof(Mailbox32));
+ }
/* Advance to next mailbox position. */
pBusLogic->uMailboxIncomingPositionCurrent++;
if (pBusLogic->uMailboxIncomingPositionCurrent >= pBusLogic->cMailbox)
pBusLogic->uMailboxIncomingPositionCurrent = 0;
-#ifdef LOG_ENABLED
+# ifdef LOG_ENABLED
ASMAtomicIncU32(&pBusLogic->cInMailboxesReady);
-#endif
+# endif
- pBusLogic->regInterrupt |= BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED;
- buslogicSetInterrupt(pBusLogic, false);
+ buslogicSetInterrupt(pBusLogic, false, BUSLOGIC_REGISTER_INTERRUPT_INCOMING_MAILBOX_LOADED);
PDMCritSectLeave(&pBusLogic->CritSectIntr);
}
-#if defined(DEBUG)
+# ifdef LOG_ENABLED
+
/**
* Dumps the content of a mailbox for debugging purposes.
*
@@ -1001,7 +1251,7 @@ static void buslogicSendIncomingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE
* @param fOutgoing true if dumping the outgoing state.
* false if dumping the incoming state.
*/
-static void buslogicDumpMailboxInfo(PMailbox pMailbox, bool fOutgoing)
+static void buslogicR3DumpMailboxInfo(PMailbox32 pMailbox, bool fOutgoing)
{
Log(("%s: Dump for %s mailbox:\n", __FUNCTION__, fOutgoing ? "outgoing" : "incoming"));
Log(("%s: u32PhysAddrCCB=%#x\n", __FUNCTION__, pMailbox->u32PhysAddrCCB));
@@ -1021,31 +1271,72 @@ static void buslogicDumpMailboxInfo(PMailbox pMailbox, bool fOutgoing)
* Dumps the content of a command control block for debugging purposes.
*
* @returns nothing.
- * @param pCCB Pointer to the command control block to dump.
- */
-static void buslogicDumpCCBInfo(PCommandControlBlock pCCB)
-{
- Log(("%s: Dump for Command Control Block:\n", __FUNCTION__));
- Log(("%s: uOpCode=%#x\n", __FUNCTION__, pCCB->uOpcode));
- Log(("%s: uDataDirection=%u\n", __FUNCTION__, pCCB->uDataDirection));
- Log(("%s: fTagQueued=%d\n", __FUNCTION__, pCCB->fTagQueued));
- Log(("%s: uQueueTag=%u\n", __FUNCTION__, pCCB->uQueueTag));
- Log(("%s: cbCDB=%u\n", __FUNCTION__, pCCB->cbCDB));
- Log(("%s: cbSenseData=%u\n", __FUNCTION__, pCCB->cbSenseData));
- Log(("%s: cbData=%u\n", __FUNCTION__, pCCB->cbData));
- Log(("%s: u32PhysAddrData=%#x\n", __FUNCTION__, pCCB->u32PhysAddrData));
- Log(("%s: uHostAdapterStatus=%u\n", __FUNCTION__, pCCB->uHostAdapterStatus));
- Log(("%s: uDeviceStatus=%u\n", __FUNCTION__, pCCB->uDeviceStatus));
- Log(("%s: uTargetId=%u\n", __FUNCTION__, pCCB->uTargetId));
- Log(("%s: uLogicalUnit=%u\n", __FUNCTION__, pCCB->uLogicalUnit));
- Log(("%s: fLegacyTagEnable=%u\n", __FUNCTION__, pCCB->fLegacyTagEnable));
- Log(("%s: uLegacyQueueTag=%u\n", __FUNCTION__, pCCB->uLegacyQueueTag));
- Log(("%s: uCDB[0]=%#x\n", __FUNCTION__, pCCB->aCDB[0]));
- for (int i = 1; i < pCCB->cbCDB; i++)
- Log(("%s: uCDB[%d]=%u\n", __FUNCTION__, i, pCCB->aCDB[i]));
- Log(("%s: u32PhysAddrSenseData=%#x\n", __FUNCTION__, pCCB->u32PhysAddrSenseData));
+ * @param pCCB Pointer to the command control block to dump.
+ * @param fIs24BitCCB Flag to determine CCB format.
+ */
+static void buslogicR3DumpCCBInfo(PCCBU pCCB, bool fIs24BitCCB)
+{
+ Log(("%s: Dump for %s Command Control Block:\n", __FUNCTION__, fIs24BitCCB ? "24-bit" : "32-bit"));
+ Log(("%s: uOpCode=%#x\n", __FUNCTION__, pCCB->c.uOpcode));
+ Log(("%s: uDataDirection=%u\n", __FUNCTION__, pCCB->c.uDataDirection));
+ Log(("%s: cbCDB=%u\n", __FUNCTION__, pCCB->c.cbCDB));
+ Log(("%s: cbSenseData=%u\n", __FUNCTION__, pCCB->c.cbSenseData));
+ Log(("%s: uHostAdapterStatus=%u\n", __FUNCTION__, pCCB->c.uHostAdapterStatus));
+ Log(("%s: uDeviceStatus=%u\n", __FUNCTION__, pCCB->c.uDeviceStatus));
+ if (fIs24BitCCB)
+ {
+ Log(("%s: cbData=%u\n", __FUNCTION__, LEN_TO_U32(pCCB->o.acbData)));
+ Log(("%s: PhysAddrData=%#x\n", __FUNCTION__, ADDR_TO_U32(pCCB->o.aPhysAddrData)));
+ Log(("%s: uTargetId=%u\n", __FUNCTION__, pCCB->o.uTargetId));
+ Log(("%s: uLogicalUnit=%u\n", __FUNCTION__, pCCB->o.uLogicalUnit));
+ }
+ else
+ {
+ Log(("%s: cbData=%u\n", __FUNCTION__, pCCB->n.cbData));
+ Log(("%s: PhysAddrData=%#x\n", __FUNCTION__, pCCB->n.u32PhysAddrData));
+ Log(("%s: uTargetId=%u\n", __FUNCTION__, pCCB->n.uTargetId));
+ Log(("%s: uLogicalUnit=%u\n", __FUNCTION__, pCCB->n.uLogicalUnit));
+ Log(("%s: fTagQueued=%d\n", __FUNCTION__, pCCB->n.fTagQueued));
+ Log(("%s: uQueueTag=%u\n", __FUNCTION__, pCCB->n.uQueueTag));
+ Log(("%s: fLegacyTagEnable=%u\n", __FUNCTION__, pCCB->n.fLegacyTagEnable));
+ Log(("%s: uLegacyQueueTag=%u\n", __FUNCTION__, pCCB->n.uLegacyQueueTag));
+ Log(("%s: PhysAddrSenseData=%#x\n", __FUNCTION__, pCCB->n.u32PhysAddrSenseData));
+ }
+ Log(("%s: uCDB[0]=%#x\n", __FUNCTION__, pCCB->c.abCDB[0]));
+ for (int i = 1; i < pCCB->c.cbCDB; i++)
+ Log(("%s: uCDB[%d]=%u\n", __FUNCTION__, i, pCCB->c.abCDB[i]));
+}
+
+# endif /* LOG_ENABLED */
+
+/**
+ * Allocate data buffer.
+ *
+ * @param pTaskState Pointer to the task state.
+ * @param GCSGList Guest physical address of S/G list.
+ * @param cEntries Number of list entries to read.
+ * @param pSGEList Pointer to 32-bit S/G list storage.
+ */
+static void buslogicR3ReadSGEntries(PBUSLOGICTASKSTATE pTaskState, RTGCPHYS GCSGList, uint32_t cEntries, SGE32 *pSGEList)
+{
+ PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
+ SGE24 aSGE24[32];
+ Assert(cEntries <= RT_ELEMENTS(aSGE24));
+
+ /* Read the S/G entries. Convert 24-bit entries to 32-bit format. */
+ if (pTaskState->fIs24Bit)
+ {
+ Log2(("Converting %u 24-bit S/G entries to 32-bit\n", cEntries));
+ PDMDevHlpPhysRead(pDevIns, GCSGList, &aSGE24, cEntries * sizeof(SGE24));
+ for (uint32_t i = 0; i < cEntries; ++i)
+ {
+ pSGEList[i].cbSegment = LEN_TO_U32(aSGE24[i].acbSegment);
+ pSGEList[i].u32PhysAddrSegmentBase = ADDR_TO_U32(aSGE24[i].aPhysAddrSegmentBase);
+ }
+ }
+ else
+ PDMDevHlpPhysRead(pDevIns, GCSGList, pSGEList, cEntries * sizeof(SGE32));
}
-#endif
/**
* Allocate data buffer.
@@ -1053,29 +1344,42 @@ static void buslogicDumpCCBInfo(PCommandControlBlock pCCB)
* @returns VBox status code.
* @param pTaskState Pointer to the task state.
*/
-static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
+static int buslogicR3DataBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
{
PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
+ uint32_t cbDataCCB;
+ uint32_t u32PhysAddrCCB;
+
+ /* Extract the data length and physical address from the CCB. */
+ if (pTaskState->fIs24Bit)
+ {
+ u32PhysAddrCCB = ADDR_TO_U32(pTaskState->CommandControlBlockGuest.o.aPhysAddrData);
+ cbDataCCB = LEN_TO_U32(pTaskState->CommandControlBlockGuest.o.acbData);
+ }
+ else
+ {
+ u32PhysAddrCCB = pTaskState->CommandControlBlockGuest.n.u32PhysAddrData;
+ cbDataCCB = pTaskState->CommandControlBlockGuest.n.cbData;
+ }
- if ( (pTaskState->CommandControlBlockGuest.uDataDirection != BUSLOGIC_CCB_DIRECTION_NO_DATA)
- && (pTaskState->CommandControlBlockGuest.cbData > 0))
+ if ( (pTaskState->CommandControlBlockGuest.c.uDataDirection != BUSLOGIC_CCB_DIRECTION_NO_DATA)
+ && cbDataCCB)
{
+ /** @todo Check following assumption and what residual means. */
/*
- * @todo: Check following assumption and what residual means.
- *
* The BusLogic adapter can handle two different data buffer formats.
* The first one is that the data pointer entry in the CCB points to
* the buffer directly. In second mode the data pointer points to a
* scatter gather list which describes the buffer.
*/
- if ( (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
- || (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
+ if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
+ || (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
{
uint32_t cScatterGatherGCRead;
uint32_t iScatterGatherEntry;
- ScatterGatherEntry aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */
- uint32_t cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry);
- RTGCPHYS GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
+ SGE32 aScatterGatherReadGC[32]; /* A buffer for scatter gather list entries read from guest memory. */
+ uint32_t cScatterGatherGCLeft = cbDataCCB / pTaskState->cbSGEntry;
+ RTGCPHYS GCPhysAddrScatterGatherCurrent = u32PhysAddrCCB;
size_t cbDataToTransfer = 0;
/* Count number of bytes to transfer. */
@@ -1086,9 +1390,7 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
: RT_ELEMENTS(aScatterGatherReadGC);
cScatterGatherGCLeft -= cScatterGatherGCRead;
- /* Read the SG entries. */
- PDMDevHlpPhysRead(pDevIns, GCPhysAddrScatterGatherCurrent, &aScatterGatherReadGC[0],
- cScatterGatherGCRead * sizeof(ScatterGatherEntry));
+ buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC);
for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++)
{
@@ -1105,7 +1407,7 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
}
/* Set address to the next entries to read. */
- GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * sizeof(ScatterGatherEntry);
+ GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * pTaskState->cbSGEntry;
} while (cScatterGatherGCLeft > 0);
Log(("%s: cbDataToTransfer=%d\n", __FUNCTION__, cbDataToTransfer));
@@ -1117,10 +1419,11 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
return VERR_NO_MEMORY;
/* Copy the data if needed */
- if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT)
+ if ( (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT)
+ || (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN))
{
- cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry);
- GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
+ cScatterGatherGCLeft = cbDataCCB / pTaskState->cbSGEntry;
+ GCPhysAddrScatterGatherCurrent = u32PhysAddrCCB;
uint8_t *pbData = (uint8_t *)pTaskState->DataSeg.pvSeg;
do
@@ -1130,9 +1433,7 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
: RT_ELEMENTS(aScatterGatherReadGC);
cScatterGatherGCLeft -= cScatterGatherGCRead;
- /* Read the SG entries. */
- PDMDevHlpPhysRead(pDevIns, GCPhysAddrScatterGatherCurrent, &aScatterGatherReadGC[0],
- cScatterGatherGCRead * sizeof(ScatterGatherEntry));
+ buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC);
for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++)
{
@@ -1150,27 +1451,27 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
}
/* Set address to the next entries to read. */
- GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * sizeof(ScatterGatherEntry);
+ GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * pTaskState->cbSGEntry;
} while (cScatterGatherGCLeft > 0);
}
}
- else if ( pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB
- || pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
+ else if ( pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB
+ || pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
{
/* The buffer is not scattered. */
- RTGCPHYS GCPhysAddrDataBase = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
+ RTGCPHYS GCPhysAddrDataBase = u32PhysAddrCCB;
AssertMsg(GCPhysAddrDataBase != 0, ("Physical address is 0\n"));
- pTaskState->DataSeg.cbSeg = pTaskState->CommandControlBlockGuest.cbData;
+ pTaskState->DataSeg.cbSeg = cbDataCCB;
pTaskState->DataSeg.pvSeg = RTMemAlloc(pTaskState->DataSeg.cbSeg);
if (!pTaskState->DataSeg.pvSeg)
return VERR_NO_MEMORY;
Log(("Non scattered buffer:\n"));
- Log(("u32PhysAddrData=%#x\n", pTaskState->CommandControlBlockGuest.u32PhysAddrData));
- Log(("cbData=%u\n", pTaskState->CommandControlBlockGuest.cbData));
+ Log(("u32PhysAddrData=%#x\n", u32PhysAddrCCB));
+ Log(("cbData=%u\n", cbDataCCB));
Log(("GCPhysAddrDataBase=0x%RGp\n", GCPhysAddrDataBase));
/* Copy the data into the buffer. */
@@ -1187,34 +1488,57 @@ static int buslogicDataBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
* @returns nothing.
* @param pTaskState Pointer to the task state.
*/
-static void buslogicDataBufferFree(PBUSLOGICTASKSTATE pTaskState)
+static void buslogicR3DataBufferFree(PBUSLOGICTASKSTATE pTaskState)
{
PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
+ uint32_t cbDataCCB;
+ uint32_t u32PhysAddrCCB;
+
+ /* Extract the data length and physical address from the CCB. */
+ if (pTaskState->fIs24Bit)
+ {
+ u32PhysAddrCCB = ADDR_TO_U32(pTaskState->CommandControlBlockGuest.o.aPhysAddrData);
+ cbDataCCB = LEN_TO_U32(pTaskState->CommandControlBlockGuest.o.acbData);
+ }
+ else
+ {
+ u32PhysAddrCCB = pTaskState->CommandControlBlockGuest.n.u32PhysAddrData;
+ cbDataCCB = pTaskState->CommandControlBlockGuest.n.cbData;
+ }
+
+#if 1
+ /* Hack for NT 10/91: A CCB describes a 2K buffer, but TEST UNIT READY is executed. This command
+ * returns no data, hence the buffer must be left alone!
+ */
+ if (pTaskState->CommandControlBlockGuest.c.abCDB[0] == 0)
+ cbDataCCB = 0;
+#endif
+
+ LogFlowFunc(("pTaskState=%#p cbDataCCB=%u direction=%u cbSeg=%u\n", pTaskState, cbDataCCB,
+ pTaskState->CommandControlBlockGuest.c.uDataDirection, pTaskState->DataSeg.cbSeg));
- if ( (pTaskState->CommandControlBlockGuest.cbData > 0)
- && ( (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN)
- || (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN)))
+ if ( (cbDataCCB > 0)
+ && ( (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN)
+ || (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN)))
{
- if ( (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
- || (pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
+ if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_SCATTER_GATHER)
+ || (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
{
uint32_t cScatterGatherGCRead;
uint32_t iScatterGatherEntry;
- ScatterGatherEntry aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */
- uint32_t cScatterGatherGCLeft = pTaskState->CommandControlBlockGuest.cbData / sizeof(ScatterGatherEntry);
- RTGCPHYS GCPhysAddrScatterGatherCurrent = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
+ SGE32 aScatterGatherReadGC[32]; /* Number of scatter gather list entries read from guest memory. */
+ uint32_t cScatterGatherGCLeft = cbDataCCB / pTaskState->cbSGEntry;
+ RTGCPHYS GCPhysAddrScatterGatherCurrent = u32PhysAddrCCB;
uint8_t *pbData = (uint8_t *)pTaskState->DataSeg.pvSeg;
do
{
- cScatterGatherGCRead = (cScatterGatherGCLeft < RT_ELEMENTS(aScatterGatherReadGC))
- ? cScatterGatherGCLeft
- : RT_ELEMENTS(aScatterGatherReadGC);
+ cScatterGatherGCRead = (cScatterGatherGCLeft < RT_ELEMENTS(aScatterGatherReadGC))
+ ? cScatterGatherGCLeft
+ : RT_ELEMENTS(aScatterGatherReadGC);
cScatterGatherGCLeft -= cScatterGatherGCRead;
- /* Read the SG entries. */
- PDMDevHlpPhysRead(pDevIns, GCPhysAddrScatterGatherCurrent, &aScatterGatherReadGC[0],
- cScatterGatherGCRead * sizeof(ScatterGatherEntry));
+ buslogicR3ReadSGEntries(pTaskState, GCPhysAddrScatterGatherCurrent, cScatterGatherGCRead, aScatterGatherReadGC);
for (iScatterGatherEntry = 0; iScatterGatherEntry < cScatterGatherGCRead; iScatterGatherEntry++)
{
@@ -1228,31 +1552,45 @@ static void buslogicDataBufferFree(PBUSLOGICTASKSTATE pTaskState)
Log(("%s: GCPhysAddrDataBase=%RGp cbDataToTransfer=%u\n", __FUNCTION__, GCPhysAddrDataBase, cbDataToTransfer));
- PDMDevHlpPhysWrite(pDevIns, GCPhysAddrDataBase, pbData, cbDataToTransfer);
+ PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrDataBase, pbData, cbDataToTransfer);
pbData += cbDataToTransfer;
}
/* Set address to the next entries to read. */
- GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * sizeof(ScatterGatherEntry);
+ GCPhysAddrScatterGatherCurrent += cScatterGatherGCRead * pTaskState->cbSGEntry;
} while (cScatterGatherGCLeft > 0);
}
- else if ( pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB
- || pTaskState->CommandControlBlockGuest.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
+ else if ( pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB
+ || pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
{
/* The buffer is not scattered. */
- RTGCPHYS GCPhysAddrDataBase = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrData;
+ RTGCPHYS GCPhysAddrDataBase = u32PhysAddrCCB;
AssertMsg(GCPhysAddrDataBase != 0, ("Physical address is 0\n"));
- Log(("Non scattered buffer:\n"));
- Log(("u32PhysAddrData=%#x\n", pTaskState->CommandControlBlockGuest.u32PhysAddrData));
- Log(("cbData=%u\n", pTaskState->CommandControlBlockGuest.cbData));
+ Log(("Non-scattered buffer:\n"));
+ Log(("u32PhysAddrData=%#x\n", u32PhysAddrCCB));
+ Log(("cbData=%u\n", cbDataCCB));
Log(("GCPhysAddrDataBase=0x%RGp\n", GCPhysAddrDataBase));
/* Copy the data into the guest memory. */
- PDMDevHlpPhysWrite(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg);
+ PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrDataBase, pTaskState->DataSeg.pvSeg, pTaskState->DataSeg.cbSeg);
}
+
+ }
+ /* Update residual data length. */
+ if ( (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_DATA_LENGTH)
+ || (pTaskState->CommandControlBlockGuest.c.uOpcode == BUSLOGIC_CCB_OPCODE_INITIATOR_CCB_RESIDUAL_SCATTER_GATHER))
+ {
+ uint32_t cbResidual;
+
+ /** @todo we need to get the actual transfer length from the VSCSI layer?! */
+ cbResidual = 0; //LEN_TO_U32(pTaskState->CCBGuest.acbData) - ???;
+ if (pTaskState->fIs24Bit)
+ U32_TO_LEN(pTaskState->CommandControlBlockGuest.o.acbData, cbResidual);
+ else
+ pTaskState->CommandControlBlockGuest.n.cbData = cbResidual;
}
RTMemFree(pTaskState->DataSeg.pvSeg);
@@ -1260,6 +1598,20 @@ static void buslogicDataBufferFree(PBUSLOGICTASKSTATE pTaskState)
pTaskState->DataSeg.cbSeg = 0;
}
+/** Convert sense buffer length taking into account shortcut values. */
+static uint32_t buslogicR3ConvertSenseBufferLength(uint32_t cbSense)
+{
+ /* Convert special sense buffer length values. */
+ if (cbSense == 0)
+ cbSense = 14; /* 0 means standard 14-byte buffer. */
+ else if (cbSense == 1)
+ cbSense = 0; /* 1 means no sense data. */
+ else if (cbSense < 8)
+ AssertMsgFailed(("Reserved cbSense value of %d used!\n", cbSense));
+
+ return cbSense;
+}
+
/**
* Free the sense buffer.
*
@@ -1267,15 +1619,33 @@ static void buslogicDataBufferFree(PBUSLOGICTASKSTATE pTaskState)
* @param pTaskState Pointer to the task state.
* @param fCopy If sense data should be copied to guest memory.
*/
-static void buslogicSenseBufferFree(PBUSLOGICTASKSTATE pTaskState, bool fCopy)
+static void buslogicR3SenseBufferFree(PBUSLOGICTASKSTATE pTaskState, bool fCopy)
{
- PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
- RTGCPHYS GCPhysAddrSenseBuffer = (RTGCPHYS)pTaskState->CommandControlBlockGuest.u32PhysAddrSenseData;
- uint32_t cbSenseBuffer = pTaskState->CommandControlBlockGuest.cbSenseData;
+ uint32_t cbSenseBuffer;
+
+ cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData);
+
+ /* Copy the sense buffer into guest memory if requested. */
+ if (fCopy && cbSenseBuffer)
+ {
+ PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
+ RTGCPHYS GCPhysAddrSenseBuffer;
+
+ /* With 32-bit CCBs, the (optional) sense buffer physical address is provided separately.
+ * On the other hand, with 24-bit CCBs, the sense buffer is simply located at the end of
+ * the CCB, right after the variable-length CDB.
+ */
+ if (pTaskState->fIs24Bit)
+ {
+ GCPhysAddrSenseBuffer = pTaskState->MailboxGuest.u32PhysAddrCCB;
+ GCPhysAddrSenseBuffer += pTaskState->CommandControlBlockGuest.c.cbCDB + RT_OFFSETOF(CCB24, abCDB);
+ }
+ else
+ GCPhysAddrSenseBuffer = pTaskState->CommandControlBlockGuest.n.u32PhysAddrSenseData;
- /* Copy into guest memory. */
- if (fCopy)
- PDMDevHlpPhysWrite(pDevIns, GCPhysAddrSenseBuffer, pTaskState->pbSenseBuffer, cbSenseBuffer);
+ Log3(("%s: sense buffer: %.*Rhxs\n", __FUNCTION__, cbSenseBuffer, pTaskState->pbSenseBuffer));
+ PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrSenseBuffer, pTaskState->pbSenseBuffer, cbSenseBuffer);
+ }
RTMemFree(pTaskState->pbSenseBuffer);
pTaskState->pbSenseBuffer = NULL;
@@ -1288,17 +1658,24 @@ static void buslogicSenseBufferFree(PBUSLOGICTASKSTATE pTaskState, bool fCopy)
* @param pTaskState Pointer to the task state.
* @note Current assumption is that the sense buffer is not scattered and does not cross a page boundary.
*/
-static int buslogicSenseBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
+static int buslogicR3SenseBufferAlloc(PBUSLOGICTASKSTATE pTaskState)
{
PPDMDEVINS pDevIns = pTaskState->CTX_SUFF(pTargetDevice)->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
- uint32_t cbSenseBuffer = pTaskState->CommandControlBlockGuest.cbSenseData;
+ uint32_t cbSenseBuffer;
+
+ pTaskState->pbSenseBuffer = NULL;
- pTaskState->pbSenseBuffer = (uint8_t *)RTMemAllocZ(cbSenseBuffer);
- if (!pTaskState->pbSenseBuffer)
- return VERR_NO_MEMORY;
+ cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData);
+ if (cbSenseBuffer)
+ {
+ pTaskState->pbSenseBuffer = (uint8_t *)RTMemAllocZ(cbSenseBuffer);
+ if (!pTaskState->pbSenseBuffer)
+ return VERR_NO_MEMORY;
+ }
return VINF_SUCCESS;
}
+
#endif /* IN_RING3 */
/**
@@ -1317,6 +1694,10 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
switch (pBusLogic->uOperationCode)
{
+ case BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT:
+ /* Valid command, no reply. */
+ pBusLogic->cbReplyParametersLeft = 0;
+ break;
case BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION:
{
PReplyInquirePCIHostAdapterInformation pReply = (PReplyInquirePCIHostAdapterInformation)pBusLogic->aReplyBuffer;
@@ -1324,20 +1705,26 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
/* It seems VMware does not provide valid information here too, lets do the same :) */
pReply->InformationIsValid = 0;
- pReply->IsaIOPort = 0xff; /* Make it invalid. */
+ pReply->IsaIOPort = pBusLogic->uISABaseCode;
+ pReply->IRQ = PCIDevGetInterruptLine(&pBusLogic->dev);
pBusLogic->cbReplyParametersLeft = sizeof(ReplyInquirePCIHostAdapterInformation);
break;
}
case BUSLOGICCOMMAND_MODIFY_IO_ADDRESS:
{
+ /* Modify the ISA-compatible I/O port base. Note that this technically
+ * violates the PCI spec, as this address is not reported through PCI.
+ * However, it is required for compatibility with old drivers.
+ */
+#ifdef IN_RING3
+ Log(("ISA I/O for PCI (code %x)\n", pBusLogic->aCommandBuffer[0]));
+ buslogicR3RegisterISARange(pBusLogic, pBusLogic->aCommandBuffer[0]);
pBusLogic->cbReplyParametersLeft = 0;
- if (pBusLogic->aCommandBuffer[0] == 0x06)
- {
- Log(("Disabling ISA I/O ports.\n"));
- pBusLogic->fISAEnabled = false;
- }
fSuppressIrq = true;
break;
+#else
+ AssertMsgFailed(("Must never get here!\n"));
+#endif
}
case BUSLOGICCOMMAND_INQUIRE_BOARD_ID:
{
@@ -1366,6 +1753,50 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
pBusLogic->cbReplyParametersLeft = 1;
break;
}
+ case BUSLOGICCOMMAND_SET_ADAPTER_OPTIONS:
+ /* The parameter list length is determined by the first byte of the command buffer. */
+ if (pBusLogic->iParameter == 1)
+ {
+ /* First pass - set the number of following parameter bytes. */
+ pBusLogic->cbCommandParametersLeft = pBusLogic->aCommandBuffer[0];
+ Log(("Set HA options: %u bytes follow\n", pBusLogic->cbCommandParametersLeft));
+ }
+ else
+ {
+ /* Second pass - process received data. */
+ Log(("Set HA options: received %u bytes\n", pBusLogic->aCommandBuffer[0]));
+ /* We ignore the data - it only concerns the SCSI hardware protocol. */
+ }
+ pBusLogic->cbReplyParametersLeft = 0;
+ break;
+
+ case BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND:
+ /* The parameter list length is at least 12 bytes; the 12th byte determines
+ * the number of additional CDB bytes that will follow.
+ */
+ if (pBusLogic->iParameter == 12)
+ {
+ /* First pass - set the number of following CDB bytes. */
+ pBusLogic->cbCommandParametersLeft = pBusLogic->aCommandBuffer[11];
+ Log(("Execute SCSI cmd: %u more bytes follow\n", pBusLogic->cbCommandParametersLeft));
+ }
+ else
+ {
+ PESCMD pCmd;
+
+ /* Second pass - process received data. */
+ Log(("Execute SCSI cmd: received %u bytes\n", pBusLogic->aCommandBuffer[0]));
+
+ pCmd = (PESCMD)pBusLogic->aCommandBuffer;
+ Log(("Addr %08X, cbData %08X, cbCDB=%u\n", pCmd->u32PhysAddrData, pCmd->cbData, pCmd->cbCDB));
+ }
+ // This is currently a dummy - just fails every command.
+ pBusLogic->cbReplyParametersLeft = 4;
+ pBusLogic->aReplyBuffer[0] = pBusLogic->aReplyBuffer[1] = 0;
+ pBusLogic->aReplyBuffer[2] = 0x11; /* HBA status (timeout). */
+ pBusLogic->aReplyBuffer[3] = 0; /* Device status. */
+ break;
+
case BUSLOGICCOMMAND_INQUIRE_HOST_ADAPTER_MODEL_NUMBER:
{
/* The reply length is set by the guest and is found in the first byte of the command buffer. */
@@ -1383,27 +1814,49 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
}
case BUSLOGICCOMMAND_INQUIRE_CONFIGURATION:
{
+ uint8_t uPciIrq = PCIDevGetInterruptLine(&pBusLogic->dev);
+
pBusLogic->cbReplyParametersLeft = sizeof(ReplyInquireConfiguration);
PReplyInquireConfiguration pReply = (PReplyInquireConfiguration)pBusLogic->aReplyBuffer;
memset(pReply, 0, sizeof(ReplyInquireConfiguration));
pReply->uHostAdapterId = 7; /* The controller has always 7 as ID. */
- /*
- * The rest of this reply only applies for ISA adapters.
- * This is a PCI adapter so they are not important and are skipped.
+ pReply->fDmaChannel6 = 1; /* DMA channel 6 is a good default. */
+ /* The PCI IRQ is not necessarily representable in this structure.
+ * If that is the case, the guest likely won't function correctly,
+ * therefore we log a warning.
*/
+ switch (uPciIrq)
+ {
+ case 9: pReply->fIrqChannel9 = 1; break;
+ case 10: pReply->fIrqChannel10 = 1; break;
+ case 11: pReply->fIrqChannel11 = 1; break;
+ case 12: pReply->fIrqChannel12 = 1; break;
+ case 14: pReply->fIrqChannel14 = 1; break;
+ case 15: pReply->fIrqChannel15 = 1; break;
+ default:
+ LogRel(("Warning: PCI IRQ %d cannot be represented as ISA!\n", uPciIrq));
+ break;
+ }
break;
}
case BUSLOGICCOMMAND_INQUIRE_EXTENDED_SETUP_INFORMATION:
{
+ /* Some Adaptec AHA-154x drivers (e.g. OS/2) execute this command and expect
+ * it to fail. If it succeeds, the drivers refuse to load. However, some newer
+ * Adaptec 154x models supposedly support it too??
+ */
+
/* The reply length is set by the guest and is found in the first byte of the command buffer. */
pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
PReplyInquireExtendedSetupInformation pReply = (PReplyInquireExtendedSetupInformation)pBusLogic->aReplyBuffer;
memset(pReply, 0, sizeof(ReplyInquireExtendedSetupInformation));
- //@todo: should this reflect the RAM contents (AutoSCSIRam)?
+ /** @todo should this reflect the RAM contents (AutoSCSIRam)? */
pReply->uBusType = 'E'; /* EISA style */
pReply->u16ScatterGatherLimit = 8192;
+ pReply->cMailbox = pBusLogic->cMailbox;
+ pReply->uMailboxAddressBase = (uint32_t)pBusLogic->GCPhysAddrMailboxOutgoingBase;
pReply->fLevelSensitiveInterrupt = true;
pReply->fHostWideSCSI = true;
pReply->fHostUltraSCSI = true;
@@ -1417,6 +1870,16 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
PReplyInquireSetupInformation pReply = (PReplyInquireSetupInformation)pBusLogic->aReplyBuffer;
memset(pReply, 0, sizeof(ReplyInquireSetupInformation));
+ pReply->fSynchronousInitiationEnabled = true;
+ pReply->fParityCheckingEnabled = true;
+ pReply->cMailbox = pBusLogic->cMailbox;
+ U32_TO_ADDR(pReply->MailboxAddress, pBusLogic->GCPhysAddrMailboxOutgoingBase);
+ pReply->uSignature = 'B';
+ /* The 'D' signature prevents Adaptec's OS/2 drivers from getting too
+ * friendly with BusLogic hardware and upsetting the HBA state.
+ */
+ pReply->uCharacterD = 'D'; /* BusLogic model. */
+ pReply->uHostBusType = 'F'; /* PCI bus. */
break;
}
case BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM:
@@ -1432,18 +1895,39 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
pBusLogic->iReply = uOffset;
break;
}
+ case BUSLOGICCOMMAND_INITIALIZE_MAILBOX:
+ {
+ PRequestInitMbx pRequest = (PRequestInitMbx)pBusLogic->aCommandBuffer;
+
+ pBusLogic->fMbxIs24Bit = true;
+ pBusLogic->cMailbox = pRequest->cMailbox;
+ pBusLogic->GCPhysAddrMailboxOutgoingBase = (RTGCPHYS)ADDR_TO_U32(pRequest->aMailboxBaseAddr);
+ /* The area for incoming mailboxes is right after the last entry of outgoing mailboxes. */
+ pBusLogic->GCPhysAddrMailboxIncomingBase = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->cMailbox * sizeof(Mailbox24));
+
+ Log(("GCPhysAddrMailboxOutgoingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxOutgoingBase));
+ Log(("GCPhysAddrMailboxIncomingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxIncomingBase));
+ Log(("cMailboxes=%u (24-bit mode)\n", pBusLogic->cMailbox));
+ LogRel(("Initialized 24-bit mailbox, %d entries at %08x\n", pRequest->cMailbox, ADDR_TO_U32(pRequest->aMailboxBaseAddr)));
+
+ pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED;
+ pBusLogic->cbReplyParametersLeft = 0;
+ break;
+ }
case BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX:
{
PRequestInitializeExtendedMailbox pRequest = (PRequestInitializeExtendedMailbox)pBusLogic->aCommandBuffer;
+ pBusLogic->fMbxIs24Bit = false;
pBusLogic->cMailbox = pRequest->cMailbox;
pBusLogic->GCPhysAddrMailboxOutgoingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress;
/* The area for incoming mailboxes is right after the last entry of outgoing mailboxes. */
- pBusLogic->GCPhysAddrMailboxIncomingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress + (pBusLogic->cMailbox * sizeof(Mailbox));
+ pBusLogic->GCPhysAddrMailboxIncomingBase = (RTGCPHYS)pRequest->uMailboxBaseAddress + (pBusLogic->cMailbox * sizeof(Mailbox32));
Log(("GCPhysAddrMailboxOutgoingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxOutgoingBase));
Log(("GCPhysAddrMailboxIncomingBase=%RGp\n", pBusLogic->GCPhysAddrMailboxIncomingBase));
- Log(("cMailboxes=%u\n", pBusLogic->cMailbox));
+ Log(("cMailboxes=%u (32-bit mode)\n", pBusLogic->cMailbox));
+ LogRel(("Initialized 32-bit mailbox, %d entries at %08x\n", pRequest->cMailbox, pRequest->uMailboxBaseAddress));
pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED;
pBusLogic->cbReplyParametersLeft = 0;
@@ -1473,6 +1957,29 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
pBusLogic->cbReplyParametersLeft = 0;
break;
}
+ case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_0_TO_7:
+ /* This is supposed to send TEST UNIT READY to each target/LUN.
+ * We cheat and skip that, since we already know what's attached
+ */
+ memset(pBusLogic->aReplyBuffer, 0, 8);
+ for (int i = 0; i < 8; ++i)
+ {
+ if (pBusLogic->aDeviceStates[i].fPresent)
+ pBusLogic->aReplyBuffer[i] = 1;
+ }
+ pBusLogic->aReplyBuffer[7] = 0; /* HA hardcoded at ID 7. */
+ pBusLogic->cbReplyParametersLeft = 8;
+ break;
+ case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_8_TO_15:
+ /* See note about cheating above. */
+ memset(pBusLogic->aReplyBuffer, 0, 8);
+ for (int i = 0; i < 8; ++i)
+ {
+ if (pBusLogic->aDeviceStates[i + 8].fPresent)
+ pBusLogic->aReplyBuffer[i] = 1;
+ }
+ pBusLogic->cbReplyParametersLeft = 8;
+ break;
case BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES:
{
/* Each bit which is set in the 16bit wide variable means a present device. */
@@ -1493,7 +2000,7 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
for (uint8_t i = 0; i < pBusLogic->cbReplyParametersLeft; i++)
- pBusLogic->aReplyBuffer[i] = 0; /* @todo Figure if we need something other here. It's not needed for the linux driver */
+ pBusLogic->aReplyBuffer[i] = 0; /** @todo Figure if we need something other here. It's not needed for the linux driver */
break;
}
@@ -1527,6 +2034,45 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
Log(("Bus-off time: %d\n", pBusLogic->aCommandBuffer[0]));
break;
}
+ case BUSLOGICCOMMAND_SET_BUS_TRANSFER_RATE:
+ {
+ pBusLogic->cbReplyParametersLeft = 0;
+ pBusLogic->LocalRam.structured.autoSCSIData.uDMATransferRate = pBusLogic->aCommandBuffer[0];
+ Log(("Bus transfer rate: %02X\n", pBusLogic->aCommandBuffer[0]));
+ break;
+ }
+ case BUSLOGICCOMMAND_WRITE_BUSMASTER_CHIP_FIFO:
+ {
+ RTGCPHYS GCPhysFifoBuf;
+ Addr24 addr;
+
+ pBusLogic->cbReplyParametersLeft = 0;
+ addr.hi = pBusLogic->aCommandBuffer[0];
+ addr.mid = pBusLogic->aCommandBuffer[1];
+ addr.lo = pBusLogic->aCommandBuffer[2];
+ GCPhysFifoBuf = (RTGCPHYS)ADDR_TO_U32(addr);
+ Log(("Write busmaster FIFO at: %04X\n", ADDR_TO_U32(addr)));
+ PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysFifoBuf,
+ &pBusLogic->LocalRam.u8View[64], 64);
+ break;
+ }
+ case BUSLOGICCOMMAND_READ_BUSMASTER_CHIP_FIFO:
+ {
+ RTGCPHYS GCPhysFifoBuf;
+ Addr24 addr;
+
+ pBusLogic->cbReplyParametersLeft = 0;
+ addr.hi = pBusLogic->aCommandBuffer[0];
+ addr.mid = pBusLogic->aCommandBuffer[1];
+ addr.lo = pBusLogic->aCommandBuffer[2];
+ GCPhysFifoBuf = (RTGCPHYS)ADDR_TO_U32(addr);
+ Log(("Read busmaster FIFO at: %04X\n", ADDR_TO_U32(addr)));
+ PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysFifoBuf,
+ &pBusLogic->LocalRam.u8View[64], 64);
+ break;
+ }
+ default:
+ AssertMsgFailed(("Invalid command %#x\n", pBusLogic->uOperationCode));
case BUSLOGICCOMMAND_EXT_BIOS_INFO:
case BUSLOGICCOMMAND_UNLOCK_MAILBOX:
/* Commands valid for Adaptec 154xC which we don't handle since
@@ -1537,8 +2083,7 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_COMMAND_INVALID;
break;
case BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND: /* Should be handled already. */
- default:
- AssertMsgFailed(("Invalid command %#x\n", pBusLogic->uOperationCode));
+ AssertMsgFailed(("Invalid mailbox execute state!\n"));
}
Log(("uOperationCode=%#x, cbReplyParametersLeft=%d\n", pBusLogic->uOperationCode, pBusLogic->cbReplyParametersLeft));
@@ -1546,7 +2091,7 @@ static int buslogicProcessCommand(PBUSLOGIC pBusLogic)
/* Set the data in ready bit in the status register in case the command has a reply. */
if (pBusLogic->cbReplyParametersLeft)
pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_DATA_IN_REGISTER_READY;
- else
+ else if (!pBusLogic->cbCommandParametersLeft)
buslogicCommandComplete(pBusLogic, fSuppressIrq);
return rc;
@@ -1569,14 +2114,27 @@ static int buslogicRegisterRead(PBUSLOGIC pBusLogic, unsigned iRegister, uint32_
case BUSLOGIC_REGISTER_STATUS:
{
*pu32 = pBusLogic->regStatus;
- /*
- * If the diagnostic active bit is set we are in a hard reset initiated from the guest.
- * The guest reads the status register and waits that the host adapter ready bit is set.
+
+ /* If the diagnostic active bit is set, we are in a guest-initiated
+ * hard reset. If the guest reads the status register and waits for
+ * the host adapter ready bit to be set, we terminate the reset right
+ * away. However, guests may also expect the reset condition to clear
+ * automatically after a period of time, in which case we can't show
+ * the DIAG bit at all.
*/
if (pBusLogic->regStatus & BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE)
{
+ uint64_t u64AccessTime = PDMDevHlpTMTimeVirtGetNano(pBusLogic->CTX_SUFF(pDevIns));
+
pBusLogic->regStatus &= ~BUSLOGIC_REGISTER_STATUS_DIAGNOSTIC_ACTIVE;
pBusLogic->regStatus |= BUSLOGIC_REGISTER_STATUS_HOST_ADAPTER_READY;
+
+ if (u64AccessTime - pBusLogic->u64ResetTime > BUSLOGIC_RESET_DURATION_NS)
+ {
+ /* If reset already expired, let the guest see that right away. */
+ *pu32 = pBusLogic->regStatus;
+ pBusLogic->u64ResetTime = 0;
+ }
}
break;
}
@@ -1587,18 +2145,24 @@ static int buslogicRegisterRead(PBUSLOGIC pBusLogic, unsigned iRegister, uint32_
else
*pu32 = pBusLogic->aReplyBuffer[pBusLogic->iReply];
- pBusLogic->iReply++;
- pBusLogic->cbReplyParametersLeft--;
-
- LogFlowFunc(("cbReplyParametersLeft=%u\n", pBusLogic->cbReplyParametersLeft));
- if (!pBusLogic->cbReplyParametersLeft)
+ /* Careful about underflow - guest can read data register even if
+ * no data is available.
+ */
+ if (pBusLogic->cbReplyParametersLeft)
{
- /*
- * Reply finished, set command complete bit, unset data in ready bit and
- * interrupt the guest if enabled.
- */
- buslogicCommandComplete(pBusLogic, false);
+ pBusLogic->iReply++;
+ pBusLogic->cbReplyParametersLeft--;
+ if (!pBusLogic->cbReplyParametersLeft)
+ {
+ /*
+ * Reply finished, set command complete bit, unset data-in ready bit and
+ * interrupt the guest if enabled.
+ */
+ buslogicCommandComplete(pBusLogic, false);
+ }
}
+ LogFlowFunc(("data=%02x, iReply=%d, cbReplyParametersLeft=%u\n", *pu32,
+ pBusLogic->iReply, pBusLogic->cbReplyParametersLeft));
break;
}
case BUSLOGIC_REGISTER_INTERRUPT:
@@ -1637,13 +2201,26 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_
{
case BUSLOGIC_REGISTER_CONTROL:
{
+ if ((uVal & BUSLOGIC_REGISTER_CONTROL_HARD_RESET) || (uVal & BUSLOGIC_REGISTER_CONTROL_SOFT_RESET))
+ {
+#ifdef IN_RING3
+ bool fHardReset = !!(uVal & BUSLOGIC_REGISTER_CONTROL_HARD_RESET);
+
+ LogRel(("BusLogic: %s reset\n", fHardReset ? "hard" : "soft"));
+ buslogicR3InitiateReset(pBusLogic, fHardReset);
+#else
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+#endif
+ break;
+ }
+
rc = PDMCritSectEnter(&pBusLogic->CritSectIntr, VINF_IOM_R3_IOPORT_WRITE);
if (rc != VINF_SUCCESS)
return rc;
#ifdef LOG_ENABLED
uint32_t cMailboxesReady = ASMAtomicXchgU32(&pBusLogic->cInMailboxesReady, 0);
- Log(("%u incoming mailboxes are ready when this interrupt was cleared\n", cMailboxesReady));
+ Log(("%u incoming mailboxes were ready when this interrupt was cleared\n", cMailboxesReady));
#endif
if (uVal & BUSLOGIC_REGISTER_CONTROL_INTERRUPT_RESET)
@@ -1651,15 +2228,6 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_
PDMCritSectLeave(&pBusLogic->CritSectIntr);
- if ((uVal & BUSLOGIC_REGISTER_CONTROL_HARD_RESET) || (uVal & BUSLOGIC_REGISTER_CONTROL_SOFT_RESET))
- {
-#ifdef IN_RING3
- buslogicIntiateHardReset(pBusLogic);
-#else
- rc = VINF_IOM_R3_IOPORT_WRITE;
-#endif
- }
-
break;
}
case BUSLOGIC_REGISTER_COMMAND:
@@ -1667,13 +2235,16 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_
/* Fast path for mailbox execution command. */
if ((uVal == BUSLOGICCOMMAND_EXECUTE_MAILBOX_COMMAND) && (pBusLogic->uOperationCode == 0xff))
{
- ASMAtomicIncU32(&pBusLogic->cMailboxesReady);
- if (!ASMAtomicXchgBool(&pBusLogic->fNotificationSend, true))
- {
- /* Send new notification to the queue. */
- PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pBusLogic->CTX_SUFF(pNotifierQueue));
- AssertMsg(pItem, ("Allocating item for queue failed\n"));
- PDMQueueInsert(pBusLogic->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem);
+ /* If there are no mailboxes configured, don't even try to do anything. */
+ if (pBusLogic->cMailbox) {
+ ASMAtomicIncU32(&pBusLogic->cMailboxesReady);
+ if (!ASMAtomicXchgBool(&pBusLogic->fNotificationSend, true))
+ {
+ /* Send new notification to the queue. */
+ PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pBusLogic->CTX_SUFF(pNotifierQueue));
+ AssertMsg(pItem, ("Allocating item for queue failed\n"));
+ PDMQueueInsert(pBusLogic->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem);
+ }
}
return rc;
@@ -1694,11 +2265,14 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_
/* Get the number of bytes for parameters from the command code. */
switch (pBusLogic->uOperationCode)
{
+ case BUSLOGICCOMMAND_TEST_CMDC_INTERRUPT:
case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_LETTER:
case BUSLOGICCOMMAND_INQUIRE_BOARD_ID:
case BUSLOGICCOMMAND_INQUIRE_FIRMWARE_VERSION_3RD_LETTER:
case BUSLOGICCOMMAND_INQUIRE_PCI_HOST_ADAPTER_INFORMATION:
case BUSLOGICCOMMAND_INQUIRE_CONFIGURATION:
+ case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_0_TO_7:
+ case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_8_TO_15:
case BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES:
pBusLogic->cbCommandParametersLeft = 0;
break;
@@ -1713,14 +2287,30 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_
case BUSLOGICCOMMAND_ECHO_COMMAND_DATA:
case BUSLOGICCOMMAND_SET_PREEMPT_TIME_ON_BUS:
case BUSLOGICCOMMAND_SET_TIME_OFF_BUS:
+ case BUSLOGICCOMMAND_SET_BUS_TRANSFER_RATE:
pBusLogic->cbCommandParametersLeft = 1;
break;
case BUSLOGICCOMMAND_FETCH_HOST_ADAPTER_LOCAL_RAM:
pBusLogic->cbCommandParametersLeft = 2;
break;
+ case BUSLOGICCOMMAND_READ_BUSMASTER_CHIP_FIFO:
+ case BUSLOGICCOMMAND_WRITE_BUSMASTER_CHIP_FIFO:
+ pBusLogic->cbCommandParametersLeft = 3;
+ break;
+ case BUSLOGICCOMMAND_INITIALIZE_MAILBOX:
+ pBusLogic->cbCommandParametersLeft = sizeof(RequestInitMbx);
+ break;
case BUSLOGICCOMMAND_INITIALIZE_EXTENDED_MAILBOX:
pBusLogic->cbCommandParametersLeft = sizeof(RequestInitializeExtendedMailbox);
break;
+ case BUSLOGICCOMMAND_SET_ADAPTER_OPTIONS:
+ /* There must be at least one byte following this command. */
+ pBusLogic->cbCommandParametersLeft = 1;
+ break;
+ case BUSLOGICCOMMAND_EXECUTE_SCSI_COMMAND:
+ /* 12 bytes + variable-length CDB. */
+ pBusLogic->cbCommandParametersLeft = 12;
+ break;
case BUSLOGICCOMMAND_EXT_BIOS_INFO:
case BUSLOGICCOMMAND_UNLOCK_MAILBOX:
/* Invalid commands. */
@@ -1733,6 +2323,14 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_
}
else
{
+#ifndef IN_RING3
+ /* This command must be executed in R3 as it rehooks the ISA I/O port. */
+ if (pBusLogic->uOperationCode == BUSLOGICCOMMAND_MODIFY_IO_ADDRESS)
+ {
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+ break;
+ }
+#endif
/*
* The real adapter would set the Command register busy bit in the status register.
* The guest has to wait until it is unset.
@@ -1783,8 +2381,7 @@ static int buslogicRegisterWrite(PBUSLOGIC pBusLogic, unsigned iRegister, uint8_
* @param pv Where to store the result.
* @param cb Number of bytes read.
*/
-PDMBOTHCBDECL(int) buslogicMMIORead(PPDMDEVINS pDevIns, void *pvUser,
- RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
+PDMBOTHCBDECL(int) buslogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
{
/* the linux driver does not make use of the MMIO area. */
AssertMsgFailed(("MMIO Read\n"));
@@ -1802,8 +2399,7 @@ PDMBOTHCBDECL(int) buslogicMMIORead(PPDMDEVINS pDevIns, void *pvUser,
* @param pv Where to fetch the result.
* @param cb Number of bytes to write.
*/
-PDMBOTHCBDECL(int) buslogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser,
- RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
+PDMBOTHCBDECL(int) buslogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
{
/* the linux driver does not make use of the MMIO area. */
AssertMsgFailed(("MMIO Write\n"));
@@ -1821,11 +2417,10 @@ PDMBOTHCBDECL(int) buslogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser,
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
-PDMBOTHCBDECL(int) buslogicIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
- RTIOPORT Port, uint32_t *pu32, unsigned cb)
+PDMBOTHCBDECL(int) buslogicIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
- PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);;
- unsigned iRegister = Port - pBusLogic->IOPortBase;
+ PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
+ unsigned iRegister = Port % 4;
Assert(cb == 1);
@@ -1843,12 +2438,11 @@ PDMBOTHCBDECL(int) buslogicIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
-PDMBOTHCBDECL(int) buslogicIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
- RTIOPORT Port, uint32_t u32, unsigned cb)
+PDMBOTHCBDECL(int) buslogicIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
int rc = VINF_SUCCESS;
- unsigned iRegister = Port - pBusLogic->IOPortBase;
+ unsigned iRegister = Port % 4;
uint8_t uVal = (uint8_t)u32;
Assert(cb == 1);
@@ -1862,90 +2456,8 @@ PDMBOTHCBDECL(int) buslogicIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
}
#ifdef IN_RING3
-/**
- * Port I/O Handler for IN operations - legacy port.
- *
- * @returns VBox status code.
- *
- * @param pDevIns The device instance.
- * @param pvUser User argument.
- * @param uPort Port number used for the IN operation.
- * @param pu32 Where to store the result.
- * @param cb Number of bytes read.
- */
-static int buslogicIsaIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
- RTIOPORT Port, uint32_t *pu32, unsigned cb)
-{
- int rc;
- PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
-
- Assert(cb == 1);
-
- if (!pBusLogic->fISAEnabled)
- return VINF_SUCCESS;
-
- rc = vboxscsiReadRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), pu32);
-
- //Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
- // __FUNCTION__, pu32, 1, pu32, (Port - BUSLOGIC_BIOS_IO_PORT), rc));
-
- return rc;
-}
-
-static void buslogicWarningDiskFull(PPDMDEVINS pDevIns)
-{
- int rc;
- LogRel(("BusLogic#%d: Host disk full\n", pDevIns->iInstance));
- rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_DISKFULL",
- N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
- AssertRC(rc);
-}
-
-static void buslogicWarningFileTooBig(PPDMDEVINS pDevIns)
-{
- int rc;
- LogRel(("BusLogic#%d: File too big\n", pDevIns->iInstance));
- rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_FILETOOBIG",
- N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files"));
- AssertRC(rc);
-}
-
-static void buslogicWarningISCSI(PPDMDEVINS pDevIns)
-{
- int rc;
- LogRel(("BusLogic#%d: iSCSI target unavailable\n", pDevIns->iInstance));
- rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_ISCSIDOWN",
- N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
- AssertRC(rc);
-}
-
-static void buslogicWarningUnknown(PPDMDEVINS pDevIns, int rc)
-{
- int rc2;
- LogRel(("BusLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc));
- rc2 = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_UNKNOWN",
- N_("An unknown but recoverable I/O error has occurred (rc=%Rrc). VM execution is suspended. You can resume when the error is fixed"), rc);
- AssertRC(rc2);
-}
-
-static void buslogicRedoSetWarning(PBUSLOGIC pThis, int rc)
-{
- if (rc == VERR_DISK_FULL)
- buslogicWarningDiskFull(pThis->CTX_SUFF(pDevIns));
- else if (rc == VERR_FILE_TOO_BIG)
- buslogicWarningFileTooBig(pThis->CTX_SUFF(pDevIns));
- else if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
- {
- /* iSCSI connection abort (first error) or failure to reestablish
- * connection (second error). Pause VM. On resume we'll retry. */
- buslogicWarningISCSI(pThis->CTX_SUFF(pDevIns));
- }
- else
- buslogicWarningUnknown(pThis->CTX_SUFF(pDevIns), rc);
-}
-
-static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic)
+static int buslogicR3PrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic)
{
int rc;
PBUSLOGICTASKSTATE pTaskState;
@@ -1975,9 +2487,9 @@ static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic)
ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
- memcpy(pBusLogic->VBoxSCSI.pBuf, &ScsiInquiryData, 5);
+ memcpy(pBusLogic->VBoxSCSI.pbBuf, &ScsiInquiryData, 5);
- rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest);
+ rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest, SCSI_STATUS_OK);
AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc);
RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
@@ -1996,8 +2508,35 @@ static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic)
return rc;
}
+
+/**
+ * Port I/O Handler for IN operations - BIOS port.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pvUser User argument.
+ * @param uPort Port number used for the IN operation.
+ * @param pu32 Where to store the result.
+ * @param cb Number of bytes read.
+ */
+static DECLCALLBACK(int) buslogicR3BiosIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ int rc;
+ PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
+
+ Assert(cb == 1);
+
+ rc = vboxscsiReadRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), pu32);
+
+ //Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
+ // __FUNCTION__, pu32, 1, pu32, (Port - BUSLOGIC_BIOS_IO_PORT), rc));
+
+ return rc;
+}
+
/**
- * Port I/O Handler for OUT operations - legacy port.
+ * Port I/O Handler for OUT operations - BIOS port.
*
* @returns VBox status code.
*
@@ -2007,8 +2546,7 @@ static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic)
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
-static int buslogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
- RTIOPORT Port, uint32_t u32, unsigned cb)
+static DECLCALLBACK(int) buslogicR3BiosIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
int rc;
PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
@@ -2018,13 +2556,10 @@ static int buslogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
Assert(cb == 1);
- if (!pBusLogic->fISAEnabled)
- return VINF_SUCCESS;
-
rc = vboxscsiWriteRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), (uint8_t)u32);
if (rc == VERR_MORE_DATA)
{
- rc = buslogicPrepareBIOSSCSIRequest(pBusLogic);
+ rc = buslogicR3PrepareBIOSSCSIRequest(pBusLogic);
AssertRC(rc);
}
else if (RT_FAILURE(rc))
@@ -2037,7 +2572,8 @@ static int buslogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
* Port I/O Handler for primary port range OUT string operations.
* @see FNIOMIOPORTOUTSTRING for details.
*/
-static DECLCALLBACK(int) buslogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
+static DECLCALLBACK(int) buslogicR3BiosIoPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc,
+ PRTGCUINTREG pcTransfer, unsigned cb)
{
PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
int rc;
@@ -2049,7 +2585,7 @@ static DECLCALLBACK(int) buslogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvU
pGCPtrSrc, pcTransfer, cb);
if (rc == VERR_MORE_DATA)
{
- rc = buslogicPrepareBIOSSCSIRequest(pBusLogic);
+ rc = buslogicR3PrepareBIOSSCSIRequest(pBusLogic);
AssertRC(rc);
}
else if (RT_FAILURE(rc))
@@ -2062,7 +2598,8 @@ static DECLCALLBACK(int) buslogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvU
* Port I/O Handler for primary port range IN string operations.
* @see FNIOMIOPORTINSTRING for details.
*/
-static DECLCALLBACK(int) buslogicIsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
+static DECLCALLBACK(int) buslogicR3BiosIoPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst,
+ PRTGCUINTREG pcTransfer, unsigned cb)
{
PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
@@ -2073,9 +2610,120 @@ static DECLCALLBACK(int) buslogicIsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUs
pGCPtrDst, pcTransfer, cb);
}
-static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
- RTGCPHYS GCPhysAddress, uint32_t cb,
- PCIADDRESSSPACE enmType)
+/**
+ * Update the ISA I/O range.
+ *
+ * @returns nothing.
+ * @param pBusLogic Pointer to the BusLogic device instance.
+ * @param uBaseCode Encoded ISA I/O base; only low 3 bits are used.
+ */
+static int buslogicR3RegisterISARange(PBUSLOGIC pBusLogic, uint8_t uBaseCode)
+{
+ uint8_t uCode = uBaseCode & MAX_ISA_BASE;
+ uint16_t uNewBase = g_aISABases[uCode];
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("ISA I/O code %02X, new base %X\n", uBaseCode, uNewBase));
+
+ /* Check if the same port range is already registered. */
+ if (uNewBase != pBusLogic->IOISABase)
+ {
+ /* Unregister the old range, if any. */
+ if (pBusLogic->IOISABase)
+ rc = PDMDevHlpIOPortDeregister(pBusLogic->CTX_SUFF(pDevIns), pBusLogic->IOISABase, 4);
+
+ if (RT_SUCCESS(rc))
+ {
+ pBusLogic->IOISABase = 0; /* First mark as unregistered. */
+ pBusLogic->uISABaseCode = ISA_BASE_DISABLED;
+
+ if (uNewBase)
+ {
+ /* Register the new range if requested. */
+ rc = PDMDevHlpIOPortRegister(pBusLogic->CTX_SUFF(pDevIns), uNewBase, 4, NULL,
+ buslogicIOPortWrite, buslogicIOPortRead,
+ NULL, NULL,
+ "BusLogic ISA");
+ if (RT_SUCCESS(rc))
+ {
+ pBusLogic->IOISABase = uNewBase;
+ pBusLogic->uISABaseCode = uCode;
+ }
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ if (uNewBase)
+ {
+ Log(("ISA I/O base: %x\n", uNewBase));
+ LogRel(("BusLogic: ISA I/O base: %x\n", uNewBase));
+ }
+ else
+ {
+ Log(("Disabling ISA I/O ports.\n"));
+ LogRel(("BusLogic: ISA I/O disabled\n"));
+ }
+ }
+
+ }
+ return rc;
+}
+
+static void buslogicR3WarningDiskFull(PPDMDEVINS pDevIns)
+{
+ int rc;
+ LogRel(("BusLogic#%d: Host disk full\n", pDevIns->iInstance));
+ rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_DISKFULL",
+ N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
+ AssertRC(rc);
+}
+
+static void buslogicR3WarningFileTooBig(PPDMDEVINS pDevIns)
+{
+ int rc;
+ LogRel(("BusLogic#%d: File too big\n", pDevIns->iInstance));
+ rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_FILETOOBIG",
+ N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files"));
+ AssertRC(rc);
+}
+
+static void buslogicR3WarningISCSI(PPDMDEVINS pDevIns)
+{
+ int rc;
+ LogRel(("BusLogic#%d: iSCSI target unavailable\n", pDevIns->iInstance));
+ rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_ISCSIDOWN",
+ N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
+ AssertRC(rc);
+}
+
+static void buslogicR3WarningUnknown(PPDMDEVINS pDevIns, int rc)
+{
+ int rc2;
+ LogRel(("BusLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc));
+ rc2 = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevBusLogic_UNKNOWN",
+ N_("An unknown but recoverable I/O error has occurred (rc=%Rrc). VM execution is suspended. You can resume when the error is fixed"), rc);
+ AssertRC(rc2);
+}
+
+static void buslogicR3RedoSetWarning(PBUSLOGIC pThis, int rc)
+{
+ if (rc == VERR_DISK_FULL)
+ buslogicR3WarningDiskFull(pThis->CTX_SUFF(pDevIns));
+ else if (rc == VERR_FILE_TOO_BIG)
+ buslogicR3WarningFileTooBig(pThis->CTX_SUFF(pDevIns));
+ else if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
+ {
+ /* iSCSI connection abort (first error) or failure to reestablish
+ * connection (second error). Pause VM. On resume we'll retry. */
+ buslogicR3WarningISCSI(pThis->CTX_SUFF(pDevIns));
+ }
+ else
+ buslogicR3WarningUnknown(pThis->CTX_SUFF(pDevIns), rc);
+}
+
+
+static DECLCALLBACK(int) buslogicR3MmioMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
+ RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
{
PPDMDEVINS pDevIns = pPciDev->pDevIns;
PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
@@ -2090,7 +2738,7 @@ static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iR
/* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
- buslogicMMIOWrite, buslogicMMIORead, "BusLogic");
+ buslogicMMIOWrite, buslogicMMIORead, "BusLogic MMIO");
if (RT_FAILURE(rc))
return rc;
@@ -2115,14 +2763,14 @@ static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iR
else if (enmType == PCI_ADDRESS_SPACE_IO)
{
rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, 32,
- NULL, buslogicIOPortWrite, buslogicIOPortRead, NULL, NULL, "BusLogic");
+ NULL, buslogicIOPortWrite, buslogicIOPortRead, NULL, NULL, "BusLogic PCI");
if (RT_FAILURE(rc))
return rc;
if (pThis->fR0Enabled)
{
rc = PDMDevHlpIOPortRegisterR0(pDevIns, (RTIOPORT)GCPhysAddress, 32,
- 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic");
+ 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic PCI");
if (RT_FAILURE(rc))
return rc;
}
@@ -2130,7 +2778,7 @@ static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iR
if (pThis->fGCEnabled)
{
rc = PDMDevHlpIOPortRegisterRC(pDevIns, (RTIOPORT)GCPhysAddress, 32,
- 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic");
+ 0, "buslogicIOPortWrite", "buslogicIOPortRead", NULL, NULL, "BusLogic PCI");
if (RT_FAILURE(rc))
return rc;
}
@@ -2143,8 +2791,8 @@ static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iR
return rc;
}
-static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
- int rcCompletion, bool fRedo, int rcReq)
+static DECLCALLBACK(int) buslogicR3DeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
+ int rcCompletion, bool fRedo, int rcReq)
{
int rc;
PBUSLOGICTASKSTATE pTaskState = (PBUSLOGICTASKSTATE)pSCSIRequest->pvUser;
@@ -2159,10 +2807,10 @@ static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInter
{
if (!pTaskState->fBIOS)
{
- buslogicDataBufferFree(pTaskState);
+ buslogicR3DataBufferFree(pTaskState);
if (pTaskState->pbSenseBuffer)
- buslogicSenseBufferFree(pTaskState, false /* fCopy */);
+ buslogicR3SenseBufferFree(pTaskState, false /* fCopy */);
}
/* Add to the list. */
@@ -2173,37 +2821,40 @@ static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInter
/* Suspend the VM if not done already. */
if (!ASMAtomicXchgBool(&pBusLogic->fRedo, true))
- buslogicRedoSetWarning(pBusLogic, rcReq);
+ buslogicR3RedoSetWarning(pBusLogic, rcReq);
}
else
{
if (pTaskState->fBIOS)
{
- rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, pSCSIRequest);
+ rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, pSCSIRequest, rcCompletion);
AssertMsgRC(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc));
}
else
{
- buslogicDataBufferFree(pTaskState);
+ buslogicR3DataBufferFree(pTaskState);
if (pTaskState->pbSenseBuffer)
- buslogicSenseBufferFree(pTaskState, (rcCompletion != SCSI_STATUS_OK));
+ buslogicR3SenseBufferFree(pTaskState, (rcCompletion != SCSI_STATUS_OK));
if (rcCompletion == SCSI_STATUS_OK)
- buslogicSendIncomingMailbox(pBusLogic, pTaskState,
+ buslogicR3SendIncomingMailbox(pBusLogic, pTaskState,
BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED,
BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD,
BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITHOUT_ERROR);
else if (rcCompletion == SCSI_STATUS_CHECK_CONDITION)
- buslogicSendIncomingMailbox(pBusLogic, pTaskState,
+ buslogicR3SendIncomingMailbox(pBusLogic, pTaskState,
BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_CMD_COMPLETED,
BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_CHECK_CONDITION,
BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR);
else
AssertMsgFailed(("invalid completion status %d\n", rcCompletion));
}
+#ifdef LOG_ENABLED
+ buslogicR3DumpCCBInfo(&pTaskState->CommandControlBlockGuest, pTaskState->fIs24Bit);
+#endif
- /* Add task to the cache. */
+ /* Remove task from the cache. */
RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
}
@@ -2213,8 +2864,8 @@ static DECLCALLBACK(int) buslogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInter
return VINF_SUCCESS;
}
-static DECLCALLBACK(int) buslogicQueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
- uint32_t *piInstance, uint32_t *piLUN)
+static DECLCALLBACK(int) buslogicR3QueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
+ uint32_t *piInstance, uint32_t *piLUN)
{
PBUSLOGICDEVICE pBusLogicDevice = PDMISCSIPORT_2_PBUSLOGICDEVICE(pInterface);
PPDMDEVINS pDevIns = pBusLogicDevice->CTX_SUFF(pBusLogic)->CTX_SUFF(pDevIns);
@@ -2230,41 +2881,42 @@ static DECLCALLBACK(int) buslogicQueryDeviceLocation(PPDMISCSIPORT pInterface, c
return VINF_SUCCESS;
}
-static int buslogicDeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState)
+static int buslogicR3DeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState)
{
int rc = VINF_SUCCESS;
+ uint8_t uTargetIdCCB;
+ PBUSLOGICDEVICE pTargetDevice;
- /* Fetch CCB. */
+ /* Fetch the CCB from guest memory. */
+ /** @todo How much do we really have to read? */
RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB;
PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB,
- &pTaskState->CommandControlBlockGuest, sizeof(CommandControlBlock));
+ &pTaskState->CommandControlBlockGuest, sizeof(CCB32));
- PBUSLOGICDEVICE pTargetDevice = &pBusLogic->aDeviceStates[pTaskState->CommandControlBlockGuest.uTargetId];
+ uTargetIdCCB = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uTargetId : pTaskState->CommandControlBlockGuest.n.uTargetId;
+ pTargetDevice = &pBusLogic->aDeviceStates[uTargetIdCCB];
pTaskState->CTX_SUFF(pTargetDevice) = pTargetDevice;
-#ifdef DEBUG
- buslogicDumpCCBInfo(&pTaskState->CommandControlBlockGuest);
+#ifdef LOG_ENABLED
+ buslogicR3DumpCCBInfo(&pTaskState->CommandControlBlockGuest, pTaskState->fIs24Bit);
#endif
/* Alloc required buffers. */
- rc = buslogicDataBufferAlloc(pTaskState);
+ rc = buslogicR3DataBufferAlloc(pTaskState);
AssertMsgRC(rc, ("Alloc failed rc=%Rrc\n", rc));
- if (pTaskState->CommandControlBlockGuest.cbSenseData)
- {
- rc = buslogicSenseBufferAlloc(pTaskState);
- AssertMsgRC(rc, ("Mapping sense buffer failed rc=%Rrc\n", rc));
- }
+ rc = buslogicR3SenseBufferAlloc(pTaskState);
+ AssertMsgRC(rc, ("Mapping sense buffer failed rc=%Rrc\n", rc));
/* Check if device is present on bus. If not return error immediately and don't process this further. */
- if (!pBusLogic->aDeviceStates[pTaskState->CommandControlBlockGuest.uTargetId].fPresent)
+ if (!pBusLogic->aDeviceStates[uTargetIdCCB].fPresent)
{
- buslogicDataBufferFree(pTaskState);
+ buslogicR3DataBufferFree(pTaskState);
if (pTaskState->pbSenseBuffer)
- buslogicSenseBufferFree(pTaskState, true);
+ buslogicR3SenseBufferFree(pTaskState, true);
- buslogicSendIncomingMailbox(pBusLogic, pTaskState,
+ buslogicR3SendIncomingMailbox(pBusLogic, pTaskState,
BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_SCSI_SELECTION_TIMEOUT,
BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD,
BUSLOGIC_MAILBOX_INCOMING_COMPLETION_WITH_ERROR);
@@ -2274,21 +2926,22 @@ static int buslogicDeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTAT
else
{
/* Setup SCSI request. */
- pTaskState->PDMScsiRequest.uLogicalUnit = pTaskState->CommandControlBlockGuest.uLogicalUnit;
+ pTaskState->PDMScsiRequest.uLogicalUnit = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uLogicalUnit
+ : pTaskState->CommandControlBlockGuest.n.uLogicalUnit;
- if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN)
+ if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_UNKNOWN)
pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_UNKNOWN;
- else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN)
+ else if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_IN)
pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_FROM_DEVICE;
- else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT)
+ else if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_OUT)
pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_TO_DEVICE;
- else if (pTaskState->CommandControlBlockGuest.uDataDirection == BUSLOGIC_CCB_DIRECTION_NO_DATA)
+ else if (pTaskState->CommandControlBlockGuest.c.uDataDirection == BUSLOGIC_CCB_DIRECTION_NO_DATA)
pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_NONE;
else
- AssertMsgFailed(("Invalid data direction type %d\n", pTaskState->CommandControlBlockGuest.uDataDirection));
+ AssertMsgFailed(("Invalid data direction type %d\n", pTaskState->CommandControlBlockGuest.c.uDataDirection));
- pTaskState->PDMScsiRequest.cbCDB = pTaskState->CommandControlBlockGuest.cbCDB;
- pTaskState->PDMScsiRequest.pbCDB = pTaskState->CommandControlBlockGuest.aCDB;
+ pTaskState->PDMScsiRequest.cbCDB = pTaskState->CommandControlBlockGuest.c.cbCDB;
+ pTaskState->PDMScsiRequest.pbCDB = pTaskState->CommandControlBlockGuest.c.abCDB;
if (pTaskState->DataSeg.cbSeg)
{
pTaskState->PDMScsiRequest.cbScatterGather = pTaskState->DataSeg.cbSeg;
@@ -2301,7 +2954,7 @@ static int buslogicDeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTAT
pTaskState->PDMScsiRequest.cScatterGatherEntries = 0;
pTaskState->PDMScsiRequest.paScatterGatherHead = NULL;
}
- pTaskState->PDMScsiRequest.cbSenseBuffer = pTaskState->CommandControlBlockGuest.cbSenseData;
+ pTaskState->PDMScsiRequest.cbSenseBuffer = buslogicR3ConvertSenseBufferLength(pTaskState->CommandControlBlockGuest.c.cbSenseData);
pTaskState->PDMScsiRequest.pbSenseBuffer = pTaskState->pbSenseBuffer;
pTaskState->PDMScsiRequest.pvUser = pTaskState;
@@ -2313,13 +2966,67 @@ static int buslogicDeviceSCSIRequestSetup(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTAT
return rc;
}
+static int buslogicR3DeviceSCSIRequestAbort(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState)
+{
+ int rc = VINF_SUCCESS;
+ uint8_t uTargetIdCCB;
+ PBUSLOGICDEVICE pTargetDevice;
+ RTGCPHYS GCPhysAddrCCB = (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB;
+
+ PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrCCB,
+ &pTaskState->CommandControlBlockGuest, sizeof(CCB32));
+
+ uTargetIdCCB = pTaskState->fIs24Bit ? pTaskState->CommandControlBlockGuest.o.uTargetId : pTaskState->CommandControlBlockGuest.n.uTargetId;
+ pTargetDevice = &pBusLogic->aDeviceStates[uTargetIdCCB];
+ pTaskState->CTX_SUFF(pTargetDevice) = pTargetDevice;
+
+ buslogicR3SendIncomingMailbox(pBusLogic, pTaskState,
+ BUSLOGIC_MAILBOX_INCOMING_ADAPTER_STATUS_ABORT_QUEUE_GENERATED,
+ BUSLOGIC_MAILBOX_INCOMING_DEVICE_STATUS_OPERATION_GOOD,
+ BUSLOGIC_MAILBOX_INCOMING_COMPLETION_ABORTED_NOT_FOUND);
+
+ RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
+
+ return rc;
+}
+
+/**
+ * Read a mailbox from guest memory. Convert 24-bit mailboxes to
+ * 32-bit format.
+ *
+ * @returns Mailbox guest physical address.
+ * @param pBusLogic Pointer to the BusLogic instance data.
+ * @param pTaskStat Pointer to the task state being set up.
+ */
+static RTGCPHYS buslogicR3ReadOutgoingMailbox(PBUSLOGIC pBusLogic, PBUSLOGICTASKSTATE pTaskState)
+{
+ RTGCPHYS GCMailbox;
+
+ if (pBusLogic->fMbxIs24Bit)
+ {
+ Mailbox24 Mbx24;
+
+ GCMailbox = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox24));
+ PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCMailbox, &Mbx24, sizeof(Mailbox24));
+ pTaskState->MailboxGuest.u32PhysAddrCCB = ADDR_TO_U32(Mbx24.aPhysAddrCCB);
+ pTaskState->MailboxGuest.u.out.uActionCode = Mbx24.uCmdState;
+ }
+ else
+ {
+ GCMailbox = pBusLogic->GCPhysAddrMailboxOutgoingBase + (pBusLogic->uMailboxOutgoingPositionCurrent * sizeof(Mailbox32));
+ PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCMailbox, &pTaskState->MailboxGuest, sizeof(Mailbox32));
+ }
+
+ return GCMailbox;
+}
+
/**
* Read mailbox from the guest and execute command.
*
* @returns VBox status code.
* @param pBusLogic Pointer to the BusLogic instance data.
*/
-static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic)
+static int buslogicR3ProcessMailboxNext(PBUSLOGIC pBusLogic)
{
PBUSLOGICTASKSTATE pTaskState = NULL;
RTGCPHYS GCPhysAddrMailboxCurrent;
@@ -2328,7 +3035,9 @@ static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic)
rc = RTMemCacheAllocEx(pBusLogic->hTaskCache, (void **)&pTaskState);
AssertMsgReturn(RT_SUCCESS(rc) && (pTaskState != NULL), ("Failed to get task state from cache\n"), rc);
- pTaskState->fBIOS = false;
+ pTaskState->fBIOS = false;
+ pTaskState->fIs24Bit = pBusLogic->fMbxIs24Bit;
+ pTaskState->cbSGEntry = pBusLogic->fMbxIs24Bit ? sizeof(SGE24) : sizeof(SGE32);
if (!pBusLogic->fStrictRoundRobinMode)
{
@@ -2338,23 +3047,17 @@ static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic)
do
{
/* Fetch mailbox from guest memory. */
- GCPhysAddrMailboxCurrent = buslogicOutgoingMailboxGetGCPhys(pBusLogic);
-
- PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent,
- &pTaskState->MailboxGuest, sizeof(Mailbox));
+ GCPhysAddrMailboxCurrent = buslogicR3ReadOutgoingMailbox(pBusLogic,pTaskState);
/* Check the next mailbox. */
- buslogicOutgoingMailboxAdvance(pBusLogic);
+ buslogicR3OutgoingMailboxAdvance(pBusLogic);
} while ( pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE
&& uMailboxPosCur != pBusLogic->uMailboxOutgoingPositionCurrent);
}
else
{
/* Fetch mailbox from guest memory. */
- GCPhysAddrMailboxCurrent = buslogicOutgoingMailboxGetGCPhys(pBusLogic);
-
- PDMDevHlpPhysRead(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent,
- &pTaskState->MailboxGuest, sizeof(Mailbox));
+ GCPhysAddrMailboxCurrent = buslogicR3ReadOutgoingMailbox(pBusLogic,pTaskState);
}
/*
@@ -2370,20 +3073,22 @@ static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic)
return VERR_NO_DATA;
}
- LogFlow(("Got loaded mailbox at slot %u, CCB phys %RGp\n", pBusLogic->uMailboxOutgoingPositionCurrent, pTaskState->MailboxGuest.u32PhysAddrCCB));
-#ifdef DEBUG
- buslogicDumpMailboxInfo(&pTaskState->MailboxGuest, true);
+ LogFlow(("Got loaded mailbox at slot %u, CCB phys %RGp\n", pBusLogic->uMailboxOutgoingPositionCurrent, (RTGCPHYS)pTaskState->MailboxGuest.u32PhysAddrCCB));
+#ifdef LOG_ENABLED
+ buslogicR3DumpMailboxInfo(&pTaskState->MailboxGuest, true);
#endif
/* We got the mailbox, mark it as free in the guest. */
uint8_t uActionCode = BUSLOGIC_MAILBOX_OUTGOING_ACTION_FREE;
- PDMDevHlpPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent + RT_OFFSETOF(Mailbox, u.out.uActionCode), &uActionCode, sizeof(uActionCode));
+ unsigned uCodeOffs = pTaskState->fIs24Bit ? RT_OFFSETOF(Mailbox24, uCmdState) : RT_OFFSETOF(Mailbox32, u.out.uActionCode);
+ PDMDevHlpPCIPhysWrite(pBusLogic->CTX_SUFF(pDevIns), GCPhysAddrMailboxCurrent + uCodeOffs, &uActionCode, sizeof(uActionCode));
if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_START_COMMAND)
- rc = buslogicDeviceSCSIRequestSetup(pBusLogic, pTaskState);
+ rc = buslogicR3DeviceSCSIRequestSetup(pBusLogic, pTaskState);
else if (pTaskState->MailboxGuest.u.out.uActionCode == BUSLOGIC_MAILBOX_OUTGOING_ACTION_ABORT_COMMAND)
{
- AssertMsgFailed(("Not implemented yet\n"));
+ LogFlow(("Aborting mailbox\n"));
+ rc = buslogicR3DeviceSCSIRequestAbort(pBusLogic, pTaskState);
}
else
AssertMsgFailed(("Invalid outgoing mailbox action code %u\n", pTaskState->MailboxGuest.u.out.uActionCode));
@@ -2392,7 +3097,7 @@ static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic)
/* Advance to the next mailbox. */
if (pBusLogic->fStrictRoundRobinMode)
- buslogicOutgoingMailboxAdvance(pBusLogic);
+ buslogicR3OutgoingMailboxAdvance(pBusLogic);
return rc;
}
@@ -2406,20 +3111,20 @@ static int buslogicProcessMailboxNext(PBUSLOGIC pBusLogic)
* @param pDevIns The device instance.
* @param pItem The item to consume. Upon return this item will be freed.
*/
-static DECLCALLBACK(bool) buslogicNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
+static DECLCALLBACK(bool) buslogicR3NotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
{
PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
/* Reset notification send flag now. */
Assert(pBusLogic->fNotificationSend);
ASMAtomicXchgBool(&pBusLogic->fNotificationSend, false);
- ASMAtomicXchgU32(&pBusLogic->cMailboxesReady, 0); /* @todo: Actually not required anymore but to stay compatible with older saved states. */
+ ASMAtomicXchgU32(&pBusLogic->cMailboxesReady, 0); /** @todo Actually not required anymore but to stay compatible with older saved states. */
/* Process mailboxes. */
int rc;
do
{
- rc = buslogicProcessMailboxNext(pBusLogic);
+ rc = buslogicR3ProcessMailboxNext(pBusLogic);
AssertMsg(RT_SUCCESS(rc) || rc == VERR_NO_DATA, ("Processing mailbox failed rc=%Rrc\n", rc));
} while (RT_SUCCESS(rc));
@@ -2433,7 +3138,7 @@ static DECLCALLBACK(bool) buslogicNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQU
* @returns nothing.
* @param pThis The BusLogic device instance.
*/
-static void buslogicKick(PBUSLOGIC pThis)
+static void buslogicR3Kick(PBUSLOGIC pThis)
{
if (pThis->fRedo)
{
@@ -2442,7 +3147,7 @@ static void buslogicKick(PBUSLOGIC pThis)
{
/* The BIOS had a request active when we got suspended. Resume it. */
- int rc = buslogicPrepareBIOSSCSIRequest(pThis);
+ int rc = buslogicR3PrepareBIOSSCSIRequest(pThis);
AssertRC(rc);
}
else
@@ -2456,7 +3161,7 @@ static void buslogicKick(PBUSLOGIC pThis)
{
PBUSLOGICTASKSTATE pCur = pTaskState;
- int rc = buslogicDeviceSCSIRequestSetup(pThis, pCur);
+ int rc = buslogicR3DeviceSCSIRequestSetup(pThis, pCur);
AssertRC(rc);
pTaskState = pTaskState->pRedoNext;
@@ -2465,7 +3170,8 @@ static void buslogicKick(PBUSLOGIC pThis)
}
}
-static DECLCALLBACK(int) buslogicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+/** @callback_method_impl{FNSSMDEVLIVEEXEC} */
+static DECLCALLBACK(int) buslogicR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
{
PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
@@ -2476,7 +3182,8 @@ static DECLCALLBACK(int) buslogicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
return VINF_SSM_DONT_CALL_AGAIN;
}
-static DECLCALLBACK(int) buslogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
+static DECLCALLBACK(int) buslogicR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
@@ -2504,8 +3211,9 @@ static DECLCALLBACK(int) buslogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
SSMR3PutU8 (pSSM, pBusLogic->iReply);
SSMR3PutU8 (pSSM, pBusLogic->cbReplyParametersLeft);
SSMR3PutBool (pSSM, pBusLogic->fIRQEnabled);
- SSMR3PutBool (pSSM, pBusLogic->fISAEnabled);
+ SSMR3PutU8 (pSSM, pBusLogic->uISABaseCode);
SSMR3PutU32 (pSSM, pBusLogic->cMailbox);
+ SSMR3PutBool (pSSM, pBusLogic->fMbxIs24Bit);
SSMR3PutGCPhys(pSSM, pBusLogic->GCPhysAddrMailboxOutgoingBase);
SSMR3PutU32 (pSSM, pBusLogic->uMailboxOutgoingPositionCurrent);
SSMR3PutU32 (pSSM, pBusLogic->cMailboxesReady);
@@ -2519,14 +3227,14 @@ static DECLCALLBACK(int) buslogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.uTargetDevice);
SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.uTxDir);
SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.cbCDB);
- SSMR3PutMem (pSSM, pBusLogic->VBoxSCSI.aCDB, sizeof(pBusLogic->VBoxSCSI.aCDB));
+ SSMR3PutMem (pSSM, pBusLogic->VBoxSCSI.abCDB, sizeof(pBusLogic->VBoxSCSI.abCDB));
SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.iCDB);
SSMR3PutU32 (pSSM, pBusLogic->VBoxSCSI.cbBuf);
SSMR3PutU32 (pSSM, pBusLogic->VBoxSCSI.iBuf);
SSMR3PutBool (pSSM, pBusLogic->VBoxSCSI.fBusy);
SSMR3PutU8 (pSSM, pBusLogic->VBoxSCSI.enmState);
if (pBusLogic->VBoxSCSI.cbBuf)
- SSMR3PutMem(pSSM, pBusLogic->VBoxSCSI.pBuf, pBusLogic->VBoxSCSI.cbBuf);
+ SSMR3PutMem(pSSM, pBusLogic->VBoxSCSI.pbBuf, pBusLogic->VBoxSCSI.cbBuf);
/*
* Save the physical addresses of the command control blocks of still pending tasks.
@@ -2558,15 +3266,18 @@ static DECLCALLBACK(int) buslogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
return SSMR3PutU32(pSSM, ~0);
}
-static DECLCALLBACK(int) buslogicLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+/** @callback_method_impl{FNSSMDEVLOADDONE} */
+static DECLCALLBACK(int) buslogicR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
- buslogicKick(pThis);
+ buslogicR3RegisterISARange(pThis, pThis->uISABaseCode);
+ buslogicR3Kick(pThis);
return VINF_SUCCESS;
}
-static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+/** @callback_method_impl{FNSSMDEVLOADEXEC} */
+static DECLCALLBACK(int) buslogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
int rc = VINF_SUCCESS;
@@ -2601,7 +3312,10 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->regGeometry);
SSMR3GetMem (pSSM, &pBusLogic->LocalRam, sizeof(pBusLogic->LocalRam));
SSMR3GetU8 (pSSM, &pBusLogic->uOperationCode);
- SSMR3GetMem (pSSM, &pBusLogic->aCommandBuffer, sizeof(pBusLogic->aCommandBuffer));
+ if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_PRE_CMDBUF_RESIZE)
+ SSMR3GetMem (pSSM, &pBusLogic->aCommandBuffer, sizeof(pBusLogic->aCommandBuffer));
+ else
+ SSMR3GetMem (pSSM, &pBusLogic->aCommandBuffer, BUSLOGIC_COMMAND_SIZE_OLD);
SSMR3GetU8 (pSSM, &pBusLogic->iParameter);
SSMR3GetU8 (pSSM, &pBusLogic->cbCommandParametersLeft);
SSMR3GetBool (pSSM, &pBusLogic->fUseLocalRam);
@@ -2609,8 +3323,10 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
SSMR3GetU8 (pSSM, &pBusLogic->iReply);
SSMR3GetU8 (pSSM, &pBusLogic->cbReplyParametersLeft);
SSMR3GetBool (pSSM, &pBusLogic->fIRQEnabled);
- SSMR3GetBool (pSSM, &pBusLogic->fISAEnabled);
+ SSMR3GetU8 (pSSM, &pBusLogic->uISABaseCode);
SSMR3GetU32 (pSSM, &pBusLogic->cMailbox);
+ if (uVersion > BUSLOGIC_SAVED_STATE_MINOR_PRE_24BIT_MBOX)
+ SSMR3GetBool (pSSM, &pBusLogic->fMbxIs24Bit);
SSMR3GetGCPhys(pSSM, &pBusLogic->GCPhysAddrMailboxOutgoingBase);
SSMR3GetU32 (pSSM, &pBusLogic->uMailboxOutgoingPositionCurrent);
SSMR3GetU32 (pSSM, (uint32_t *)&pBusLogic->cMailboxesReady);
@@ -2624,7 +3340,7 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.uTargetDevice);
SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.uTxDir);
SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.cbCDB);
- SSMR3GetMem (pSSM, pBusLogic->VBoxSCSI.aCDB, sizeof(pBusLogic->VBoxSCSI.aCDB));
+ SSMR3GetMem (pSSM, pBusLogic->VBoxSCSI.abCDB, sizeof(pBusLogic->VBoxSCSI.abCDB));
SSMR3GetU8 (pSSM, &pBusLogic->VBoxSCSI.iCDB);
SSMR3GetU32 (pSSM, &pBusLogic->VBoxSCSI.cbBuf);
SSMR3GetU32 (pSSM, &pBusLogic->VBoxSCSI.iBuf);
@@ -2632,14 +3348,14 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
SSMR3GetU8 (pSSM, (uint8_t *)&pBusLogic->VBoxSCSI.enmState);
if (pBusLogic->VBoxSCSI.cbBuf)
{
- pBusLogic->VBoxSCSI.pBuf = (uint8_t *)RTMemAllocZ(pBusLogic->VBoxSCSI.cbBuf);
- if (!pBusLogic->VBoxSCSI.pBuf)
+ pBusLogic->VBoxSCSI.pbBuf = (uint8_t *)RTMemAllocZ(pBusLogic->VBoxSCSI.cbBuf);
+ if (!pBusLogic->VBoxSCSI.pbBuf)
{
LogRel(("BusLogic: Out of memory during restore.\n"));
return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY,
N_("BusLogic: Out of memory during restore\n"));
}
- SSMR3GetMem(pSSM, pBusLogic->VBoxSCSI.pBuf, pBusLogic->VBoxSCSI.cbBuf);
+ SSMR3GetMem(pSSM, pBusLogic->VBoxSCSI.pbBuf, pBusLogic->VBoxSCSI.cbBuf);
}
if (pBusLogic->VBoxSCSI.fBusy)
@@ -2689,7 +3405,7 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
}
/**
- * Gets the pointer to the status LED of a device - called from the SCSi driver.
+ * Gets the pointer to the status LED of a device - called from the SCSI driver.
*
* @returns VBox status code.
* @param pInterface Pointer to the interface structure containing the called function pointer.
@@ -2697,7 +3413,7 @@ static DECLCALLBACK(int) buslogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
* doesn't know about other LUN's.
* @param ppLed Where to store the LED pointer.
*/
-static DECLCALLBACK(int) buslogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+static DECLCALLBACK(int) buslogicR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
PBUSLOGICDEVICE pDevice = PDMILEDPORTS_2_PBUSLOGICDEVICE(pInterface);
if (iLUN == 0)
@@ -2712,7 +3428,7 @@ static DECLCALLBACK(int) buslogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface,
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
-static DECLCALLBACK(void *) buslogicDeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+static DECLCALLBACK(void *) buslogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
PBUSLOGICDEVICE pDevice = PDMIBASE_2_PBUSLOGICDEVICE(pInterface);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase);
@@ -2729,7 +3445,7 @@ static DECLCALLBACK(void *) buslogicDeviceQueryInterface(PPDMIBASE pInterface, c
* @param iLUN The unit which status LED we desire.
* @param ppLed Where to store the LED pointer.
*/
-static DECLCALLBACK(int) buslogicStatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+static DECLCALLBACK(int) buslogicR3StatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
PBUSLOGIC pBusLogic = PDMILEDPORTS_2_PBUSLOGIC(pInterface);
if (iLUN < BUSLOGIC_MAX_DEVICES)
@@ -2744,7 +3460,7 @@ static DECLCALLBACK(int) buslogicStatusQueryStatusLed(PPDMILEDPORTS pInterface,
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
-static DECLCALLBACK(void *) buslogicStatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+static DECLCALLBACK(void *) buslogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
PBUSLOGIC pThis = PDMIBASE_2_PBUSLOGIC(pInterface);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
@@ -2752,12 +3468,117 @@ static DECLCALLBACK(void *) buslogicStatusQueryInterface(PPDMIBASE pInterface, c
return NULL;
}
+/**
+ * BusLogic debugger info callback.
+ *
+ * @param pDevIns The device instance.
+ * @param pHlp The output helpers.
+ * @param pszArgs The arguments.
+ */
+static DECLCALLBACK(void) buslogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
+ unsigned i;
+ bool fVerbose = false;
+
+ /* Parse arguments. */
+ if (pszArgs)
+ fVerbose = strstr(pszArgs, "verbose") != NULL;
+
+ /* Show basic information. */
+ pHlp->pfnPrintf(pHlp,
+ "%s#%d: PCI I/O=%RTiop ISA I/O=%RTiop MMIO=%RGp IRQ=%u GC=%RTbool R0=%RTbool\n",
+ pDevIns->pReg->szName,
+ pDevIns->iInstance,
+ pThis->IOPortBase, pThis->IOISABase, pThis->MMIOBase,
+ PCIDevGetInterruptLine(&pThis->dev),
+ !!pThis->fGCEnabled, !!pThis->fR0Enabled);
+
+ /* Print mailbox state. */
+ if (pThis->regStatus & BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED)
+ pHlp->pfnPrintf(pHlp, "Mailbox not initialized\n");
+ else
+ pHlp->pfnPrintf(pHlp, "%u-bit mailbox with %u entries at %RGp\n",
+ pThis->fMbxIs24Bit ? 24 : 32, pThis->cMailbox,
+ pThis->GCPhysAddrMailboxOutgoingBase);
+
+ /* Print register contents. */
+ pHlp->pfnPrintf(pHlp, "Registers: STAT=%02x INTR=%02x GEOM=%02x\n",
+ pThis->regStatus, pThis->regInterrupt, pThis->regGeometry);
+
+ /* Print the current command, if any. */
+ if (pThis->uOperationCode != 0xff )
+ pHlp->pfnPrintf(pHlp, "Current command: %02X\n", pThis->uOperationCode);
+
+ if (fVerbose && (pThis->regStatus & BUSLOGIC_REGISTER_STATUS_INITIALIZATION_REQUIRED) == 0)
+ {
+ RTGCPHYS GCMailbox;
+
+ /* Dump the mailbox contents. */
+ if (pThis->fMbxIs24Bit)
+ {
+ Mailbox24 Mbx24;
+
+ /* Outgoing mailbox, 24-bit format. */
+ GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase;
+ pHlp->pfnPrintf(pHlp, " Outgoing mailbox entries (24-bit) at %06X:\n", GCMailbox);
+ for (i = 0; i < pThis->cMailbox; ++i)
+ {
+ PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx24, sizeof(Mailbox24));
+ pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %06X action code %02X", i, ADDR_TO_U32(Mbx24.aPhysAddrCCB), Mbx24.uCmdState);
+ pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxOutgoingPositionCurrent == i ? " *" : "");
+ GCMailbox += sizeof(Mailbox24);
+ }
+
+ /* Incoming mailbox, 24-bit format. */
+ GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase + (pThis->cMailbox * sizeof(Mailbox24));
+ pHlp->pfnPrintf(pHlp, " Incoming mailbox entries (24-bit) at %06X:\n", GCMailbox);
+ for (i = 0; i < pThis->cMailbox; ++i)
+ {
+ PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx24, sizeof(Mailbox24));
+ pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %06X completion code %02X", i, ADDR_TO_U32(Mbx24.aPhysAddrCCB), Mbx24.uCmdState);
+ pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxIncomingPositionCurrent == i ? " *" : "");
+ GCMailbox += sizeof(Mailbox24);
+ }
+
+ }
+ else
+ {
+ Mailbox32 Mbx32;
+
+ /* Outgoing mailbox, 32-bit format. */
+ GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase;
+ pHlp->pfnPrintf(pHlp, " Outgoing mailbox entries (32-bit) at %08X:\n", (uint32_t)GCMailbox);
+ for (i = 0; i < pThis->cMailbox; ++i)
+ {
+ PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx32, sizeof(Mailbox32));
+ pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %08X action code %02X", i, Mbx32.u32PhysAddrCCB, Mbx32.u.out.uActionCode);
+ pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxOutgoingPositionCurrent == i ? " *" : "");
+ GCMailbox += sizeof(Mailbox32);
+ }
+
+ /* Incoming mailbox, 32-bit format. */
+ GCMailbox = pThis->GCPhysAddrMailboxOutgoingBase + (pThis->cMailbox * sizeof(Mailbox32));
+ pHlp->pfnPrintf(pHlp, " Outgoing mailbox entries (32-bit) at %08X:\n", (uint32_t)GCMailbox);
+ for (i = 0; i < pThis->cMailbox; ++i)
+ {
+ PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCMailbox, &Mbx32, sizeof(Mailbox32));
+ pHlp->pfnPrintf(pHlp, " slot %03d: CCB at %08X completion code %02X BTSTAT %02X SDSTAT %02X", i,
+ Mbx32.u32PhysAddrCCB, Mbx32.u.in.uCompletionCode, Mbx32.u.in.uHostAdapterStatus, Mbx32.u.in.uTargetDeviceStatus);
+ pHlp->pfnPrintf(pHlp, "%s\n", pThis->uMailboxOutgoingPositionCurrent == i ? " *" : "");
+ GCMailbox += sizeof(Mailbox32);
+ }
+
+ }
+ }
+}
+
/* -=-=-=-=- Helper -=-=-=-=- */
/**
* Checks if all asynchronous I/O is finished.
*
- * Used by buslogicReset, buslogicSuspend and buslogicPowerOff.
+ * Used by buslogicR3Reset, buslogicR3Suspend and buslogicR3PowerOff.
*
* @returns true if quiesced, false if busy.
* @param pDevIns The device instance.
@@ -2849,9 +3670,9 @@ static void buslogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns, bool fPowerOff)
*
* @param pDevIns The device instance data.
*/
-static DECLCALLBACK(void) buslogicSuspend(PPDMDEVINS pDevIns)
+static DECLCALLBACK(void) buslogicR3Suspend(PPDMDEVINS pDevIns)
{
- Log(("buslogicSuspend\n"));
+ Log(("buslogicR3Suspend\n"));
buslogicR3SuspendOrPowerOff(pDevIns, false /* fPoweroff */);
}
@@ -2860,11 +3681,11 @@ static DECLCALLBACK(void) buslogicSuspend(PPDMDEVINS pDevIns)
*
* @param pDevIns The device instance data.
*/
-static DECLCALLBACK(void) buslogicResume(PPDMDEVINS pDevIns)
+static DECLCALLBACK(void) buslogicR3Resume(PPDMDEVINS pDevIns)
{
- Log(("buslogicResume\n"));
+ Log(("buslogicR3Resume\n"));
PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
- buslogicKick(pThis);
+ buslogicR3Kick(pThis);
}
@@ -2878,7 +3699,7 @@ static DECLCALLBACK(void) buslogicResume(PPDMDEVINS pDevIns)
* @param iLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
-static DECLCALLBACK(void) buslogicDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+static DECLCALLBACK(void) buslogicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[iLUN];
@@ -2906,7 +3727,7 @@ static DECLCALLBACK(void) buslogicDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint
* @param iLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
-static DECLCALLBACK(int) buslogicAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+static DECLCALLBACK(int) buslogicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[iLUN];
@@ -2958,14 +3779,14 @@ static DECLCALLBACK(bool) buslogicR3IsAsyncResetDone(PPDMDEVINS pDevIns)
return false;
ASMAtomicWriteBool(&pThis->fSignalIdle, false);
- buslogicHwReset(pThis);
+ buslogicR3HwReset(pThis, true);
return true;
}
/**
* @copydoc FNPDMDEVRESET
*/
-static DECLCALLBACK(void) buslogicReset(PPDMDEVINS pDevIns)
+static DECLCALLBACK(void) buslogicR3Reset(PPDMDEVINS pDevIns)
{
PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
@@ -2975,21 +3796,20 @@ static DECLCALLBACK(void) buslogicReset(PPDMDEVINS pDevIns)
else
{
ASMAtomicWriteBool(&pThis->fSignalIdle, false);
- buslogicHwReset(pThis);
+ buslogicR3HwReset(pThis, true);
}
}
-static DECLCALLBACK(void) buslogicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+static DECLCALLBACK(void) buslogicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
{
- uint32_t i;
- PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
+ PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
- pBusLogic->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
- pBusLogic->pNotifierQueueRC = PDMQueueRCPtr(pBusLogic->pNotifierQueueR3);
+ pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pThis->pNotifierQueueRC = PDMQueueRCPtr(pThis->pNotifierQueueR3);
- for (i = 0; i < BUSLOGIC_MAX_DEVICES; i++)
+ for (uint32_t i = 0; i < BUSLOGIC_MAX_DEVICES; i++)
{
- PBUSLOGICDEVICE pDevice = &pBusLogic->aDeviceStates[i];
+ PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[i];
pDevice->pBusLogicRC = PDMINS_2_DATA_RCPTR(pDevIns);
}
@@ -3001,9 +3821,9 @@ static DECLCALLBACK(void) buslogicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDel
*
* @param pDevIns Pointer to the device instance
*/
-static DECLCALLBACK(void) buslogicPowerOff(PPDMDEVINS pDevIns)
+static DECLCALLBACK(void) buslogicR3PowerOff(PPDMDEVINS pDevIns)
{
- Log(("buslogicPowerOff\n"));
+ Log(("buslogicR3PowerOff\n"));
buslogicR3SuspendOrPowerOff(pDevIns, true /* fPoweroff */);
}
@@ -3015,7 +3835,7 @@ static DECLCALLBACK(void) buslogicPowerOff(PPDMDEVINS pDevIns)
*
* @param pDevIns The device instance data.
*/
-static DECLCALLBACK(int) buslogicDestruct(PPDMDEVINS pDevIns)
+static DECLCALLBACK(int) buslogicR3Destruct(PPDMDEVINS pDevIns)
{
PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
@@ -3054,20 +3874,46 @@ static DECLCALLBACK(int) buslogicDestruct(PPDMDEVINS pDevIns)
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
-static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+static DECLCALLBACK(int) buslogicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
int rc = VINF_SUCCESS;
bool fBootable = true;
+ char achISACompat[16];
PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
/*
+ * Init instance data (do early because of constructor).
+ */
+ pThis->hTaskCache = NIL_RTMEMCACHE;
+ pThis->pDevInsR3 = pDevIns;
+ pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pThis->IBase.pfnQueryInterface = buslogicR3StatusQueryInterface;
+ pThis->ILeds.pfnQueryStatusLed = buslogicR3StatusQueryStatusLed;
+
+ PCIDevSetVendorId (&pThis->dev, 0x104b); /* BusLogic */
+ PCIDevSetDeviceId (&pThis->dev, 0x1040); /* BT-958 */
+ PCIDevSetCommand (&pThis->dev, 0x0003);
+ PCIDevSetRevisionId (&pThis->dev, 0x01);
+ PCIDevSetClassProg (&pThis->dev, 0x00); /* SCSI */
+ PCIDevSetClassSub (&pThis->dev, 0x00); /* SCSI */
+ PCIDevSetClassBase (&pThis->dev, 0x01); /* Mass storage */
+ PCIDevSetBaseAddress (&pThis->dev, 0, true /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000);
+ PCIDevSetBaseAddress (&pThis->dev, 1, false /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000);
+ PCIDevSetSubSystemVendorId(&pThis->dev, 0x104b);
+ PCIDevSetSubSystemId (&pThis->dev, 0x1040);
+ PCIDevSetInterruptLine (&pThis->dev, 0x00);
+ PCIDevSetInterruptPin (&pThis->dev, 0x01);
+
+ /*
* Validate and read configuration.
*/
if (!CFGMR3AreValuesValid(pCfg,
"GCEnabled\0"
"R0Enabled\0"
- "Bootable\0"))
+ "Bootable\0"
+ "ISACompat\0"))
return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("BusLogic configuration error: unknown option specified"));
@@ -3088,52 +3934,58 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
N_("BusLogic configuration error: failed to read Bootable as boolean"));
Log(("%s: fBootable=%RTbool\n", __FUNCTION__, fBootable));
- pThis->pDevInsR3 = pDevIns;
- pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
- pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
- pThis->IBase.pfnQueryInterface = buslogicStatusQueryInterface;
- pThis->ILeds.pfnQueryStatusLed = buslogicStatusQueryStatusLed;
-
- PCIDevSetVendorId (&pThis->dev, 0x104b); /* BusLogic */
- PCIDevSetDeviceId (&pThis->dev, 0x1040); /* BT-958 */
- PCIDevSetCommand (&pThis->dev, 0x0003);
- PCIDevSetRevisionId (&pThis->dev, 0x01);
- PCIDevSetClassProg (&pThis->dev, 0x00); /* SCSI */
- PCIDevSetClassSub (&pThis->dev, 0x00); /* SCSI */
- PCIDevSetClassBase (&pThis->dev, 0x01); /* Mass storage */
- PCIDevSetBaseAddress (&pThis->dev, 0, true /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000);
- PCIDevSetBaseAddress (&pThis->dev, 1, false /*IO*/, false /*Pref*/, false /*64-bit*/, 0x00000000);
- PCIDevSetSubSystemVendorId(&pThis->dev, 0x104b);
- PCIDevSetSubSystemId (&pThis->dev, 0x1040);
- PCIDevSetInterruptLine (&pThis->dev, 0x00);
- PCIDevSetInterruptPin (&pThis->dev, 0x01);
+ /* Only the first instance defaults to having the ISA compatibility ports enabled. */
+ if (iInstance == 0)
+ rc = CFGMR3QueryStringDef(pCfg, "ISACompat", achISACompat, sizeof(achISACompat), "Alternate");
+ else
+ rc = CFGMR3QueryStringDef(pCfg, "ISACompat", achISACompat, sizeof(achISACompat), "Disabled");
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("BusLogic configuration error: failed to read ISACompat as string"));
+ Log(("%s: ISACompat=%s\n", __FUNCTION__, achISACompat));
+
+ /* Grok the ISACompat setting. */
+ if (!strcmp(achISACompat, "Disabled"))
+ pThis->uDefaultISABaseCode = ISA_BASE_DISABLED;
+ else if (!strcmp(achISACompat, "Primary"))
+ pThis->uDefaultISABaseCode = 0; /* I/O base at 330h. */
+ else if (!strcmp(achISACompat, "Alternate"))
+ pThis->uDefaultISABaseCode = 1; /* I/O base at 334h. */
+ else
+ return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
+ N_("BusLogic configuration error: invalid ISACompat setting"));
/*
- * Register the PCI device, it's I/O regions.
+ * Register the PCI device and its I/O regions.
*/
- rc = PDMDevHlpPCIRegister (pDevIns, &pThis->dev);
+ rc = PDMDevHlpPCIRegister(pDevIns, &pThis->dev);
if (RT_FAILURE(rc))
return rc;
- rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 32, PCI_ADDRESS_SPACE_IO, buslogicMMIOMap);
+ rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 32, PCI_ADDRESS_SPACE_IO, buslogicR3MmioMap);
if (RT_FAILURE(rc))
return rc;
- rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, 32, PCI_ADDRESS_SPACE_MEM, buslogicMMIOMap);
+ rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, 32, PCI_ADDRESS_SPACE_MEM, buslogicR3MmioMap);
if (RT_FAILURE(rc))
return rc;
if (fBootable)
{
- /* Register I/O port space in ISA region for BIOS access. */
+ /* Register I/O port space for BIOS access. */
rc = PDMDevHlpIOPortRegister(pDevIns, BUSLOGIC_BIOS_IO_PORT, 4, NULL,
- buslogicIsaIOPortWrite, buslogicIsaIOPortRead,
- buslogicIsaIOPortWriteStr, buslogicIsaIOPortReadStr,
+ buslogicR3BiosIoPortWrite, buslogicR3BiosIoPortRead,
+ buslogicR3BiosIoPortWriteStr, buslogicR3BiosIoPortReadStr,
"BusLogic BIOS");
if (RT_FAILURE(rc))
- return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register legacy I/O handlers"));
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register BIOS I/O handlers"));
}
+ /* Set up the compatibility I/O range. */
+ rc = buslogicR3RegisterISARange(pThis, pThis->uDefaultISABaseCode);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register ISA I/O handlers"));
+
/* Initialize task cache. */
rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(BUSLOGICTASKSTATE), 0, UINT32_MAX,
NULL, NULL, NULL, 0);
@@ -3143,7 +3995,7 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
/* Initialize task queue. */
rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 5, 0,
- buslogicNotifyQueueConsumer, true, "BusLogicTask", &pThis->pNotifierQueueR3);
+ buslogicR3NotifyQueueConsumer, true, "BusLogicTask", &pThis->pNotifierQueueR3);
if (RT_FAILURE(rc))
return rc;
pThis->pNotifierQueueR0 = PDMQueueR0Ptr(pThis->pNotifierQueueR3);
@@ -3151,8 +4003,7 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIntr, RT_SRC_POS, "BusLogic-Intr#%u", pDevIns->iInstance);
if (RT_FAILURE(rc))
- return PDMDEV_SET_ERROR(pDevIns, rc,
- N_("BusLogic: cannot create critical section"));
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic: cannot create critical section"));
/* Initialize per device state. */
for (unsigned i = 0; i < RT_ELEMENTS(pThis->aDeviceStates); i++)
@@ -3160,7 +4011,7 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
char szName[24];
PBUSLOGICDEVICE pDevice = &pThis->aDeviceStates[i];
- RTStrPrintf(szName, sizeof(szName), "Device%d", i);
+ RTStrPrintf(szName, sizeof(szName), "Device%u", i);
/* Initialize static parts of the device. */
pDevice->iLUN = i;
@@ -3168,10 +4019,10 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
pDevice->pBusLogicR0 = PDMINS_2_DATA_R0PTR(pDevIns);
pDevice->pBusLogicRC = PDMINS_2_DATA_RCPTR(pDevIns);
pDevice->Led.u32Magic = PDMLED_MAGIC;
- pDevice->IBase.pfnQueryInterface = buslogicDeviceQueryInterface;
- pDevice->ISCSIPort.pfnSCSIRequestCompleted = buslogicDeviceSCSIRequestCompleted;
- pDevice->ISCSIPort.pfnQueryDeviceLocation = buslogicQueryDeviceLocation;
- pDevice->ILed.pfnQueryStatusLed = buslogicDeviceQueryStatusLed;
+ pDevice->IBase.pfnQueryInterface = buslogicR3DeviceQueryInterface;
+ pDevice->ISCSIPort.pfnSCSIRequestCompleted = buslogicR3DeviceSCSIRequestCompleted;
+ pDevice->ISCSIPort.pfnQueryDeviceLocation = buslogicR3QueryDeviceLocation;
+ pDevice->ILed.pfnQueryStatusLed = buslogicR3DeviceQueryStatusLed;
/* Attach SCSI driver. */
rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, szName);
@@ -3211,13 +4062,20 @@ static DECLCALLBACK(int) buslogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
}
rc = PDMDevHlpSSMRegisterEx(pDevIns, BUSLOGIC_SAVED_STATE_MINOR_VERSION, sizeof(*pThis), NULL,
- NULL, buslogicLiveExec, NULL,
- NULL, buslogicSaveExec, NULL,
- NULL, buslogicLoadExec, buslogicLoadDone);
+ NULL, buslogicR3LiveExec, NULL,
+ NULL, buslogicR3SaveExec, NULL,
+ NULL, buslogicR3LoadExec, buslogicR3LoadDone);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register save state handlers"));
- rc = buslogicHwReset(pThis);
+ /*
+ * Register the debugger info callback.
+ */
+ char szTmp[128];
+ RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance);
+ PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "BusLogic HBA info", buslogicR3Info);
+
+ rc = buslogicR3HwReset(pThis, true);
AssertMsgRC(rc, ("hardware reset of BusLogic host adapter failed rc=%Rrc\n", rc));
return rc;
@@ -3249,31 +4107,31 @@ const PDMDEVREG g_DeviceBusLogic =
/* cbInstance */
sizeof(BUSLOGIC),
/* pfnConstruct */
- buslogicConstruct,
+ buslogicR3Construct,
/* pfnDestruct */
- buslogicDestruct,
+ buslogicR3Destruct,
/* pfnRelocate */
- buslogicRelocate,
- /* pfnIOCtl */
+ buslogicR3Relocate,
+ /* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
- buslogicReset,
+ buslogicR3Reset,
/* pfnSuspend */
- buslogicSuspend,
+ buslogicR3Suspend,
/* pfnResume */
- buslogicResume,
+ buslogicR3Resume,
/* pfnAttach */
- buslogicAttach,
+ buslogicR3Attach,
/* pfnDetach */
- buslogicDetach,
+ buslogicR3Detach,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
- buslogicPowerOff,
+ buslogicR3PowerOff,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
@@ -3282,4 +4140,3 @@ const PDMDEVREG g_DeviceBusLogic =
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
-
diff --git a/src/VBox/Devices/Storage/fdc.c b/src/VBox/Devices/Storage/DevFdc.cpp
index 703639e3..b719d8c0 100644
--- a/src/VBox/Devices/Storage/fdc.c
+++ b/src/VBox/Devices/Storage/DevFdc.cpp
@@ -1,10 +1,10 @@
-/* $Id: fdc.c $ */
+/* $Id: DevFdc.cpp $ */
/** @file
* VBox storage devices: Floppy disk controller
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -59,12 +59,6 @@
#define MAX_FD 2
-#define PDMIBASE_2_FDRIVE(pInterface) \
- ((fdrive_t *)((uintptr_t)(pInterface) - RT_OFFSETOF(fdrive_t, IBase)))
-
-#define PDMIMOUNTNOTIFY_2_FDRIVE(p) \
- ((fdrive_t *)((uintptr_t)(p) - RT_OFFSETOF(fdrive_t, IMountNotify)))
-
/********************************************************/
/* debug Floppy devices */
@@ -127,11 +121,14 @@ typedef enum fdrive_type_t {
FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */
FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */
FDRIVE_DRV_NONE = 0x03 /* No drive connected */
+#ifdef VBOX
+ , FDRIVE_DRV_FAKE_15_6 = 0x0e /* Fake 15.6 MB drive. */
+ , FDRIVE_DRV_FAKE_63_5 = 0x0f /* Fake 63.5 MB drive. */
+#endif
} fdrive_type_t;
-typedef enum fdrive_flags_t {
- FDISK_DBL_SIDES = 0x01
-} fdrive_flags_t;
+typedef uint8_t fdrive_flags_t;
+#define FDISK_DBL_SIDES UINT8_C(0x01)
typedef enum fdrive_rate_t {
FDRIVE_RATE_500K = 0x00, /* 500 Kbps */
@@ -179,6 +176,7 @@ typedef struct fdrive_t {
uint8_t head;
uint8_t track;
uint8_t sect;
+ uint8_t ltrk; /* Logical track */
/* Media */
fdrive_flags_t flags;
uint8_t last_sect; /* Nb sector per track */
@@ -190,10 +188,43 @@ typedef struct fdrive_t {
#define NUM_SIDES(drv) (drv->flags & FDISK_DBL_SIDES ? 2 : 1)
-static void fd_init(fdrive_t *drv)
+static void fd_init(fdrive_t *drv, bool fInit)
{
/* Drive */
+#ifndef VBOX
drv->drive = FDRIVE_DRV_NONE;
+#else /* VBOX */
+ if (fInit) {
+ /* Fixate the drive type at init time if possible. */
+ if (drv->pDrvBlock) {
+ PDMBLOCKTYPE enmType = drv->pDrvBlock->pfnGetType(drv->pDrvBlock);
+ switch (enmType) {
+ case PDMBLOCKTYPE_FLOPPY_360:
+ case PDMBLOCKTYPE_FLOPPY_1_20:
+ drv->drive = FDRIVE_DRV_120;
+ break;
+ case PDMBLOCKTYPE_FLOPPY_720:
+ case PDMBLOCKTYPE_FLOPPY_1_44:
+ drv->drive = FDRIVE_DRV_144;
+ break;
+ default:
+ AssertFailed();
+ case PDMBLOCKTYPE_FLOPPY_2_88:
+ drv->drive = FDRIVE_DRV_288;
+ break;
+ case PDMBLOCKTYPE_FLOPPY_FAKE_15_6:
+ drv->drive = FDRIVE_DRV_FAKE_15_6;
+ break;
+ case PDMBLOCKTYPE_FLOPPY_FAKE_63_5:
+ drv->drive = FDRIVE_DRV_FAKE_63_5;
+ break;
+ }
+ } else {
+ drv->drive = FDRIVE_DRV_NONE;
+ }
+ } /* else: The BIOS (and others) get the drive type via the CMOS, so
+ don't change it after the VM has been constructed. */
+#endif /* VBOX */
drv->perpendicular = 0;
/* Disk */
drv->last_sect = 0;
@@ -233,7 +264,7 @@ static int fd_seek(fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect,
drv->max_track, drv->last_sect);
return 2;
}
- if (sect > drv->last_sect) {
+ if (sect > drv->last_sect || sect < 1) {
FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
head, track, sect, 1,
(drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
@@ -256,6 +287,7 @@ static int fd_seek(fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect,
drv->track = track;
drv->sect = sect;
}
+ drv->ltrk = drv->track;
return ret;
}
@@ -266,6 +298,7 @@ static void fd_recalibrate(fdrive_t *drv)
FLOPPY_DPRINTF("recalibrate\n");
drv->head = 0;
drv->track = 0;
+ drv->ltrk = 0;
drv->sect = 1;
}
@@ -273,13 +306,16 @@ static void fd_recalibrate(fdrive_t *drv)
typedef struct fd_format_t {
fdrive_type_t drive;
fdisk_type_t disk;
- uint8_t last_sect;
- uint8_t max_track;
- uint8_t max_head;
+ uint8_t last_sect; /**< Number of sectors. */
+ uint8_t max_track; /**< Number of tracks. */
+ uint8_t max_head; /**< Max head number. */
fdrive_rate_t rate;
const char *str;
} fd_format_t;
+/* Note: Low-density disks (160K/180K/320K/360K) use 250 Kbps data rate
+ * in 40-track drives, but 300 Kbps in high-capacity 80-track drives.
+ */
static fd_format_t fd_formats[] = {
/* First entry is default format */
/* 1.44 MB 3"1/2 floppy disks */
@@ -313,18 +349,69 @@ static fd_format_t fd_formats[] = {
/* 720 kB 5"1/4 floppy disks */
{ FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, FDRIVE_RATE_250K, "720 kB 5\"1/4", },
{ FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, FDRIVE_RATE_250K, "880 kB 5\"1/4", },
- /* 360 kB 5"1/4 floppy disks */
+ /* 360 kB 5"1/4 floppy disks (newer 9-sector formats) */
{ FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, FDRIVE_RATE_300K, "360 kB 5\"1/4", },
{ FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, FDRIVE_RATE_300K, "180 kB 5\"1/4", },
{ FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, FDRIVE_RATE_300K, "410 kB 5\"1/4", },
{ FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, FDRIVE_RATE_300K, "420 kB 5\"1/4", },
- /* 320 kB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, FDRIVE_RATE_250K, "320 kB 5\"1/4", },
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, FDRIVE_RATE_250K, "160 kB 5\"1/4", },
- /* 360 kB must match 5"1/4 better than 3"1/2... */
- { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, FDRIVE_RATE_250K, "360 kB 3\"1/2", },
+ /* 320 kB 5"1/4 floppy disks (old 8-sector formats) */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, FDRIVE_RATE_300K, "320 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, FDRIVE_RATE_300K, "160 kB 5\"1/4", },
+ /* 1.2 MB and low density 3"1/2 floppy 'aliases' */
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 15, 80, 1, FDRIVE_RATE_500K, " 1.2 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 40, 1, FDRIVE_RATE_300K, "360 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 40, 0, FDRIVE_RATE_300K, "180 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 8, 40, 1, FDRIVE_RATE_300K, "320 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 8, 40, 0, FDRIVE_RATE_300K, "160 kB 3\"1/2", },
+#ifdef VBOX /* For larger than real life floppy images (see DrvBlock.cpp). */
+ /* 15.6 MB fake floppy disk (just need something big). */
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_USER, 63, 255, 1, FDRIVE_RATE_1M, "15.6 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_288, 36, 80, 1, FDRIVE_RATE_1M, "2.88 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_288, 39, 80, 1, FDRIVE_RATE_1M, "3.12 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_288, 40, 80, 1, FDRIVE_RATE_1M, "3.2 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_288, 44, 80, 1, FDRIVE_RATE_1M, "3.52 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_288, 48, 80, 1, FDRIVE_RATE_1M, "3.84 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 18, 80, 1, FDRIVE_RATE_500K, "1.44 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 20, 80, 1, FDRIVE_RATE_500K, "1.6 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 21, 80, 1, FDRIVE_RATE_500K, "1.68 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 21, 82, 1, FDRIVE_RATE_500K, "1.72 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 21, 83, 1, FDRIVE_RATE_500K, "1.74 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 22, 80, 1, FDRIVE_RATE_500K, "1.76 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 23, 80, 1, FDRIVE_RATE_500K, "1.84 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_144, 24, 80, 1, FDRIVE_RATE_500K, "1.92 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 9, 80, 1, FDRIVE_RATE_250K, "720 kB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 10, 80, 1, FDRIVE_RATE_250K, "800 kB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 10, 82, 1, FDRIVE_RATE_250K, "820 kB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 10, 83, 1, FDRIVE_RATE_250K, "830 kB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 13, 80, 1, FDRIVE_RATE_250K, "1.04 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 14, 80, 1, FDRIVE_RATE_250K, "1.12 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_15_6, FDRIVE_DISK_720, 9, 80, 0, FDRIVE_RATE_250K, "360 kB 3\"1/2", },
+ /* 63.5 MB fake floppy disk (just need something big). */
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_USER, 255, 255, 1, FDRIVE_RATE_1M, "63.5 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_USER, 63, 255, 1, FDRIVE_RATE_1M, "15.6 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_288, 36, 80, 1, FDRIVE_RATE_1M, "2.88 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_288, 39, 80, 1, FDRIVE_RATE_1M, "3.12 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_288, 40, 80, 1, FDRIVE_RATE_1M, "3.2 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_288, 44, 80, 1, FDRIVE_RATE_1M, "3.52 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_288, 48, 80, 1, FDRIVE_RATE_1M, "3.84 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 18, 80, 1, FDRIVE_RATE_500K, "1.44 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 20, 80, 1, FDRIVE_RATE_500K, "1.6 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 21, 80, 1, FDRIVE_RATE_500K, "1.68 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 21, 82, 1, FDRIVE_RATE_500K, "1.72 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 21, 83, 1, FDRIVE_RATE_500K, "1.74 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 22, 80, 1, FDRIVE_RATE_500K, "1.76 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 23, 80, 1, FDRIVE_RATE_500K, "1.84 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_144, 24, 80, 1, FDRIVE_RATE_500K, "1.92 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 9, 80, 1, FDRIVE_RATE_250K, "720 kB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 10, 80, 1, FDRIVE_RATE_250K, "800 kB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 10, 82, 1, FDRIVE_RATE_250K, "820 kB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 10, 83, 1, FDRIVE_RATE_250K, "830 kB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 13, 80, 1, FDRIVE_RATE_250K, "1.04 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 14, 80, 1, FDRIVE_RATE_250K, "1.12 MB 3\"1/2", },
+ { FDRIVE_DRV_FAKE_63_5, FDRIVE_DISK_720, 9, 80, 0, FDRIVE_RATE_250K, "360 kB 3\"1/2", },
+#endif
/* end */
- { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, 0, NULL, },
+ { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, (uint8_t)-1, (uint8_t)-1, 0, (fdrive_rate_t)0, NULL, },
};
/* Revalidate a disk drive after a disk change */
@@ -341,7 +428,7 @@ static void fd_revalidate(fdrive_t *drv)
ro = bdrv_is_read_only(drv->bs);
bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect);
#else /* VBOX */
- if (drv->pDrvBlock
+ if ( drv->pDrvBlock
&& drv->pDrvMount
&& drv->pDrvMount->pfnIsMounted (drv->pDrvMount)) {
ro = drv->pDrvBlock->pfnIsReadOnly (drv->pDrvBlock);
@@ -450,7 +537,8 @@ enum {
FD_DIR_READ = 1,
FD_DIR_SCANE = 2,
FD_DIR_SCANL = 3,
- FD_DIR_SCANH = 4
+ FD_DIR_SCANH = 4,
+ FD_DIR_FORMAT = 5
};
enum {
@@ -523,10 +611,12 @@ enum {
enum {
FD_SR1_MA = 0x01, /* Missing address mark */
FD_SR1_NW = 0x02, /* Not writable */
+ FD_SR1_ND = 0x04, /* No data */
FD_SR1_EC = 0x80 /* End of cylinder */
};
enum {
+ FD_SR2_MD = 0x01, /* Missing data address mark */
FD_SR2_SNS = 0x04, /* Scan not satisfied */
FD_SR2_SEH = 0x08 /* Scan equal hit */
};
@@ -668,7 +758,7 @@ struct fdctrl_t {
static uint32_t fdctrl_read (void *opaque, uint32_t reg)
{
- fdctrl_t *fdctrl = opaque;
+ fdctrl_t *fdctrl = (fdctrl_t *)opaque;
uint32_t retval;
switch (reg) {
@@ -704,7 +794,7 @@ static uint32_t fdctrl_read (void *opaque, uint32_t reg)
static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
{
- fdctrl_t *fdctrl = opaque;
+ fdctrl_t *fdctrl = (fdctrl_t *)opaque;
FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
@@ -729,33 +819,6 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
}
}
-#ifdef VBOX
-/**
- * Called when a medium is mounted.
- *
- * @param pInterface Pointer to the interface structure
- * containing the called function pointer.
- */
-static DECLCALLBACK(void) fdMountNotify(PPDMIMOUNTNOTIFY pInterface)
-{
- fdrive_t *drv = PDMIMOUNTNOTIFY_2_FDRIVE (pInterface);
- LogFlow(("fdMountNotify:\n"));
- fd_revalidate(drv);
-}
-
-/**
- * Called when a medium is unmounted.
- * @param pInterface Pointer to the interface structure
- * containing the called function pointer.
- */
-static DECLCALLBACK(void) fdUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
-{
- fdrive_t *drv = PDMIMOUNTNOTIFY_2_FDRIVE (pInterface);
- LogFlow(("fdUnmountNotify:\n"));
- fd_revalidate(drv);
-}
-#endif
-
/* Change IRQ state */
static void fdctrl_reset_irq(fdctrl_t *fdctrl)
{
@@ -773,6 +836,7 @@ static void fdctrl_reset_irq(fdctrl_t *fdctrl)
static void fdctrl_raise_irq(fdctrl_t *fdctrl, uint8_t status0)
{
if (!(fdctrl->sra & FD_SRA_INTPEND)) {
+ FLOPPY_DPRINTF("Raising interrupt...\n");
#ifdef VBOX
PDMDevHlpISASetIrq (fdctrl->pDevIns, fdctrl->irq_lvl, 1);
#else
@@ -1038,7 +1102,12 @@ static uint32_t fdctrl_read_dir(fdctrl_t *fdctrl)
uint32_t retval = 0;
#ifdef VBOX
- if (fdctrl_media_changed(get_cur_drv(fdctrl)))
+ /* The change line signal is reported by the currently selected
+ * drive. If the corresponding motor on bit is not set, the drive
+ * is *not* selected!
+ */
+ if (fdctrl_media_changed(get_cur_drv(fdctrl))
+ && (fdctrl->dor & (0x10 << fdctrl->cur_drv)))
#else
if (fdctrl_media_changed(drv0(fdctrl))
|| fdctrl_media_changed(drv1(fdctrl))
@@ -1099,12 +1168,12 @@ static int fdctrl_seek_to_next_sect(fdctrl_t *fdctrl, fdrive_t *cur_drv)
cur_drv->head = 1;
} else {
cur_drv->head = 0;
- cur_drv->track++;
+ cur_drv->ltrk++;
if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
return 0;
}
} else {
- cur_drv->track++;
+ cur_drv->ltrk++;
return 0;
}
FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
@@ -1129,10 +1198,14 @@ static void fdctrl_stop_transfer(fdctrl_t *fdctrl, uint8_t status0,
fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
fdctrl->fifo[1] = status1;
fdctrl->fifo[2] = status2;
- fdctrl->fifo[3] = cur_drv->track;
+ fdctrl->fifo[3] = cur_drv->ltrk;
fdctrl->fifo[4] = cur_drv->head;
fdctrl->fifo[5] = cur_drv->sect;
fdctrl->fifo[6] = FD_SECTOR_SC;
+ FLOPPY_DPRINTF("ST0:%02x ST1:%02x ST2:%02x C:%02x H:%02x R:%02x N:%02x\n",
+ fdctrl->fifo[0], fdctrl->fifo[1], fdctrl->fifo[2], fdctrl->fifo[3],
+ fdctrl->fifo[4], fdctrl->fifo[5], fdctrl->fifo[6]);
+
fdctrl->data_dir = FD_DIR_READ;
if (!(fdctrl->msr & FD_MSR_NONDMA)) {
#ifdef VBOX
@@ -1161,6 +1234,10 @@ static void fdctrl_start_transfer(fdctrl_t *fdctrl, int direction)
FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
GET_CUR_DRV(fdctrl), kh, kt, ks,
fd_sector_calc(kh, kt, ks, cur_drv->last_sect, NUM_SIDES(cur_drv)));
+ FLOPPY_DPRINTF("CMD:%02x SEL:%02x C:%02x H:%02x R:%02x N:%02x EOT:%02x GPL:%02x DTL:%02x\n",
+ fdctrl->fifo[0], fdctrl->fifo[1], fdctrl->fifo[2],
+ fdctrl->fifo[3], fdctrl->fifo[4], fdctrl->fifo[5],
+ fdctrl->fifo[6], fdctrl->fifo[7], fdctrl->fifo[8]);
switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
case 2:
/* sect too big */
@@ -1196,7 +1273,7 @@ static void fdctrl_start_transfer(fdctrl_t *fdctrl, int direction)
if ((fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n",
fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
- fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, FD_SR2_MD);
fdctrl->fifo[3] = kt;
fdctrl->fifo[4] = kh;
fdctrl->fifo[5] = ks;
@@ -1242,7 +1319,7 @@ static void fdctrl_start_transfer(fdctrl_t *fdctrl, int direction)
if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
direction == FD_DIR_SCANH) && dma_mode == 0) ||
(direction == FD_DIR_WRITE && dma_mode == 2) ||
- (direction == FD_DIR_READ && dma_mode == 1)) {
+ (direction == FD_DIR_READ && (dma_mode == 1 || dma_mode == 0))) {
/* No access is allowed until DMA transfer has completed */
fdctrl->msr &= ~FD_MSR_RQM;
/* Now, we just have to wait for the DMA controller to
@@ -1270,6 +1347,110 @@ static void fdctrl_start_transfer(fdctrl_t *fdctrl, int direction)
return;
}
+/* Prepare a format data transfer (either DMA or FIFO) */
+static void fdctrl_start_format(fdctrl_t *fdctrl)
+{
+ fdrive_t *cur_drv;
+ uint8_t ns, dp, kh, kt, ks;
+
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ kt = cur_drv->track;
+ kh = (fdctrl->fifo[1] & 0x04) >> 2;
+ ns = fdctrl->fifo[3];
+ dp = fdctrl->fifo[5];
+ ks = 1;
+ FLOPPY_DPRINTF("Start format at %d %d %02x, %d sect, pat %02x (%d)\n",
+ GET_CUR_DRV(fdctrl), kh, kt, ns, dp,
+ fd_sector_calc(kh, kt, ks, cur_drv->last_sect, NUM_SIDES(cur_drv)));
+ switch (fd_seek(cur_drv, kh, kt, ks, false)) {
+ case 2:
+ /* sect too big */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 3:
+ /* track too big */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 4:
+ /* No seek enabled */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 1:
+ break;
+ default:
+ break;
+ }
+ /* It's not clear what should happen if the data rate does not match. */
+#if 0
+ /* Check the data rate. If the programmed data rate does not match
+ * the currently inserted medium, the operation has to fail.
+ */
+ if ((fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
+ FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n",
+ fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, FD_SR2_MD);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ }
+#endif
+ /* Set the FIFO state */
+ fdctrl->data_dir = FD_DIR_FORMAT;
+ fdctrl->data_pos = 0;
+ fdctrl->msr |= FD_MSR_CMDBUSY;
+ fdctrl->data_state &= ~(FD_STATE_MULTI | FD_STATE_SEEK);
+ fdctrl->data_len = ns * 4;
+ fdctrl->eot = ns;
+ if (fdctrl->dor & FD_DOR_DMAEN) {
+ int dma_mode;
+ /* DMA transfer are enabled. Check if DMA channel is well programmed */
+#ifndef VBOX
+ dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
+#else
+ dma_mode = PDMDevHlpDMAGetChannelMode (fdctrl->pDevIns, fdctrl->dma_chann);
+#endif
+ dma_mode = (dma_mode >> 2) & 3;
+ FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
+ dma_mode, fdctrl->data_dir,
+ (128 << fdctrl->fifo[2]) *
+ (cur_drv->last_sect + 1), fdctrl->data_len);
+ if (fdctrl->data_dir == FD_DIR_FORMAT && dma_mode == 2) {
+ /* No access is allowed until DMA transfer has completed */
+ fdctrl->msr &= ~FD_MSR_RQM;
+ /* Now, we just have to wait for the DMA controller to
+ * recall us...
+ */
+#ifndef VBOX
+ DMA_hold_DREQ(fdctrl->dma_chann);
+ DMA_schedule(fdctrl->dma_chann);
+#else
+ PDMDevHlpDMASetDREQ (fdctrl->pDevIns, fdctrl->dma_chann, 1);
+ PDMDevHlpDMASchedule (fdctrl->pDevIns);
+#endif
+ return;
+ } else {
+ FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, fdctrl->data_dir);
+ }
+ }
+ FLOPPY_DPRINTF("start non-DMA format\n");
+ fdctrl->msr |= FD_MSR_NONDMA;
+ /* IO based transfer: calculate len */
+ fdctrl_raise_irq(fdctrl, 0x00);
+
+ return;
+}
+
/* Prepare a transfer of deleted data */
static void fdctrl_start_transfer_del(fdctrl_t *fdctrl, int direction)
{
@@ -1335,13 +1516,14 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
fdrive_t *cur_drv;
#ifdef VBOX
int rc;
- uint32_t len, start_pos, rel_pos;
+ uint32_t len = 0;
+ uint32_t start_pos, rel_pos;
#else
int len, start_pos, rel_pos;
#endif
uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
- fdctrl = opaque;
+ fdctrl = (fdctrl_t *)opaque;
if (fdctrl->msr & FD_MSR_RQM) {
FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
return 0;
@@ -1362,9 +1544,27 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
else
fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
- len = 0;
+ Assert(len == 0);
goto transfer_error;
}
+
+#ifdef VBOX
+ if (cur_drv->ro)
+ {
+ if (fdctrl->data_dir == FD_DIR_WRITE || fdctrl->data_dir == FD_DIR_FORMAT)
+ {
+ /* Handle readonly medium early, no need to do DMA, touch the
+ * LED or attempt any writes. A real floppy doesn't attempt
+ * to write to readonly media either. */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW,
+ 0x00);
+ Assert(len == 0);
+ goto transfer_error;
+ }
+ }
+#endif
+
+
rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
len = dma_len - fdctrl->data_pos;
@@ -1375,8 +1575,9 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head,
cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
fd_sector(cur_drv) * FD_SECTOR_LEN);
- if (fdctrl->data_dir != FD_DIR_WRITE ||
- len < FD_SECTOR_LEN || rel_pos != 0) {
+ if (fdctrl->data_dir != FD_DIR_FORMAT &&
+ (fdctrl->data_dir != FD_DIR_WRITE ||
+ len < FD_SECTOR_LEN || rel_pos != 0)) {
/* READ & SCAN commands and realign to a sector for WRITE */
#ifdef VBOX
rc = blk_read(cur_drv, fd_sector(cur_drv), fdctrl->fifo, 1);
@@ -1414,16 +1615,6 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
case FD_DIR_WRITE:
/* WRITE commands */
#ifdef VBOX
- if (cur_drv->ro)
- {
- /* Handle readonly medium early, no need to do DMA, touch the
- * LED or attempt any writes. A real floppy doesn't attempt
- * to write to readonly media either. */
- fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW,
- 0x00);
- goto transfer_error;
- }
-
{
uint32_t written;
int rc2 = PDMDevHlpDMAReadMemory(fdctrl->pDevIns, nchan,
@@ -1447,6 +1638,38 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
goto transfer_error;
}
break;
+#ifdef VBOX
+ case FD_DIR_FORMAT:
+ /* FORMAT command */
+ {
+ uint8_t eot = fdctrl->fifo[3];
+ uint8_t filler = fdctrl->fifo[5];
+ uint32_t written;
+ int sct;
+ int rc2 = PDMDevHlpDMAReadMemory(fdctrl->pDevIns, nchan,
+ fdctrl->fifo + rel_pos,
+ fdctrl->data_pos,
+ len, &written);
+ AssertMsgRC (rc2, ("DMAReadMemory -> %Rrc\n", rc2));
+
+ /* Fill the entire track with desired data pattern. */
+ FLOPPY_DPRINTF("formatting track: %d sectors, pattern %02x\n",
+ eot, filler);
+ memset(fdctrl->fifo, filler, FD_SECTOR_LEN);
+ for (sct = 0; sct < eot; ++sct)
+ {
+ rc = blk_write(cur_drv, fd_sector(cur_drv), fdctrl->fifo, 1);
+ if (RT_FAILURE(rc))
+ {
+ FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv));
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
+ goto transfer_error;
+ }
+ fdctrl_seek_to_next_sect(fdctrl, cur_drv);
+ }
+ }
+ break;
+#endif
default:
/* SCAN commands */
{
@@ -1736,6 +1959,8 @@ static void fdctrl_handle_readid(fdctrl_t *fdctrl, int direction)
{
fdrive_t *cur_drv = get_cur_drv(fdctrl);
+ FLOPPY_DPRINTF("CMD:%02x SEL:%02x\n", fdctrl->fifo[0], fdctrl->fifo[1]);
+
/* XXX: should set main status register to busy */
cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
#ifdef VBOX
@@ -1749,30 +1974,33 @@ static void fdctrl_handle_readid(fdctrl_t *fdctrl, int direction)
static void fdctrl_handle_format_track(fdctrl_t *fdctrl, int direction)
{
fdrive_t *cur_drv;
+ uint8_t ns, dp;
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl);
- fdctrl->data_state |= FD_STATE_FORMAT;
- if (fdctrl->fifo[0] & 0x80)
- fdctrl->data_state |= FD_STATE_MULTI;
- else
- fdctrl->data_state &= ~FD_STATE_MULTI;
- fdctrl->data_state &= ~FD_STATE_SEEK;
- cur_drv->bps =
- fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
-#if 0
- cur_drv->last_sect =
- cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
- fdctrl->fifo[3] / 2;
-#else
- cur_drv->last_sect = fdctrl->fifo[3];
-#endif
- /* TODO: implement format using DMA expected by the Bochs BIOS
- * and Linux fdformat (read 3 bytes per sector via DMA and fill
- * the sector with the specified fill byte
+ fdctrl->data_state &= ~(FD_STATE_MULTI | FD_STATE_SEEK);
+ ns = fdctrl->fifo[3];
+ dp = fdctrl->fifo[5];
+
+ FLOPPY_DPRINTF("Format track %d at %d, %d sectors, filler %02x\n",
+ cur_drv->track, GET_CUR_DRV(fdctrl), ns, dp);
+ FLOPPY_DPRINTF("CMD:%02x SEL:%02x N:%02x SC:%02x GPL:%02x D:%02x\n",
+ fdctrl->fifo[0], fdctrl->fifo[1], fdctrl->fifo[2],
+ fdctrl->fifo[3], fdctrl->fifo[4], fdctrl->fifo[5]);
+
+ /* Since we cannot actually format anything, we have to make sure that
+ * whatever new format the guest is trying to establish matches the
+ * existing format of the medium.
*/
- fdctrl->data_state &= ~FD_STATE_FORMAT;
- fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+ if (cur_drv->last_sect != ns || fdctrl->fifo[2] != 2)
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_NW, 0);
+ else
+ {
+ cur_drv->bps = fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
+ cur_drv->last_sect = ns;
+
+ fdctrl_start_format(fdctrl);
+ }
}
static void fdctrl_handle_specify(fdctrl_t *fdctrl, int direction)
@@ -1806,19 +2034,25 @@ static void fdctrl_handle_sense_drive_status(fdctrl_t *fdctrl, int direction)
static void fdctrl_handle_recalibrate(fdctrl_t *fdctrl, int direction)
{
fdrive_t *cur_drv;
+ uint8_t st0;
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl);
fd_recalibrate(cur_drv);
fdctrl_reset_fifo(fdctrl);
+ st0 = FD_SR0_SEEK | GET_CUR_DRV(fdctrl);
+ /* No drive means no TRK0 signal. */
+ if (cur_drv->drive == FDRIVE_DRV_NONE)
+ st0 |= FD_SR0_ABNTERM | FD_SR0_EQPMT;
/* Raise Interrupt */
- fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
+ fdctrl_raise_irq(fdctrl, st0);
}
static void fdctrl_handle_sense_interrupt_status(fdctrl_t *fdctrl, int direction)
{
fdrive_t *cur_drv = get_cur_drv(fdctrl);
+ FLOPPY_DPRINTF("CMD:%02x\n", fdctrl->fifo[0]);
if(fdctrl->reset_sensei > 0) {
fdctrl->fifo[0] =
FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
@@ -1829,11 +2063,14 @@ static void fdctrl_handle_sense_interrupt_status(fdctrl_t *fdctrl, int direction
ASAP */
fdctrl->fifo[0] =
FD_SR0_SEEK | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
+ /* Hack to preserve SR0 on equipment check failures (no drive). */
+ if (fdctrl->status0 & FD_SR0_EQPMT)
+ fdctrl->fifo[0] = fdctrl->status0;
}
fdctrl->fifo[1] = cur_drv->track;
fdctrl_set_fifo(fdctrl, 2, 0);
- fdctrl_reset_irq(fdctrl);
+ FLOPPY_DPRINTF("ST0:%02x PCN:%02x\n", fdctrl->fifo[0], fdctrl->fifo[1]);
fdctrl->status0 = FD_SR0_RDYCHG;
}
@@ -1841,6 +2078,9 @@ static void fdctrl_handle_seek(fdctrl_t *fdctrl, int direction)
{
fdrive_t *cur_drv;
+ FLOPPY_DPRINTF("CMD:%02x SEL:%02x NCN:%02x\n", fdctrl->fifo[0],
+ fdctrl->fifo[1], fdctrl->fifo[2]);
+
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl);
fdctrl_reset_fifo(fdctrl);
@@ -1849,8 +2089,10 @@ static void fdctrl_handle_seek(fdctrl_t *fdctrl, int direction)
* there's a medium inserted or if it's banging the head against the drive.
*/
cur_drv->track = fdctrl->fifo[2];
+ cur_drv->ltrk = cur_drv->track;
+ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
/* Raise Interrupt */
- fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
+ fdctrl_raise_irq(fdctrl, FD_SR0_SEEK | GET_CUR_DRV(fdctrl));
#else
if (fdctrl->fifo[2] > cur_drv->max_track) {
fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK);
@@ -2031,6 +2273,7 @@ static void fdctrl_write_data(fdctrl_t *fdctrl, uint32_t value)
}
if (fdctrl->data_pos == 0) {
/* Command */
+ fdctrl_reset_irq(fdctrl); /* If pending from previous seek/recalibrate. */
pos = command_to_handler[value & 0xff];
FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
fdctrl->data_len = handlers[pos].parameters + 1;
@@ -2056,7 +2299,7 @@ static void fdctrl_write_data(fdctrl_t *fdctrl, uint32_t value)
static void fdctrl_result_timer(void *opaque)
{
- fdctrl_t *fdctrl = opaque;
+ fdctrl_t *fdctrl = (fdctrl_t *)opaque;
fdrive_t *cur_drv = get_cur_drv(fdctrl);
/* Pretend we are spinning.
@@ -2068,115 +2311,134 @@ static void fdctrl_result_timer(void *opaque)
}
/* READ_ID can't automatically succeed! */
#ifdef VBOX
- if (/* !cur_drv->fMediaPresent || */
- ((fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate)) {
+ if (!cur_drv->max_track) {
+ FLOPPY_DPRINTF("read id when no disk in drive\n");
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA | FD_SR1_ND, FD_SR2_MD);
+ } else if ((fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n",
fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
- fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA | FD_SR1_ND, FD_SR2_MD);
+ } else if (cur_drv->track >= cur_drv->max_track) {
+ FLOPPY_DPRINTF("read id past last track (%d >= %d)\n",
+ cur_drv->track, cur_drv->max_track);
+ cur_drv->ltrk = 0;
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA | FD_SR1_ND, FD_SR2_MD);
}
else
#endif
fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
}
+
#ifdef VBOX
-static DECLCALLBACK(void) fdc_timer (PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+
+/* -=-=-=-=-=-=-=-=- Timer Callback -=-=-=-=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV}
+ */
+static DECLCALLBACK(void) fdcTimerCallback(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
fdctrl_t *fdctrl = (fdctrl_t *)pvUser;
- fdctrl_result_timer (fdctrl);
+ fdctrl_result_timer(fdctrl);
}
-static DECLCALLBACK(int) fdc_io_write (PPDMDEVINS pDevIns,
- void *pvUser,
- RTIOPORT Port,
- uint32_t u32,
- unsigned cb)
+
+/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+static DECLCALLBACK(int) fdcIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
- if (cb == 1) {
+ if (cb == 1)
fdctrl_write (pvUser, Port & 7, u32);
- }
- else {
+ else
AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
- }
return VINF_SUCCESS;
}
-static DECLCALLBACK(int) fdc_io_read (PPDMDEVINS pDevIns,
- void *pvUser,
- RTIOPORT Port,
- uint32_t *pu32,
- unsigned cb)
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+static DECLCALLBACK(int) fdcIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
- if (cb == 1) {
+ if (cb == 1)
+ {
*pu32 = fdctrl_read (pvUser, Port & 7);
return VINF_SUCCESS;
}
- else {
- return VERR_IOM_IOPORT_UNUSED;
- }
+ return VERR_IOM_IOPORT_UNUSED;
}
-static DECLCALLBACK(int) fdcSaveExec (PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
+
+/* -=-=-=-=-=-=-=-=- Saved state -=-=-=-=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) fdcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
- fdctrl_t *s = PDMINS_2_DATA (pDevIns, fdctrl_t *);
- QEMUFile *f = pSSMHandle;
+ fdctrl_t *pThis = PDMINS_2_DATA(pDevIns, fdctrl_t *);
unsigned int i;
/* Save the FDC I/O registers... */
- SSMR3PutU8(pSSMHandle, s->sra);
- SSMR3PutU8(pSSMHandle, s->srb);
- SSMR3PutU8(pSSMHandle, s->dor);
- SSMR3PutU8(pSSMHandle, s->tdr);
- SSMR3PutU8(pSSMHandle, s->dsr);
- SSMR3PutU8(pSSMHandle, s->msr);
+ SSMR3PutU8(pSSM, pThis->sra);
+ SSMR3PutU8(pSSM, pThis->srb);
+ SSMR3PutU8(pSSM, pThis->dor);
+ SSMR3PutU8(pSSM, pThis->tdr);
+ SSMR3PutU8(pSSM, pThis->dsr);
+ SSMR3PutU8(pSSM, pThis->msr);
/* ...the status registers... */
- SSMR3PutU8(pSSMHandle, s->status0);
- SSMR3PutU8(pSSMHandle, s->status1);
- SSMR3PutU8(pSSMHandle, s->status2);
+ SSMR3PutU8(pSSM, pThis->status0);
+ SSMR3PutU8(pSSM, pThis->status1);
+ SSMR3PutU8(pSSM, pThis->status2);
/* ...the command FIFO... */
- SSMR3PutU32(pSSMHandle, sizeof(s->fifo));
- SSMR3PutMem(pSSMHandle, &s->fifo, sizeof(s->fifo));
- SSMR3PutU32(pSSMHandle, s->data_pos);
- SSMR3PutU32(pSSMHandle, s->data_len);
- SSMR3PutU8(pSSMHandle, s->data_state);
- SSMR3PutU8(pSSMHandle, s->data_dir);
+ SSMR3PutU32(pSSM, sizeof(pThis->fifo));
+ SSMR3PutMem(pSSM, &pThis->fifo, sizeof(pThis->fifo));
+ SSMR3PutU32(pSSM, pThis->data_pos);
+ SSMR3PutU32(pSSM, pThis->data_len);
+ SSMR3PutU8(pSSM, pThis->data_state);
+ SSMR3PutU8(pSSM, pThis->data_dir);
/* ...and miscellaneous internal FDC state. */
- SSMR3PutU8(pSSMHandle, s->reset_sensei);
- SSMR3PutU8(pSSMHandle, s->eot);
- SSMR3PutU8(pSSMHandle, s->timer0);
- SSMR3PutU8(pSSMHandle, s->timer1);
- SSMR3PutU8(pSSMHandle, s->precomp_trk);
- SSMR3PutU8(pSSMHandle, s->config);
- SSMR3PutU8(pSSMHandle, s->lock);
- SSMR3PutU8(pSSMHandle, s->pwrd);
- SSMR3PutU8(pSSMHandle, s->version);
+ SSMR3PutU8(pSSM, pThis->reset_sensei);
+ SSMR3PutU8(pSSM, pThis->eot);
+ SSMR3PutU8(pSSM, pThis->timer0);
+ SSMR3PutU8(pSSM, pThis->timer1);
+ SSMR3PutU8(pSSM, pThis->precomp_trk);
+ SSMR3PutU8(pSSM, pThis->config);
+ SSMR3PutU8(pSSM, pThis->lock);
+ SSMR3PutU8(pSSM, pThis->pwrd);
+ SSMR3PutU8(pSSM, pThis->version);
/* Save the number of drives and per-drive state. Note that the media
* states will be updated in fd_revalidate() and need not be saved.
*/
- SSMR3PutU8(pSSMHandle, s->num_floppies);
- Assert(RT_ELEMENTS(s->drives) == s->num_floppies);
- for (i = 0; i < s->num_floppies; ++i) {
- fdrive_t *d = &s->drives[i];
+ SSMR3PutU8(pSSM, pThis->num_floppies);
+ Assert(RT_ELEMENTS(pThis->drives) == pThis->num_floppies);
+ for (i = 0; i < pThis->num_floppies; ++i)
+ {
+ fdrive_t *d = &pThis->drives[i];
- SSMR3PutMem(pSSMHandle, &d->Led, sizeof(d->Led));
- SSMR3PutU32(pSSMHandle, d->drive);
- SSMR3PutU8(pSSMHandle, d->dsk_chg);
- SSMR3PutU8(pSSMHandle, d->perpendicular);
- SSMR3PutU8(pSSMHandle, d->head);
- SSMR3PutU8(pSSMHandle, d->track);
- SSMR3PutU8(pSSMHandle, d->sect);
+ SSMR3PutMem(pSSM, &d->Led, sizeof(d->Led));
+ SSMR3PutU32(pSSM, d->drive);
+ SSMR3PutU8(pSSM, d->dsk_chg);
+ SSMR3PutU8(pSSM, d->perpendicular);
+ SSMR3PutU8(pSSM, d->head);
+ SSMR3PutU8(pSSM, d->track);
+ SSMR3PutU8(pSSM, d->sect);
}
- return TMR3TimerSave (s->result_timer, pSSMHandle);
+ return TMR3TimerSave (pThis->result_timer, pSSM);
}
-static DECLCALLBACK(int) fdcLoadExec (PPDMDEVINS pDevIns,
- PSSMHANDLE pSSMHandle,
- uint32_t uVersion,
- uint32_t uPass)
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) fdcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
- fdctrl_t *s = PDMINS_2_DATA (pDevIns, fdctrl_t *);
- QEMUFile *f = pSSMHandle;
+ fdctrl_t *pThis = PDMINS_2_DATA(pDevIns, fdctrl_t *);
unsigned int i;
uint32_t val32;
uint8_t val8;
@@ -2193,149 +2455,171 @@ static DECLCALLBACK(int) fdcLoadExec (PPDMDEVINS pDevIns,
if (uVersion == FDC_SAVESTATE_OLD)
{
/* First verify a few assumptions. */
- AssertMsgReturn(sizeof(s->fifo) == FD_SECTOR_LEN,
+ AssertMsgReturn(sizeof(pThis->fifo) == FD_SECTOR_LEN,
("The size of FIFO in saved state doesn't match!\n"),
VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
- AssertMsgReturn(RT_ELEMENTS(s->drives) == 2,
+ AssertMsgReturn(RT_ELEMENTS(pThis->drives) == 2,
("The number of drives in old saved state doesn't match!\n"),
VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
/* Now load the old state. */
- SSMR3GetU8(pSSMHandle, &s->version);
+ SSMR3GetU8(pSSM, &pThis->version);
/* Toss IRQ level, DMA channel, I/O base, and state. */
- SSMR3GetU8(pSSMHandle, &val8);
- SSMR3GetU8(pSSMHandle, &val8);
- SSMR3GetU32(pSSMHandle, &val32);
- SSMR3GetU8(pSSMHandle, &val8);
+ SSMR3GetU8(pSSM, &val8);
+ SSMR3GetU8(pSSM, &val8);
+ SSMR3GetU32(pSSM, &val32);
+ SSMR3GetU8(pSSM, &val8);
/* Translate dma_en. */
- SSMR3GetU8(pSSMHandle, &val8);
+ SSMR3GetU8(pSSM, &val8);
if (val8)
- s->dor |= FD_DOR_DMAEN;
- SSMR3GetU8(pSSMHandle, &s->cur_drv);
+ pThis->dor |= FD_DOR_DMAEN;
+ SSMR3GetU8(pSSM, &pThis->cur_drv);
/* Translate bootsel. */
- SSMR3GetU8(pSSMHandle, &val8);
- s->tdr |= val8 << 2;
- SSMR3GetMem(pSSMHandle, &s->fifo, FD_SECTOR_LEN);
- SSMR3GetU32(pSSMHandle, &s->data_pos);
- SSMR3GetU32(pSSMHandle, &s->data_len);
- SSMR3GetU8(pSSMHandle, &s->data_state);
- SSMR3GetU8(pSSMHandle, &s->data_dir);
- SSMR3GetU8(pSSMHandle, &s->status0);
- SSMR3GetU8(pSSMHandle, &s->eot);
- SSMR3GetU8(pSSMHandle, &s->timer0);
- SSMR3GetU8(pSSMHandle, &s->timer1);
- SSMR3GetU8(pSSMHandle, &s->precomp_trk);
- SSMR3GetU8(pSSMHandle, &s->config);
- SSMR3GetU8(pSSMHandle, &s->lock);
- SSMR3GetU8(pSSMHandle, &s->pwrd);
-
- for (i = 0; i < 2; ++i) {
- fdrive_t *d = &s->drives[i];
-
- SSMR3GetMem (pSSMHandle, &d->Led, sizeof (d->Led));
- SSMR3GetU32(pSSMHandle, &val32);
- d->drive = val32;
- SSMR3GetU32(pSSMHandle, &val32); /* Toss drflags */
- SSMR3GetU8(pSSMHandle, &d->perpendicular);
- SSMR3GetU8(pSSMHandle, &d->head);
- SSMR3GetU8(pSSMHandle, &d->track);
- SSMR3GetU8(pSSMHandle, &d->sect);
- SSMR3GetU8(pSSMHandle, &val8); /* Toss dir, rw */
- SSMR3GetU8(pSSMHandle, &val8);
- SSMR3GetU32(pSSMHandle, &val32);
- d->flags = val32;
- SSMR3GetU8(pSSMHandle, &d->last_sect);
- SSMR3GetU8(pSSMHandle, &d->max_track);
- SSMR3GetU16(pSSMHandle, &d->bps);
- SSMR3GetU8(pSSMHandle, &d->ro);
+ SSMR3GetU8(pSSM, &val8);
+ pThis->tdr |= val8 << 2;
+ SSMR3GetMem(pSSM, &pThis->fifo, FD_SECTOR_LEN);
+ SSMR3GetU32(pSSM, &pThis->data_pos);
+ SSMR3GetU32(pSSM, &pThis->data_len);
+ SSMR3GetU8(pSSM, &pThis->data_state);
+ SSMR3GetU8(pSSM, &pThis->data_dir);
+ SSMR3GetU8(pSSM, &pThis->status0);
+ SSMR3GetU8(pSSM, &pThis->eot);
+ SSMR3GetU8(pSSM, &pThis->timer0);
+ SSMR3GetU8(pSSM, &pThis->timer1);
+ SSMR3GetU8(pSSM, &pThis->precomp_trk);
+ SSMR3GetU8(pSSM, &pThis->config);
+ SSMR3GetU8(pSSM, &pThis->lock);
+ SSMR3GetU8(pSSM, &pThis->pwrd);
+
+ for (i = 0; i < 2; ++i)
+ {
+ fdrive_t *d = &pThis->drives[i];
+
+ SSMR3GetMem (pSSM, &d->Led, sizeof (d->Led));
+ SSMR3GetU32(pSSM, &val32);
+ d->drive = (fdrive_type_t)val32;
+ SSMR3GetU32(pSSM, &val32); /* Toss drflags */
+ SSMR3GetU8(pSSM, &d->perpendicular);
+ SSMR3GetU8(pSSM, &d->head);
+ SSMR3GetU8(pSSM, &d->track);
+ SSMR3GetU8(pSSM, &d->sect);
+ SSMR3GetU8(pSSM, &val8); /* Toss dir, rw */
+ SSMR3GetU8(pSSM, &val8);
+ SSMR3GetU32(pSSM, &val32);
+ d->flags = (fdrive_flags_t)val32;
+ SSMR3GetU8(pSSM, &d->last_sect);
+ SSMR3GetU8(pSSM, &d->max_track);
+ SSMR3GetU16(pSSM, &d->bps);
+ SSMR3GetU8(pSSM, &d->ro);
}
}
else /* New state - straightforward. */
{
Assert(uVersion == FDC_SAVESTATE_CURRENT);
/* Load the FDC I/O registers... */
- SSMR3GetU8(pSSMHandle, &s->sra);
- SSMR3GetU8(pSSMHandle, &s->srb);
- SSMR3GetU8(pSSMHandle, &s->dor);
- SSMR3GetU8(pSSMHandle, &s->tdr);
- SSMR3GetU8(pSSMHandle, &s->dsr);
- SSMR3GetU8(pSSMHandle, &s->msr);
+ SSMR3GetU8(pSSM, &pThis->sra);
+ SSMR3GetU8(pSSM, &pThis->srb);
+ SSMR3GetU8(pSSM, &pThis->dor);
+ SSMR3GetU8(pSSM, &pThis->tdr);
+ SSMR3GetU8(pSSM, &pThis->dsr);
+ SSMR3GetU8(pSSM, &pThis->msr);
/* ...the status registers... */
- SSMR3GetU8(pSSMHandle, &s->status0);
- SSMR3GetU8(pSSMHandle, &s->status1);
- SSMR3GetU8(pSSMHandle, &s->status2);
+ SSMR3GetU8(pSSM, &pThis->status0);
+ SSMR3GetU8(pSSM, &pThis->status1);
+ SSMR3GetU8(pSSM, &pThis->status2);
/* ...the command FIFO, if the size matches... */
- SSMR3GetU32(pSSMHandle, &val32);
- AssertMsgReturn(sizeof(s->fifo) == val32,
+ SSMR3GetU32(pSSM, &val32);
+ AssertMsgReturn(sizeof(pThis->fifo) == val32,
("The size of FIFO in saved state doesn't match!\n"),
VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
- SSMR3GetMem(pSSMHandle, &s->fifo, sizeof(s->fifo));
- SSMR3GetU32(pSSMHandle, &s->data_pos);
- SSMR3GetU32(pSSMHandle, &s->data_len);
- SSMR3GetU8(pSSMHandle, &s->data_state);
- SSMR3GetU8(pSSMHandle, &s->data_dir);
+ SSMR3GetMem(pSSM, &pThis->fifo, sizeof(pThis->fifo));
+ SSMR3GetU32(pSSM, &pThis->data_pos);
+ SSMR3GetU32(pSSM, &pThis->data_len);
+ SSMR3GetU8(pSSM, &pThis->data_state);
+ SSMR3GetU8(pSSM, &pThis->data_dir);
/* ...and miscellaneous internal FDC state. */
- SSMR3GetU8(pSSMHandle, &s->reset_sensei);
- SSMR3GetU8(pSSMHandle, &s->eot);
- SSMR3GetU8(pSSMHandle, &s->timer0);
- SSMR3GetU8(pSSMHandle, &s->timer1);
- SSMR3GetU8(pSSMHandle, &s->precomp_trk);
- SSMR3GetU8(pSSMHandle, &s->config);
- SSMR3GetU8(pSSMHandle, &s->lock);
- SSMR3GetU8(pSSMHandle, &s->pwrd);
- SSMR3GetU8(pSSMHandle, &s->version);
+ SSMR3GetU8(pSSM, &pThis->reset_sensei);
+ SSMR3GetU8(pSSM, &pThis->eot);
+ SSMR3GetU8(pSSM, &pThis->timer0);
+ SSMR3GetU8(pSSM, &pThis->timer1);
+ SSMR3GetU8(pSSM, &pThis->precomp_trk);
+ SSMR3GetU8(pSSM, &pThis->config);
+ SSMR3GetU8(pSSM, &pThis->lock);
+ SSMR3GetU8(pSSM, &pThis->pwrd);
+ SSMR3GetU8(pSSM, &pThis->version);
/* Validate the number of drives. */
- SSMR3GetU8(pSSMHandle, &s->num_floppies);
- AssertMsgReturn(RT_ELEMENTS(s->drives) == s->num_floppies,
+ SSMR3GetU8(pSSM, &pThis->num_floppies);
+ AssertMsgReturn(RT_ELEMENTS(pThis->drives) == pThis->num_floppies,
("The number of drives in saved state doesn't match!\n"),
VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
/* Load the per-drive state. */
- for (i = 0; i < s->num_floppies; ++i) {
- fdrive_t *d = &s->drives[i];
-
- SSMR3GetMem(pSSMHandle, &d->Led, sizeof(d->Led));
- SSMR3GetU32(pSSMHandle, &val32);
- d->drive = val32;
- SSMR3GetU8(pSSMHandle, &d->dsk_chg);
- SSMR3GetU8(pSSMHandle, &d->perpendicular);
- SSMR3GetU8(pSSMHandle, &d->head);
- SSMR3GetU8(pSSMHandle, &d->track);
- SSMR3GetU8(pSSMHandle, &d->sect);
+ for (i = 0; i < pThis->num_floppies; ++i)
+ {
+ fdrive_t *d = &pThis->drives[i];
+
+ SSMR3GetMem(pSSM, &d->Led, sizeof(d->Led));
+ SSMR3GetU32(pSSM, &val32);
+ d->drive = (fdrive_type_t)val32;
+ SSMR3GetU8(pSSM, &d->dsk_chg);
+ SSMR3GetU8(pSSM, &d->perpendicular);
+ SSMR3GetU8(pSSM, &d->head);
+ SSMR3GetU8(pSSM, &d->track);
+ SSMR3GetU8(pSSM, &d->sect);
}
}
- return TMR3TimerLoad (s->result_timer, pSSMHandle);
+ return TMR3TimerLoad (pThis->result_timer, pSSM);
}
+
+/* -=-=-=-=-=-=-=-=- Drive level interfaces -=-=-=-=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIMOUNTNOTIFY,pfnMountNotify}
+ */
+static DECLCALLBACK(void) fdMountNotify(PPDMIMOUNTNOTIFY pInterface)
+{
+ fdrive_t *pDrv = RT_FROM_MEMBER(pInterface, fdrive_t, IMountNotify);
+ LogFlow(("fdMountNotify:\n"));
+ fd_revalidate(pDrv);
+}
+
+
+/**
+ * @interface_method_impl{PDMIMOUNTNOTIFY,pfnUnmountNotify}
+ */
+static DECLCALLBACK(void) fdUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
+{
+ fdrive_t *pDrv = RT_FROM_MEMBER(pInterface, fdrive_t, IMountNotify);
+ LogFlow(("fdUnmountNotify:\n"));
+ fd_revalidate(pDrv);
+}
+
+
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
static DECLCALLBACK(void *) fdQueryInterface (PPDMIBASE pInterface, const char *pszIID)
{
- fdrive_t *pDrive = PDMIBASE_2_FDRIVE(pInterface);
+ fdrive_t *pDrv = RT_FROM_MEMBER(pInterface, fdrive_t, IBase);
- PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrive->IBase);
- PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pDrive->IPort);
- PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pDrive->IMountNotify);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrv->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pDrv->IPort);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pDrv->IMountNotify);
return NULL;
}
+
+/* -=-=-=-=-=-=-=-=- Controller level interfaces -=-=-=-=-=-=-=-=- */
+
/**
- * Gets the pointer to the status LED of a unit.
- *
- * @returns VBox status code.
- * @param pInterface Pointer to the interface structure containing the called function pointer.
- * @param iLUN The unit which status LED we desire.
- * @param ppLed Where to store the LED pointer.
+ * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
*/
-static DECLCALLBACK(int) fdcStatusQueryStatusLed (PPDMILEDPORTS pInterface,
- unsigned iLUN,
- PPDMLED *ppLed)
-{
- fdctrl_t *fdctrl = (fdctrl_t *)
- ((uintptr_t )pInterface - RT_OFFSETOF (fdctrl_t, ILeds));
- if (iLUN < RT_ELEMENTS(fdctrl->drives)) {
- *ppLed = &fdctrl->drives[iLUN].Led;
+static DECLCALLBACK(int) fdcStatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+{
+ fdctrl_t *pThis = RT_FROM_MEMBER (pInterface, fdctrl_t, ILeds);
+ if (iLUN < RT_ELEMENTS(pThis->drives)) {
+ *ppLed = &pThis->drives[iLUN].Led;
Assert ((*ppLed)->u32Magic == PDMLED_MAGIC);
return VINF_SUCCESS;
}
@@ -2362,16 +2646,17 @@ static DECLCALLBACK(void *) fdcStatusQueryInterface(PPDMIBASE pInterface, const
* @returns VBox status code.
* @param drv The drive in question.
* @param pDevIns The driver instance.
+ * @param fInit Set if we're at init time and can change the drive type.
*/
-static int fdConfig (fdrive_t *drv, PPDMDEVINS pDevIns)
+static int fdConfig(fdrive_t *drv, PPDMDEVINS pDevIns, bool fInit)
{
- static const char *descs[] = {"Floppy Drive A:", "Floppy Drive B"};
+ static const char * const s_apszDesc[] = {"Floppy Drive A:", "Floppy Drive B"};
int rc;
/*
* Reset the LED just to be on the safe side.
*/
- Assert (RT_ELEMENTS(descs) > drv->iLUN);
+ Assert (RT_ELEMENTS(s_apszDesc) > drv->iLUN);
Assert (drv->Led.u32Magic == PDMLED_MAGIC);
drv->Led.Actual.u32 = 0;
drv->Led.Asserted.u32 = 0;
@@ -2379,7 +2664,7 @@ static int fdConfig (fdrive_t *drv, PPDMDEVINS pDevIns)
/*
* Try attach the block device and get the interfaces.
*/
- rc = PDMDevHlpDriverAttach (pDevIns, drv->iLUN, &drv->IBase, &drv->pDrvBase, descs[drv->iLUN]);
+ rc = PDMDevHlpDriverAttach (pDevIns, drv->iLUN, &drv->IBase, &drv->pDrvBase, s_apszDesc[drv->iLUN]);
if (RT_SUCCESS (rc)) {
drv->pDrvBlock = PDMIBASE_QUERY_INTERFACE(drv->pDrvBase, PDMIBLOCK);
if (drv->pDrvBlock) {
@@ -2387,7 +2672,7 @@ static int fdConfig (fdrive_t *drv, PPDMDEVINS pDevIns)
if (drv->pDrvBlockBios) {
drv->pDrvMount = PDMIBASE_QUERY_INTERFACE(drv->pDrvBase, PDMIMOUNT);
if (drv->pDrvMount) {
- fd_init(drv);
+ fd_init(drv, fInit);
} else {
AssertMsgFailed (("Configuration error: LUN#%d without mountable interface!\n", drv->iLUN));
rc = VERR_PDM_MISSING_INTERFACE;
@@ -2431,18 +2716,13 @@ static int fdConfig (fdrive_t *drv, PPDMDEVINS pDevIns)
/**
- * Attach command.
+ * @interface_method_impl{PDMDEVREG,pfnAttach}
*
* This is called when we change block driver for a floppy drive.
- *
- * @returns VBox status code.
- * @param pDevIns The device instance.
- * @param iLUN The logical unit which is being detached.
*/
-static DECLCALLBACK(int) fdcAttach (PPDMDEVINS pDevIns,
- unsigned iLUN, uint32_t fFlags)
+static DECLCALLBACK(int) fdcAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
- fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *);
+ fdctrl_t *fdctrl = PDMINS_2_DATA(pDevIns, fdctrl_t *);
fdrive_t *drv;
int rc;
LogFlow (("ideDetach: iLUN=%u\n", iLUN));
@@ -2471,7 +2751,7 @@ static DECLCALLBACK(int) fdcAttach (PPDMDEVINS pDevIns,
AssertRelease (!drv->pDrvBlockBios);
AssertRelease (!drv->pDrvMount);
- rc = fdConfig (drv, pDevIns);
+ rc = fdConfig (drv, pDevIns, false /*fInit*/);
AssertMsg (rc != VERR_PDM_NO_ATTACHED_DRIVER,
("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc));
if (RT_SUCCESS(rc)) {
@@ -2484,69 +2764,62 @@ static DECLCALLBACK(int) fdcAttach (PPDMDEVINS pDevIns,
/**
- * Detach notification.
+ * @interface_method_impl{PDMDEVREG,pfnDetach}
*
* The floppy drive has been temporarily 'unplugged'.
- *
- * @param pDevIns The device instance.
- * @param iLUN The logical unit which is being detached.
*/
-static DECLCALLBACK(void) fdcDetach (PPDMDEVINS pDevIns,
- unsigned iLUN, uint32_t fFlags)
+static DECLCALLBACK(void) fdcDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
- fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *);
+ fdctrl_t *pThis = PDMINS_2_DATA(pDevIns, fdctrl_t *);
LogFlow (("ideDetach: iLUN=%u\n", iLUN));
- switch (iLUN) {
- case 0:
- case 1: {
- fdrive_t *drv = &fdctrl->drives[iLUN];
- drv->pDrvBase = NULL;
- drv->pDrvBlock = NULL;
- drv->pDrvBlockBios = NULL;
- drv->pDrvMount = NULL;
- break;
- }
+ switch (iLUN)
+ {
+ case 0:
+ case 1:
+ {
+ fdrive_t *drv = &pThis->drives[iLUN];
+ drv->pDrvBase = NULL;
+ drv->pDrvBlock = NULL;
+ drv->pDrvBlockBios = NULL;
+ drv->pDrvMount = NULL;
+ break;
+ }
- default:
- AssertMsgFailed (("Cannot detach LUN#%d!\n", iLUN));
- break;
+ default:
+ AssertMsgFailed(("Cannot detach LUN#%d!\n", iLUN));
+ break;
}
}
/**
- * Handle reset.
+ * @interface_method_impl{PDMDEVREG,pfnReset}
*
* I haven't check the specs on what's supposed to happen on reset, but we
* should get any 'FATAL: floppy recal:f07 ctrl not ready' when resetting
* at wrong time like we do if this was all void.
- *
- * @param pDevIns The device instance.
*/
-static DECLCALLBACK(void) fdcReset (PPDMDEVINS pDevIns)
+static DECLCALLBACK(void) fdcReset(PPDMDEVINS pDevIns)
{
- fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *);
+ fdctrl_t *pThis = PDMINS_2_DATA (pDevIns, fdctrl_t *);
unsigned i;
LogFlow (("fdcReset:\n"));
- fdctrl_reset(fdctrl, 0);
+ fdctrl_reset(pThis, 0);
- for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++) {
- fd_revalidate(&fdctrl->drives[i]);
- }
+ for (i = 0; i < RT_ELEMENTS(pThis->drives); i++)
+ fd_revalidate(&pThis->drives[i]);
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
-static DECLCALLBACK(int) fdcConstruct (PPDMDEVINS pDevIns,
- int iInstance,
- PCFGMNODE pCfg)
+static DECLCALLBACK(int) fdcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
+ fdctrl_t *pThis = PDMINS_2_DATA(pDevIns, fdctrl_t *);
int rc;
- fdctrl_t *fdctrl = PDMINS_2_DATA(pDevIns, fdctrl_t*);
unsigned i, j;
int ii;
bool mem_mapped;
@@ -2566,154 +2839,139 @@ static DECLCALLBACK(int) fdcConstruct (PPDMDEVINS pDevIns,
/*
* Read the configuration.
*/
- rc = CFGMR3QueryU8 (pCfg, "IRQ", &irq_lvl);
- if (rc == VERR_CFGM_VALUE_NOT_FOUND)
- irq_lvl = 6;
- else if (RT_FAILURE (rc)) {
- AssertMsgFailed (("Configuration error: Failed to read U8 IRQ, rc=%Rrc\n", rc));
- return rc;
- }
+ rc = CFGMR3QueryU8Def(pCfg, "IRQ", &irq_lvl, 6);
+ AssertMsgRCReturn(rc, ("Configuration error: Failed to read U8 IRQ, rc=%Rrc\n", rc), rc);
- rc = CFGMR3QueryU8 (pCfg, "DMA", &dma_chann);
- if (rc == VERR_CFGM_VALUE_NOT_FOUND)
- dma_chann = 2;
- else if (RT_FAILURE (rc)) {
- AssertMsgFailed (("Configuration error: Failed to read U8 DMA, rc=%Rrc\n", rc));
- return rc;
- }
+ rc = CFGMR3QueryU8Def(pCfg, "DMA", &dma_chann, 2);
+ AssertMsgRCReturn(rc, ("Configuration error: Failed to read U8 DMA, rc=%Rrc\n", rc), rc);
- rc = CFGMR3QueryU16 (pCfg, "IOBase", &io_base);
- if (rc == VERR_CFGM_VALUE_NOT_FOUND)
- io_base = 0x3f0;
- else if (RT_FAILURE (rc)) {
- AssertMsgFailed (("Configuration error: Failed to read U16 IOBase, rc=%Rrc\n", rc));
- return rc;
- }
+ rc = CFGMR3QueryU16Def(pCfg, "IOBase", &io_base, 0x3f0);
+ AssertMsgRCReturn(rc, ("Configuration error: Failed to read U16 IOBase, rc=%Rrc\n", rc), rc);
- rc = CFGMR3QueryBool (pCfg, "MemMapped", &mem_mapped);
- if (rc == VERR_CFGM_VALUE_NOT_FOUND)
- mem_mapped = false;
- else if (RT_FAILURE (rc)) {
- AssertMsgFailed (("Configuration error: Failed to read bool value MemMapped rc=%Rrc\n", rc));
- return rc;
- }
+ rc = CFGMR3QueryBoolDef(pCfg, "MemMapped", &mem_mapped, false);
+ AssertMsgRCReturn(rc, ("Configuration error: Failed to read bool value MemMapped rc=%Rrc\n", rc), rc);
/*
* Initialize data.
*/
LogFlow(("fdcConstruct: irq_lvl=%d dma_chann=%d io_base=%#x\n", irq_lvl, dma_chann, io_base));
- fdctrl->pDevIns = pDevIns;
- fdctrl->version = 0x90; /* Intel 82078 controller */
- fdctrl->irq_lvl = irq_lvl;
- fdctrl->dma_chann = dma_chann;
- fdctrl->io_base = io_base;
- fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */
- fdctrl->num_floppies = MAX_FD;
+ pThis->pDevIns = pDevIns;
+ pThis->version = 0x90; /* Intel 82078 controller */
+ pThis->irq_lvl = irq_lvl;
+ pThis->dma_chann = dma_chann;
+ pThis->io_base = io_base;
+ pThis->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */
+ pThis->num_floppies = MAX_FD;
/* Fill 'command_to_handler' lookup table */
- for (ii = RT_ELEMENTS(handlers) - 1; ii >= 0; ii--) {
- for (j = 0; j < sizeof(command_to_handler); j++) {
- if ((j & handlers[ii].mask) == handlers[ii].value) {
+ for (ii = RT_ELEMENTS(handlers) - 1; ii >= 0; ii--)
+ for (j = 0; j < sizeof(command_to_handler); j++)
+ if ((j & handlers[ii].mask) == handlers[ii].value)
command_to_handler[j] = ii;
- }
- }
- }
- fdctrl->IBaseStatus.pfnQueryInterface = fdcStatusQueryInterface;
- fdctrl->ILeds.pfnQueryStatusLed = fdcStatusQueryStatusLed;
+ pThis->IBaseStatus.pfnQueryInterface = fdcStatusQueryInterface;
+ pThis->ILeds.pfnQueryStatusLed = fdcStatusQueryStatusLed;
- for (i = 0; i < RT_ELEMENTS(fdctrl->drives); ++i) {
- fdrive_t *drv = &fdctrl->drives[i];
+ for (i = 0; i < RT_ELEMENTS(pThis->drives); ++i)
+ {
+ fdrive_t *pDrv = &pThis->drives[i];
- drv->drive = FDRIVE_DRV_NONE;
- drv->iLUN = i;
+ pDrv->drive = FDRIVE_DRV_NONE;
+ pDrv->iLUN = i;
- drv->IBase.pfnQueryInterface = fdQueryInterface;
- drv->IMountNotify.pfnMountNotify = fdMountNotify;
- drv->IMountNotify.pfnUnmountNotify = fdUnmountNotify;
- drv->Led.u32Magic = PDMLED_MAGIC;
+ pDrv->IBase.pfnQueryInterface = fdQueryInterface;
+ pDrv->IMountNotify.pfnMountNotify = fdMountNotify;
+ pDrv->IMountNotify.pfnUnmountNotify = fdUnmountNotify;
+ pDrv->Led.u32Magic = PDMLED_MAGIC;
}
/*
* Create the FDC timer.
*/
- rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, fdc_timer, fdctrl,
- TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "FDC Timer", &fdctrl->result_timer);
- if (RT_FAILURE (rc))
+ rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, fdcTimerCallback, pThis,
+ TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "FDC Timer", &pThis->result_timer);
+ if (RT_FAILURE(rc))
return rc;
/*
* Register DMA channel.
*/
- if (fdctrl->dma_chann != 0xff) {
- rc = PDMDevHlpDMARegister (pDevIns, dma_chann, &fdctrl_transfer_handler, fdctrl);
- if (RT_FAILURE (rc))
+ if (pThis->dma_chann != 0xff)
+ {
+ rc = PDMDevHlpDMARegister(pDevIns, dma_chann, &fdctrl_transfer_handler, pThis);
+ if (RT_FAILURE(rc))
return rc;
}
/*
* IO / MMIO.
*/
- if (mem_mapped) {
- AssertMsgFailed (("Memory mapped floppy not support by now\n"));
+ if (mem_mapped)
+ {
+ AssertMsgFailed(("Memory mapped floppy not support by now\n"));
return VERR_NOT_SUPPORTED;
#if 0
FLOPPY_ERROR("memory mapped floppy not supported by now !\n");
io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write);
cpu_register_physical_memory(base, 0x08, io_mem);
#endif
- } else {
- rc = PDMDevHlpIOPortRegister (pDevIns, io_base + 0x1, 5, fdctrl,
- fdc_io_write, fdc_io_read, NULL, NULL, "FDC#1");
- if (RT_FAILURE (rc))
+ }
+ else
+ {
+ rc = PDMDevHlpIOPortRegister(pDevIns, io_base + 0x1, 5, pThis,
+ fdcIoPortWrite, fdcIoPortRead, NULL, NULL, "FDC#1");
+ if (RT_FAILURE(rc))
return rc;
- rc = PDMDevHlpIOPortRegister (pDevIns, io_base + 0x7, 1, fdctrl,
- fdc_io_write, fdc_io_read, NULL, NULL, "FDC#2");
- if (RT_FAILURE (rc))
+ rc = PDMDevHlpIOPortRegister(pDevIns, io_base + 0x7, 1, pThis,
+ fdcIoPortWrite, fdcIoPortRead, NULL, NULL, "FDC#2");
+ if (RT_FAILURE(rc))
return rc;
}
/*
* Register the saved state data unit.
*/
- rc = PDMDevHlpSSMRegister (pDevIns, FDC_SAVESTATE_CURRENT, sizeof(*fdctrl), fdcSaveExec, fdcLoadExec);
+ rc = PDMDevHlpSSMRegister(pDevIns, FDC_SAVESTATE_CURRENT, sizeof(*pThis), fdcSaveExec, fdcLoadExec);
if (RT_FAILURE(rc))
return rc;
/*
* Attach the status port (optional).
*/
- rc = PDMDevHlpDriverAttach (pDevIns, PDM_STATUS_LUN, &fdctrl->IBaseStatus, &pBase, "Status Port");
- if (RT_SUCCESS (rc)) {
- fdctrl->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
- } else if (rc != VERR_PDM_NO_ATTACHED_DRIVER) {
- AssertMsgFailed (("Failed to attach to status driver. rc=%Rrc\n",
- rc));
+ rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBaseStatus, &pBase, "Status Port");
+ if (RT_SUCCESS (rc))
+ pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
+ else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
+ {
+ AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
return rc;
}
/*
* Initialize drives.
*/
- for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++) {
- fdrive_t *drv = &fdctrl->drives[i];
- rc = fdConfig (drv, pDevIns);
- if ( RT_FAILURE (rc)
- && rc != VERR_PDM_NO_ATTACHED_DRIVER) {
- AssertMsgFailed (("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc));
+ for (i = 0; i < RT_ELEMENTS(pThis->drives); i++)
+ {
+ fdrive_t *pDrv = &pThis->drives[i];
+ rc = fdConfig(pDrv, pDevIns, true /*fInit*/);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_PDM_NO_ATTACHED_DRIVER)
+ {
+ AssertMsgFailed(("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc));
return rc;
}
}
- fdctrl_reset(fdctrl, 0);
+ fdctrl_reset(pThis, 0);
- for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++)
- fd_revalidate(&fdctrl->drives[i]);
+ for (i = 0; i < RT_ELEMENTS(pThis->drives); i++)
+ fd_revalidate(&pThis->drives[i]);
return VINF_SUCCESS;
}
+
/**
* The device registration structure.
*/
@@ -2743,7 +3001,7 @@ const PDMDEVREG g_DeviceFloppyController =
NULL,
/* pfnRelocate */
NULL,
- /* pfnIOCtl */
+ /* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
diff --git a/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp b/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp
index a8bce58e..6511ef7c 100644
--- a/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp
+++ b/src/VBox/Devices/Storage/DevLsiLogicSCSI.cpp
@@ -1,10 +1,10 @@
/* $Id: DevLsiLogicSCSI.cpp $ */
/** @file
- * VBox storage devices: LsiLogic LSI53c1030 SCSI controller.
+ * DevLsiLogicSCSI - LsiLogic LSI53c1030 SCSI controller.
*/
/*
- * Copyright (C) 2006-2012 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -15,15 +15,20 @@
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
-//#define DEBUG
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_LSILOGICSCSI
#include <VBox/vmm/pdmdev.h>
#include <VBox/vmm/pdmqueue.h>
+#include <VBox/vmm/pdmthread.h>
#include <VBox/vmm/pdmcritsect.h>
#include <VBox/scsi.h>
+#include <VBox/sup.h>
#include <iprt/assert.h>
#include <iprt/asm.h>
#include <iprt/string.h>
+#include <iprt/list.h>
#ifdef IN_RING3
# include <iprt/memcache.h>
# include <iprt/mem.h>
@@ -37,17 +42,54 @@
#include "VBoxDD.h"
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
/** The current saved state version. */
-#define LSILOGIC_SAVED_STATE_VERSION 3
+#define LSILOGIC_SAVED_STATE_VERSION 5
+/** The saved state version used by VirtualBox before the diagnostic
+ * memory access was implemented. */
+#define LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM 4
+/** The saved state version used by VirtualBox before the doorbell status flag
+ * was changed from bool to a 32bit enum. */
+#define LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL 3
/** The saved state version used by VirtualBox before SAS support was added. */
-#define LSILOGIC_SAVED_STATE_VERSION_PRE_SAS 2
+#define LSILOGIC_SAVED_STATE_VERSION_PRE_SAS 2
/** The saved state version used by VirtualBox 3.0 and earlier. It does not
* include the device config part. */
-#define LSILOGIC_SAVED_STATE_VERSION_VBOX_30 1
+#define LSILOGIC_SAVED_STATE_VERSION_VBOX_30 1
/** Maximum number of entries in the release log. */
#define MAX_REL_LOG_ERRORS 1024
+#define LSILOGIC_RTGCPHYS_FROM_U32(Hi, Lo) ( (RTGCPHYS)RT_MAKE_U64(Lo, Hi) )
+
+/** Upper number a buffer is freed if it was too big before. */
+#define LSILOGIC_MAX_ALLOC_TOO_MUCH 20
+
+/** Maximum size of the memory regions (prevents teh guest from DOSing the host by
+ * allocating loadds of memory). */
+#define LSILOGIC_MEMORY_REGIONS_MAX (_1M)
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+
+/**
+ * I/O buffer copy worker.
+ *
+ * @returns nothing.
+ * @param pDevIns Device instance data.
+ * @param GCPhysIoBuf Guest physical address of the I/O buffer.
+ * @param pvBuf R3 buffer pointer.
+ * @param cbCopy How much to copy.
+ */
+typedef DECLCALLBACK(void) FNLSILOGICIOBUFCOPY(PPDMDEVINS pDevIns, RTGCPHYS GCPhysIoBuf,
+ void *pvBuf, size_t cbCopy);
+/** Pointer to a I/O buffer copy worker. */
+typedef FNLSILOGICIOBUFCOPY *PFNLSILOGICIOBUFCOPY;
+
/**
* Reply data.
*/
@@ -61,7 +103,26 @@ typedef struct LSILOGICSCSIREPLY
uint32_t cbReply;
/** Different views to the reply depending on the request type. */
MptReplyUnion Reply;
-} LSILOGICSCSIREPLY, *PLSILOGICSCSIREPLY;
+} LSILOGICSCSIREPLY;
+/** Pointer to reply data. */
+typedef LSILOGICSCSIREPLY *PLSILOGICSCSIREPLY;
+
+/**
+ * Memory region of the IOC.
+ */
+typedef struct LSILOGICMEMREGN
+{
+ /** List node. */
+ RTLISTNODE NodeList;
+ /** 32bit address the region starts to describe. */
+ uint32_t u32AddrStart;
+ /** 32bit address the region ends (inclusive). */
+ uint32_t u32AddrEnd;
+ /** Data for this region - variable. */
+ uint32_t au32Data[1];
+} LSILOGICMEMREGN;
+/** Pointer to a memory region. */
+typedef LSILOGICMEMREGN *PLSILOGICMEMREGN;
/**
* State of a device attached to the buslogic host adapter.
@@ -76,7 +137,7 @@ typedef struct LSILOGICDEVICE
R3PTRTYPE(struct LSILOGICSCSI *) pLsiLogicR3;
/** LUN of the device. */
- RTUINT iLUN;
+ uint32_t iLUN;
/** Number of outstanding tasks on the port. */
volatile uint32_t cOutstandingRequests;
@@ -97,14 +158,15 @@ typedef struct LSILOGICDEVICE
/** The status LED state for this device. */
PDMLED Led;
-} LSILOGICDEVICE, *PLSILOGICDEVICE;
+} LSILOGICDEVICE;
+/** Pointer to a device state. */
+typedef LSILOGICDEVICE *PLSILOGICDEVICE;
/** Pointer to a task state. */
-typedef struct LSILOGICTASKSTATE *PLSILOGICTASKSTATE;
+typedef struct LSILOGICREQ *PLSILOGICREQ;
/**
- * Device instance data for the emulated
- * SCSI controller.
+ * Device instance data for the emulated SCSI controller.
*/
typedef struct LSILOGICSCSI
{
@@ -127,19 +189,15 @@ typedef struct LSILOGICSCSI
/** Who needs to init the driver to get into operational state. */
LSILOGICWHOINIT enmWhoInit;
/** Flag whether we are in doorbell function. */
- bool fDoorbellInProgress;
+ LSILOGICDOORBELLSTATE enmDoorbellState;
/** Flag whether diagnostic access is enabled. */
bool fDiagnosticEnabled;
-
/** Flag whether a notification was send to R3. */
bool fNotificationSend;
-
/** Flag whether the guest enabled event notification from the IOC. */
bool fEventNotificationEnabled;
-
-#if HC_ARCH_BITS == 64
- uint32_t Alignment0;
-#endif
+ /** Flag whether the diagnostic address and RW registers are enabled. */
+ bool fDiagRegsEnabled;
/** Queue to send tasks to R3. - R3 ptr */
R3PTRTYPE(PPDMQUEUE) pNotificationQueueR3;
@@ -148,40 +206,30 @@ typedef struct LSILOGICSCSI
/** Queue to send tasks to R3. - RC ptr */
RCPTRTYPE(PPDMQUEUE) pNotificationQueueRC;
-#if HC_ARCH_BITS == 64
- uint32_t Alignment1;
-#endif
-
/** Number of device states allocated. */
- uint32_t cDeviceStates;
-
-#if HC_ARCH_BITS == 64
- uint32_t Alignment2;
-#endif
+ uint32_t cDeviceStates;
/** States for attached devices. */
R3PTRTYPE(PLSILOGICDEVICE) paDeviceStates;
-
- /** MMIO address the device is mapped to. */
- RTGCPHYS GCPhysMMIOBase;
- /** I/O port address the device is mapped to. */
- RTIOPORT IOPortBase;
+#if HC_ARCH_BITS == 32
+ RTR3PTR R3PtrPadding0;
+#endif
/** Interrupt mask. */
volatile uint32_t uInterruptMask;
/** Interrupt status register. */
volatile uint32_t uInterruptStatus;
- /** Buffer for messages which are passed
- * through the doorbell using the
+ /** Buffer for messages which are passed through the doorbell using the
* handshake method. */
- uint32_t aMessage[sizeof(MptConfigurationRequest)];
+ uint32_t aMessage[sizeof(MptConfigurationRequest)]; /** @todo r=bird: Looks like 4 tims the required size? Please explain in comment if this correct... */
/** Actual position in the buffer. */
uint32_t iMessage;
/** Size of the message which is given in the doorbell message in dwords. */
uint32_t cMessage;
- /** Reply buffer. */
+ /** Reply buffer.
+ * @note 60 bytes */
MptReplyUnion ReplyBuffer;
/** Next entry to read. */
uint32_t uNextReplyEntryRead;
@@ -191,6 +239,11 @@ typedef struct LSILOGICSCSI
/** The fault code of the I/O controller if we are in the fault state. */
uint16_t u16IOCFaultCode;
+ /** I/O port address the device is mapped to. */
+ RTIOPORT IOPortBase;
+ /** MMIO address the device is mapped to. */
+ RTGCPHYS GCPhysMMIOBase;
+
/** Upper 32 bits of the message frame address to locate requests in guest memory. */
uint32_t u32HostMFAHighAddr;
/** Upper 32 bits of the sense buffer address. */
@@ -211,7 +264,6 @@ typedef struct LSILOGICSCSI
/** Number entries allocated for the outstanding request queue. */
uint32_t cRequestQueueEntries;
- uint32_t Alignment3;
/** Critical section protecting the reply post queue. */
PDMCRITSECT ReplyPostQueueCritSect;
@@ -238,6 +290,8 @@ typedef struct LSILOGICSCSI
RCPTRTYPE(volatile uint32_t *) pReplyPostQueueBaseRC;
/** Pointer to the start of the request queue - RC. */
RCPTRTYPE(volatile uint32_t *) pRequestQueueBaseRC;
+ /** End these RC pointers on a 64-bit boundrary. */
+ RTRCPTR RCPtrPadding1;
/** Next free entry in the reply queue the guest can write a address to. */
volatile uint32_t uReplyFreeQueueNextEntryFreeWrite;
@@ -259,18 +313,12 @@ typedef struct LSILOGICSCSI
/** Handle counter */
uint16_t u16NextHandle;
- uint16_t u16Alignment4;
- uint32_t u32Alignment5;
-
/** Number of ports this controller has. */
uint8_t cPorts;
-#if HC_ARCH_BITS == 64
- uint32_t Alignment6;
-#endif
-
/** BIOS emulation. */
VBOXSCSI VBoxSCSI;
+
/** Cache for allocated tasks. */
R3PTRTYPE(RTMEMCACHE) hTaskCache;
/** Status LUN: The base interface. */
@@ -282,54 +330,53 @@ typedef struct LSILOGICSCSI
/** Pointer to the configuration page area. */
R3PTRTYPE(PMptConfigurationPagesSupported) pConfigurationPages;
-#if HC_ARCH_BITS == 64
- uint32_t Alignment7;
-#endif
-
/** Indicates that PDMDevHlpAsyncNotificationCompleted should be called when
* a port is entering the idle state. */
- bool volatile fSignalIdle;
+ bool volatile fSignalIdle;
/** Flag whether we have tasks which need to be processed again- */
- bool volatile fRedo;
+ bool volatile fRedo;
+ /** Flag whether the worker thread is sleeping. */
+ volatile bool fWrkThreadSleeping;
+ /** Alignment padding. */
+ bool afPadding2[HC_ARCH_BITS == 32 ? 1 : 5];
/** List of tasks which can be redone. */
- R3PTRTYPE(volatile PLSILOGICTASKSTATE) pTasksRedoHead;
+ R3PTRTYPE(volatile PLSILOGICREQ) pTasksRedoHead;
-} LSILOGISCSI, *PLSILOGICSCSI;
+ /** Current address to read from or write to in the diagnostic memory region. */
+ uint32_t u32DiagMemAddr;
+ /** Current size of the memory regions. */
+ uint32_t cbMemRegns;
+
+#if HC_ARCH_BITS ==32
+ uint32_t u32Padding3;
+#endif
-/**
- * Scatter gather list entry data.
- */
-typedef struct LSILOGICTASKSTATESGENTRY
-{
- /** Flag whether the buffer in the list is from the guest or an
- * allocated temporary buffer because the segments in the guest
- * are not sector aligned.
- */
- bool fGuestMemory;
- /** Flag whether the buffer contains data or is the destination for the transfer. */
- bool fBufferContainsData;
- /** Pointer to the start of the buffer. */
- void *pvBuf;
- /** Size of the buffer. */
- uint32_t cbBuf;
- /** Flag dependent data. */
union
{
- /** Data to handle direct mappings of guest buffers. */
- PGMPAGEMAPLOCK PageLock;
- /** The segment in the guest which is not sector aligned. */
- RTGCPHYS GCPhysAddrBufferUnaligned;
- } u;
-} LSILOGICTASKSTATESGENTRY, *PLSILOGICTASKSTATESGENTRY;
+ /** List of memory regions - PLSILOGICMEMREGN. */
+ RTLISTANCHOR ListMemRegns;
+ uint8_t u8Padding[2 * sizeof(RTUINTPTR)];
+ };
+
+ /** The support driver session handle. */
+ R3R0PTRTYPE(PSUPDRVSESSION) pSupDrvSession;
+ /** Worker thread. */
+ R3PTRTYPE(PPDMTHREAD) pThreadWrk;
+ /** The event semaphore the processing thread waits on. */
+ SUPSEMEVENT hEvtProcess;
+
+} LSILOGISCSI;
+/** Pointer to the device instance data of the LsiLogic emulation. */
+typedef LSILOGICSCSI *PLSILOGICSCSI;
/**
* Task state object which holds all necessary data while
* processing the request from the guest.
*/
-typedef struct LSILOGICTASKSTATE
+typedef struct LSILOGICREQ
{
/** Next in the redo list. */
- PLSILOGICTASKSTATE pRedoNext;
+ PLSILOGICREQ pRedoNext;
/** Target device. */
PLSILOGICDEVICE pTargetDevice;
/** The message request from the guest. */
@@ -341,50 +388,43 @@ typedef struct LSILOGICTASKSTATE
/** Address of the message request frame in guests memory.
* Used to read the S/G entries in the second step. */
RTGCPHYS GCPhysMessageFrameAddr;
- /** Number of scatter gather list entries. */
- uint32_t cSGListEntries;
- /** How many entries would fit into the sg list. */
- uint32_t cSGListSize;
- /** How many times the list was too big. */
- uint32_t cSGListTooBig;
- /** Pointer to the first entry of the scatter gather list. */
- PRTSGSEG pSGListHead;
- /** How many entries would fit into the sg info list. */
- uint32_t cSGInfoSize;
- /** Number of entries for the information entries. */
- uint32_t cSGInfoEntries;
- /** How many times the list was too big. */
- uint32_t cSGInfoTooBig;
- /** Pointer to the first mapping information entry. */
- PLSILOGICTASKSTATESGENTRY paSGEntries;
- /** Size of the temporary buffer for unaligned guest segments. */
- uint32_t cbBufferUnaligned;
- /** Pointer to the temporary buffer. */
- void *pvBufferUnaligned;
+ /** Physical start address of the S/G list. */
+ RTGCPHYS GCPhysSgStart;
+ /** Chain offset */
+ uint32_t cChainOffset;
+ /** Segment describing the I/O buffer. */
+ RTSGSEG SegIoBuf;
+ /** Additional memory allocation for this task. */
+ void *pvAlloc;
+ /** Siize of the allocation. */
+ size_t cbAlloc;
+ /** Number of times we had too much memory allocated for the request. */
+ unsigned cAllocTooMuch;
/** Pointer to the sense buffer. */
uint8_t abSenseBuffer[18];
/** Flag whether the request was issued from the BIOS. */
bool fBIOS;
-} LSILOGICTASKSTATE;
+} LSILOGICREQ;
+
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
RT_C_DECLS_BEGIN
#ifdef IN_RING3
-static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic);
-static void lsilogicConfigurationPagesFree(PLSILOGICSCSI pThis);
-static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConfigurationRequest pConfigurationReq,
- PMptConfigurationReply pReply);
+static void lsilogicR3InitializeConfigurationPages(PLSILOGICSCSI pThis);
+static void lsilogicR3ConfigurationPagesFree(PLSILOGICSCSI pThis);
+static int lsilogicR3ProcessConfigurationRequest(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
+ PMptConfigurationReply pReply);
#endif
RT_C_DECLS_END
-#define PDMIBASE_2_PLSILOGICDEVICE(pInterface) ( (PLSILOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICDEVICE, IBase)) )
-#define PDMISCSIPORT_2_PLSILOGICDEVICE(pInterface) ( (PLSILOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICDEVICE, ISCSIPort)) )
-#define PDMILEDPORTS_2_PLSILOGICDEVICE(pInterface) ( (PLSILOGICDEVICE)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICDEVICE, ILed)) )
-#define LSILOGIC_RTGCPHYS_FROM_U32(Hi, Lo) ( (RTGCPHYS)RT_MAKE_U64(Lo, Hi) )
-#define PDMIBASE_2_PLSILOGICSCSI(pInterface) ( (PLSILOGICSCSI)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICSCSI, IBase)) )
-#define PDMILEDPORTS_2_PLSILOGICSCSI(pInterface) ( (PLSILOGICSCSI)((uintptr_t)(pInterface) - RT_OFFSETOF(LSILOGICSCSI, ILeds)) )
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
/** Key sequence the guest has to write to enable access
* to diagnostic memory. */
static const uint8_t g_lsilogicDiagnosticAccess[] = {0x04, 0x0b, 0x02, 0x07, 0x0d};
@@ -393,7 +433,7 @@ static const uint8_t g_lsilogicDiagnosticAccess[] = {0x04, 0x0b, 0x02, 0x07, 0x0
* Updates the status of the interrupt pin of the device.
*
* @returns nothing.
- * @param pThis Pointer to the device instance data.
+ * @param pThis Pointer to the LsiLogic device state.
*/
static void lsilogicUpdateInterrupt(PLSILOGICSCSI pThis)
{
@@ -423,13 +463,13 @@ static void lsilogicUpdateInterrupt(PLSILOGICSCSI pThis)
* updates the interrupt status.
*
* @returns nothing.
- * @param pLsiLogic Pointer to the device instance.
- * @param uStatus The status bit to set.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param uStatus The status bit to set.
*/
-DECLINLINE(void) lsilogicSetInterrupt(PLSILOGICSCSI pLsiLogic, uint32_t uStatus)
+DECLINLINE(void) lsilogicSetInterrupt(PLSILOGICSCSI pThis, uint32_t uStatus)
{
- ASMAtomicOrU32(&pLsiLogic->uInterruptStatus, uStatus);
- lsilogicUpdateInterrupt(pLsiLogic);
+ ASMAtomicOrU32(&pThis->uInterruptStatus, uStatus);
+ lsilogicUpdateInterrupt(pThis);
}
/**
@@ -437,50 +477,86 @@ DECLINLINE(void) lsilogicSetInterrupt(PLSILOGICSCSI pLsiLogic, uint32_t uStatus)
* updates the interrupt status.
*
* @returns nothing.
- * @param pLsiLogic Pointer to the device instance.
- * @param uStatus The status bit to set.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param uStatus The status bit to set.
*/
-DECLINLINE(void) lsilogicClearInterrupt(PLSILOGICSCSI pLsiLogic, uint32_t uStatus)
+DECLINLINE(void) lsilogicClearInterrupt(PLSILOGICSCSI pThis, uint32_t uStatus)
{
- ASMAtomicAndU32(&pLsiLogic->uInterruptStatus, ~uStatus);
- lsilogicUpdateInterrupt(pLsiLogic);
+ ASMAtomicAndU32(&pThis->uInterruptStatus, ~uStatus);
+ lsilogicUpdateInterrupt(pThis);
}
/**
* Sets the I/O controller into fault state and sets the fault code.
*
* @returns nothing
- * @param pLsiLogic Pointer to the controller device instance.
- * @param uIOCFaultCode Fault code to set.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param uIOCFaultCode Fault code to set.
*/
-DECLINLINE(void) lsilogicSetIOCFaultCode(PLSILOGICSCSI pLsiLogic, uint16_t uIOCFaultCode)
+DECLINLINE(void) lsilogicSetIOCFaultCode(PLSILOGICSCSI pThis, uint16_t uIOCFaultCode)
{
- if (pLsiLogic->enmState != LSILOGICSTATE_FAULT)
+ if (pThis->enmState != LSILOGICSTATE_FAULT)
{
Log(("%s: Setting I/O controller into FAULT state: uIOCFaultCode=%u\n", __FUNCTION__, uIOCFaultCode));
- pLsiLogic->enmState = LSILOGICSTATE_FAULT;
- pLsiLogic->u16IOCFaultCode = uIOCFaultCode;
+ pThis->enmState = LSILOGICSTATE_FAULT;
+ pThis->u16IOCFaultCode = uIOCFaultCode;
}
else
- {
Log(("%s: We are already in FAULT state\n"));
- }
+}
+
+/**
+ * Returns the number of frames in the reply free queue.
+ *
+ * @returns Number of frames in the reply free queue.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+DECLINLINE(uint32_t) lsilogicReplyFreeQueueGetFrameCount(PLSILOGICSCSI pThis)
+{
+ uint32_t cReplyFrames = 0;
+
+ if (pThis->uReplyFreeQueueNextAddressRead <= pThis->uReplyFreeQueueNextEntryFreeWrite)
+ cReplyFrames = pThis->uReplyFreeQueueNextEntryFreeWrite - pThis->uReplyFreeQueueNextAddressRead;
+ else
+ cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyFreeQueueNextAddressRead + pThis->uReplyFreeQueueNextEntryFreeWrite;
+
+ return cReplyFrames;
+}
+
+/**
+ * Returns the number of free entries in the reply post queue.
+ *
+ * @returns Number of frames in the reply free queue.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+DECLINLINE(uint32_t) lsilogicReplyPostQueueGetFrameCount(PLSILOGICSCSI pThis)
+{
+ uint32_t cReplyFrames = 0;
+
+ if (pThis->uReplyPostQueueNextAddressRead <= pThis->uReplyPostQueueNextEntryFreeWrite)
+ cReplyFrames = pThis->cReplyQueueEntries - pThis->uReplyPostQueueNextEntryFreeWrite + pThis->uReplyPostQueueNextAddressRead;
+ else
+ cReplyFrames = pThis->uReplyPostQueueNextEntryFreeWrite - pThis->uReplyPostQueueNextAddressRead;
+
+ return cReplyFrames;
}
#ifdef IN_RING3
+
/**
* Performs a hard reset on the controller.
*
* @returns VBox status code.
- * @param pThis Pointer to the device instance to initialize.
+ * @param pThis Pointer to the LsiLogic device state.
*/
-static int lsilogicHardReset(PLSILOGICSCSI pThis)
+static int lsilogicR3HardReset(PLSILOGICSCSI pThis)
{
pThis->enmState = LSILOGICSTATE_RESET;
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
/* The interrupts are masked out. */
- pThis->uInterruptMask |= LSILOGIC_REG_HOST_INTR_MASK_DOORBELL |
- LSILOGIC_REG_HOST_INTR_MASK_REPLY;
+ pThis->uInterruptMask |= LSILOGIC_REG_HOST_INTR_MASK_DOORBELL
+ | LSILOGIC_REG_HOST_INTR_MASK_REPLY;
/* Reset interrupt states. */
pThis->uInterruptStatus = 0;
lsilogicUpdateInterrupt(pThis);
@@ -494,17 +570,19 @@ static int lsilogicHardReset(PLSILOGICSCSI pThis)
pThis->uRequestQueueNextAddressRead = 0;
/* Disable diagnostic access. */
- pThis->iDiagnosticAccess = 0;
+ pThis->iDiagnosticAccess = 0;
+ pThis->fDiagnosticEnabled = false;
+ pThis->fDiagRegsEnabled = false;
/* Set default values. */
- pThis->cMaxDevices = pThis->cDeviceStates;
- pThis->cMaxBuses = 1;
- pThis->cbReplyFrame = 128; /* @todo Figure out where it is needed. */
- pThis->u16NextHandle = 1;
- /** @todo: Put stuff to reset here. */
+ pThis->cMaxDevices = pThis->cDeviceStates;
+ pThis->cMaxBuses = 1;
+ pThis->cbReplyFrame = 128; /* @todo Figure out where it is needed. */
+ pThis->u16NextHandle = 1;
+ pThis->u32DiagMemAddr = 0;
- lsilogicConfigurationPagesFree(pThis);
- lsilogicInitializeConfigurationPages(pThis);
+ lsilogicR3ConfigurationPagesFree(pThis);
+ lsilogicR3InitializeConfigurationPages(pThis);
/* Mark that we finished performing the reset. */
pThis->enmState = LSILOGICSTATE_READY;
@@ -517,7 +595,7 @@ static int lsilogicHardReset(PLSILOGICSCSI pThis)
* @returns nothing.
* @param pThis The LsiLogic controller instance
*/
-static void lsilogicConfigurationPagesFree(PLSILOGICSCSI pThis)
+static void lsilogicR3ConfigurationPagesFree(PLSILOGICSCSI pThis)
{
if (pThis->pConfigurationPages)
@@ -553,69 +631,39 @@ static void lsilogicConfigurationPagesFree(PLSILOGICSCSI pThis)
* Finishes a context reply.
*
* @returns nothing
- * @param pLsiLogic Pointer to the device instance
- * @param u32MessageContext The message context ID to post.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param u32MessageContext The message context ID to post.
*/
-static void lsilogicFinishContextReply(PLSILOGICSCSI pLsiLogic, uint32_t u32MessageContext)
+static void lsilogicR3FinishContextReply(PLSILOGICSCSI pThis, uint32_t u32MessageContext)
{
int rc;
- LogFlowFunc(("pLsiLogic=%#p u32MessageContext=%#x\n", pLsiLogic, u32MessageContext));
+ LogFlowFunc(("pThis=%#p u32MessageContext=%#x\n", pThis, u32MessageContext));
- AssertMsg(!pLsiLogic->fDoorbellInProgress, ("We are in a doorbell function\n"));
+ AssertMsg(pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE, ("We are in a doorbell function\n"));
/* Write message context ID into reply post queue. */
- rc = PDMCritSectEnter(&pLsiLogic->ReplyPostQueueCritSect, VINF_SUCCESS);
+ rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_SUCCESS);
AssertRC(rc);
-#if 0
/* Check for a entry in the queue. */
- if (RT_UNLIKELY(pLsiLogic->uReplyPostQueueNextAddressRead != pLsiLogic->uReplyPostQueueNextEntryFreeWrite))
+ if (!lsilogicReplyPostQueueGetFrameCount(pThis))
{
/* Set error code. */
- lsilogicSetIOCFaultCode(pLsiLogic, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
- PDMCritSectLeave(&pLsiLogic->ReplyPostQueueCritSect);
+ lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
return;
}
-#endif
/* We have a context reply. */
- ASMAtomicWriteU32(&pLsiLogic->CTX_SUFF(pReplyPostQueueBase)[pLsiLogic->uReplyPostQueueNextEntryFreeWrite], u32MessageContext);
- ASMAtomicIncU32(&pLsiLogic->uReplyPostQueueNextEntryFreeWrite);
- pLsiLogic->uReplyPostQueueNextEntryFreeWrite %= pLsiLogic->cReplyQueueEntries;
+ ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyPostQueueBase)[pThis->uReplyPostQueueNextEntryFreeWrite], u32MessageContext);
+ ASMAtomicIncU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
+ pThis->uReplyPostQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
/* Set interrupt. */
- lsilogicSetInterrupt(pLsiLogic, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
-
- PDMCritSectLeave(&pLsiLogic->ReplyPostQueueCritSect);
-}
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
-static void lsilogicTaskStateClear(PLSILOGICTASKSTATE pTaskState)
-{
- RTMemFree(pTaskState->pSGListHead);
- RTMemFree(pTaskState->paSGEntries);
- if (pTaskState->pvBufferUnaligned)
- RTMemPageFree(pTaskState->pvBufferUnaligned, pTaskState->cbBufferUnaligned);
- pTaskState->cSGListSize = 0;
- pTaskState->cSGInfoSize = 0;
- pTaskState->cSGInfoEntries = 0;
- pTaskState->cSGListTooBig = 0;
- pTaskState->pSGListHead = NULL;
- pTaskState->paSGEntries = NULL;
- pTaskState->pvBufferUnaligned = NULL;
- pTaskState->cbBufferUnaligned = 0;
-}
-
-static int lsilogicTaskStateCtor(RTMEMCACHE hMemCache, void *pvObj, void *pvUser)
-{
- memset(pvObj, 0, sizeof(LSILOGICTASKSTATE));
- return VINF_SUCCESS;
-}
-
-static void lsilogicTaskStateDtor(RTMEMCACHE hMemCache, void *pvObj, void *pvUser)
-{
- PLSILOGICTASKSTATE pTaskState = (PLSILOGICTASKSTATE)pvObj;
- lsilogicTaskStateClear(pTaskState);
+ PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
}
#endif /* IN_RING3 */
@@ -624,24 +672,24 @@ static void lsilogicTaskStateDtor(RTMEMCACHE hMemCache, void *pvObj, void *pvUse
* Takes necessary steps to finish a reply frame.
*
* @returns nothing
- * @param pLsiLogic Pointer to the device instance
+ * @param pThis Pointer to the LsiLogic device state.
* @param pReply Pointer to the reply message.
* @param fForceReplyFifo Flag whether the use of the reply post fifo is forced.
*/
-static void lsilogicFinishAddressReply(PLSILOGICSCSI pLsiLogic, PMptReplyUnion pReply, bool fForceReplyFifo)
+static void lsilogicFinishAddressReply(PLSILOGICSCSI pThis, PMptReplyUnion pReply, bool fForceReplyFifo)
{
/*
* If we are in a doorbell function we set the reply size now and
* set the system doorbell status interrupt to notify the guest that
* we are ready to send the reply.
*/
- if (pLsiLogic->fDoorbellInProgress && !fForceReplyFifo)
+ if (pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_NOT_IN_USE && !fForceReplyFifo)
{
/* Set size of the reply in 16bit words. The size in the reply is in 32bit dwords. */
- pLsiLogic->cReplySize = pReply->Header.u8MessageLength * 2;
- Log(("%s: cReplySize=%u\n", __FUNCTION__, pLsiLogic->cReplySize));
- pLsiLogic->uNextReplyEntryRead = 0;
- lsilogicSetInterrupt(pLsiLogic, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ pThis->cReplySize = pReply->Header.u8MessageLength * 2;
+ Log(("%s: cReplySize=%u\n", __FUNCTION__, pThis->cReplySize));
+ pThis->uNextReplyEntryRead = 0;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
}
else
{
@@ -653,65 +701,61 @@ static void lsilogicFinishAddressReply(PLSILOGICSCSI pLsiLogic, PMptReplyUnion p
#ifdef IN_RING3
int rc;
/* Grab a free reply message from the queue. */
- rc = PDMCritSectEnter(&pLsiLogic->ReplyFreeQueueCritSect, VINF_SUCCESS);
+ rc = PDMCritSectEnter(&pThis->ReplyFreeQueueCritSect, VINF_SUCCESS);
AssertRC(rc);
-#if 0
/* Check for a free reply frame. */
- if (RT_UNLIKELY(pLsiLogic->uReplyFreeQueueNextAddressRead != pLsiLogic->uReplyFreeQueueNextEntryFreeWrite))
+ if (!lsilogicReplyFreeQueueGetFrameCount(pThis))
{
/* Set error code. */
- lsilogicSetIOCFaultCode(pLsiLogic, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
- PDMCritSectLeave(&pLsiLogic->ReplyFreeQueueCritSect);
+ lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ PDMCritSectLeave(&pThis->ReplyFreeQueueCritSect);
return;
}
-#endif
- uint32_t u32ReplyFrameAddressLow = pLsiLogic->CTX_SUFF(pReplyFreeQueueBase)[pLsiLogic->uReplyFreeQueueNextAddressRead];
+ uint32_t u32ReplyFrameAddressLow = pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead];
- pLsiLogic->uReplyFreeQueueNextAddressRead++;
- pLsiLogic->uReplyFreeQueueNextAddressRead %= pLsiLogic->cReplyQueueEntries;
+ pThis->uReplyFreeQueueNextAddressRead++;
+ pThis->uReplyFreeQueueNextAddressRead %= pThis->cReplyQueueEntries;
- PDMCritSectLeave(&pLsiLogic->ReplyFreeQueueCritSect);
+ PDMCritSectLeave(&pThis->ReplyFreeQueueCritSect);
/* Build 64bit physical address. */
- RTGCPHYS GCPhysReplyMessage = LSILOGIC_RTGCPHYS_FROM_U32(pLsiLogic->u32HostMFAHighAddr, u32ReplyFrameAddressLow);
- size_t cbReplyCopied = (pLsiLogic->cbReplyFrame < sizeof(MptReplyUnion)) ? pLsiLogic->cbReplyFrame : sizeof(MptReplyUnion);
+ RTGCPHYS GCPhysReplyMessage = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr, u32ReplyFrameAddressLow);
+ size_t cbReplyCopied = (pThis->cbReplyFrame < sizeof(MptReplyUnion)) ? pThis->cbReplyFrame : sizeof(MptReplyUnion);
/* Write reply to guest memory. */
- PDMDevHlpPhysWrite(pLsiLogic->CTX_SUFF(pDevIns), GCPhysReplyMessage, pReply, cbReplyCopied);
+ PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysReplyMessage, pReply, cbReplyCopied);
/* Write low 32bits of reply frame into post reply queue. */
- rc = PDMCritSectEnter(&pLsiLogic->ReplyPostQueueCritSect, VINF_SUCCESS);
+ rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_SUCCESS);
AssertRC(rc);
-#if 0
/* Check for a entry in the queue. */
- if (RT_UNLIKELY(pLsiLogic->uReplyPostQueueNextAddressRead != pLsiLogic->uReplyPostQueueNextEntryFreeWrite))
+ if (!lsilogicReplyPostQueueGetFrameCount(pThis))
{
/* Set error code. */
- lsilogicSetIOCFaultCode(pLsiLogic, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
- PDMCritSectLeave(&pLsiLogic->ReplyPostQueueCritSect);
+ lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INSUFFICIENT_RESOURCES);
+ PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
return;
}
-#endif
/* We have a address reply. Set the 31th bit to indicate that. */
- ASMAtomicWriteU32(&pLsiLogic->CTX_SUFF(pReplyPostQueueBase)[pLsiLogic->uReplyPostQueueNextEntryFreeWrite],
+ ASMAtomicWriteU32(&pThis->CTX_SUFF(pReplyPostQueueBase)[pThis->uReplyPostQueueNextEntryFreeWrite],
RT_BIT(31) | (u32ReplyFrameAddressLow >> 1));
- ASMAtomicIncU32(&pLsiLogic->uReplyPostQueueNextEntryFreeWrite);
- pLsiLogic->uReplyPostQueueNextEntryFreeWrite %= pLsiLogic->cReplyQueueEntries;
+ ASMAtomicIncU32(&pThis->uReplyPostQueueNextEntryFreeWrite);
+ pThis->uReplyPostQueueNextEntryFreeWrite %= pThis->cReplyQueueEntries;
if (fForceReplyFifo)
{
- pLsiLogic->fDoorbellInProgress = false;
- lsilogicSetInterrupt(pLsiLogic, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
}
/* Set interrupt. */
- lsilogicSetInterrupt(pLsiLogic, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR);
- PDMCritSectLeave(&pLsiLogic->ReplyPostQueueCritSect);
+ PDMCritSectLeave(&pThis->ReplyPostQueueCritSect);
#else
AssertMsgFailed(("This is not allowed to happen.\n"));
#endif
@@ -719,25 +763,256 @@ static void lsilogicFinishAddressReply(PLSILOGICSCSI pLsiLogic, PMptReplyUnion p
}
#ifdef IN_RING3
+
+/**
+ * Tries to find a memory region which covers the given address.
+ *
+ * @returns Pointer to memory region or NULL if not found.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param u32Addr The 32bit address to search for.
+ */
+static PLSILOGICMEMREGN lsilogicR3MemRegionFindByAddr(PLSILOGICSCSI pThis, uint32_t u32Addr)
+{
+ PLSILOGICMEMREGN pIt;
+ PLSILOGICMEMREGN pRegion = NULL;
+
+ RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
+ {
+ if ( u32Addr >= pIt->u32AddrStart
+ && u32Addr <= pIt->u32AddrEnd)
+ {
+ pRegion = pIt;
+ break;
+ }
+ }
+
+ return pRegion;
+}
+
+/**
+ * Frees all allocated memory regions.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static void lsilogicR3MemRegionsFree(PLSILOGICSCSI pThis)
+{
+ PLSILOGICMEMREGN pIt;
+ PLSILOGICMEMREGN pItNext;
+
+ RTListForEachSafe(&pThis->ListMemRegns, pIt, pItNext, LSILOGICMEMREGN, NodeList)
+ {
+ RTListNodeRemove(&pIt->NodeList);
+ RTMemFree(pIt);
+ }
+ pThis->cbMemRegns = 0;
+}
+
+/**
+ * Inserts a given memory region into the list.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pRegion The region to insert.
+ */
+static void lsilogicR3MemRegionInsert(PLSILOGICSCSI pThis, PLSILOGICMEMREGN pRegion)
+{
+ PLSILOGICMEMREGN pIt;
+ bool fInserted = false;
+
+ /* Insert at the right position. */
+ RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
+ {
+ if (pRegion->u32AddrEnd < pIt->u32AddrStart)
+ {
+ RTListNodeInsertBefore(&pIt->NodeList, &pRegion->NodeList);
+ fInserted = true;
+ break;
+ }
+ }
+ if (!fInserted)
+ RTListAppend(&pThis->ListMemRegns, &pRegion->NodeList);
+}
+
+/**
+ * Count number of memory regions.
+ *
+ * @returns Number of memory regions.
+ * @param pThis Pointer to the LsiLogic device state.
+ */
+static uint32_t lsilogicR3MemRegionsCount(PLSILOGICSCSI pThis)
+{
+ uint32_t cRegions = 0;
+ PLSILOGICMEMREGN pIt;
+
+ RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
+ {
+ cRegions++;
+ }
+
+ return cRegions;
+}
+
+/**
+ * Handles a write to the diagnostic data register.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param u32Data Data to write.
+ */
+static void lsilogicR3DiagRegDataWrite(PLSILOGICSCSI pThis, uint32_t u32Data)
+{
+ PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThis, pThis->u32DiagMemAddr);
+
+ if (pRegion)
+ {
+ uint32_t offRegion = pThis->u32DiagMemAddr - pRegion->u32AddrStart;
+
+ AssertMsg( offRegion % 4 == 0
+ && pThis->u32DiagMemAddr <= pRegion->u32AddrEnd,
+ ("Region offset not on a word boundary or crosses memory region\n"));
+
+ offRegion /= 4;
+ pRegion->au32Data[offRegion] = u32Data;
+ }
+ else
+ {
+ PLSILOGICMEMREGN pIt;
+
+ pRegion = NULL;
+
+ /* Create new region, first check whether we can extend another region. */
+ RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
+ {
+ if (pThis->u32DiagMemAddr == pIt->u32AddrEnd + sizeof(uint32_t))
+ {
+ pRegion = pIt;
+ break;
+ }
+ }
+
+ if (pRegion)
+ {
+ /* Reallocate. */
+ RTListNodeRemove(&pRegion->NodeList);
+
+ uint32_t cRegionSizeOld = (pRegion->u32AddrEnd - pRegion->u32AddrStart) / 4 + 1;
+ uint32_t cRegionSizeNew = cRegionSizeOld + 512;
+
+ if (pThis->cbMemRegns + 512 * sizeof(uint32_t) < LSILOGIC_MEMORY_REGIONS_MAX)
+ {
+ PLSILOGICMEMREGN pRegionNew = (PLSILOGICMEMREGN)RTMemRealloc(pRegion, RT_OFFSETOF(LSILOGICMEMREGN, au32Data[cRegionSizeNew]));
+
+ if (pRegionNew)
+ {
+ pRegion = pRegionNew;
+ memset(&pRegion->au32Data[cRegionSizeOld], 0, 512 * sizeof(uint32_t));
+ pRegion->au32Data[cRegionSizeOld] = u32Data;
+ pRegion->u32AddrEnd = pRegion->u32AddrStart + (cRegionSizeNew - 1) * sizeof(uint32_t);
+ pThis->cbMemRegns += 512 * sizeof(uint32_t);
+ }
+ /* else: Silently fail, there is nothing we can do here and the guest might work nevertheless. */
+
+ lsilogicR3MemRegionInsert(pThis, pRegion);
+ }
+ }
+ else
+ {
+ if (pThis->cbMemRegns + 512 * sizeof(uint32_t) < LSILOGIC_MEMORY_REGIONS_MAX)
+ {
+ /* Create completely new. */
+ pRegion = (PLSILOGICMEMREGN)RTMemAllocZ(RT_OFFSETOF(LSILOGICMEMREGN, au32Data[512]));
+ if (pRegion)
+ {
+ pRegion->u32AddrStart = pThis->u32DiagMemAddr;
+ pRegion->u32AddrEnd = pRegion->u32AddrStart + (512 - 1) * sizeof(uint32_t);
+ pRegion->au32Data[0] = u32Data;
+ pThis->cbMemRegns += 512 * sizeof(uint32_t);
+
+ lsilogicR3MemRegionInsert(pThis, pRegion);
+ }
+ /* else: Silently fail, there is nothing we can do here and the guest might work nevertheless. */
+ }
+ }
+
+ }
+
+ /* Memory access is always 32bit big. */
+ pThis->u32DiagMemAddr += sizeof(uint32_t);
+}
+
+/**
+ * Handles a read from the diagnostic data register.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pu32Data Where to store the data.
+ */
+static void lsilogicR3DiagRegDataRead(PLSILOGICSCSI pThis, uint32_t *pu32Data)
+{
+ PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThis, pThis->u32DiagMemAddr);
+
+ if (pRegion)
+ {
+ uint32_t offRegion = pThis->u32DiagMemAddr - pRegion->u32AddrStart;
+
+ AssertMsg( offRegion % 4 == 0
+ && pThis->u32DiagMemAddr <= pRegion->u32AddrEnd,
+ ("Region offset not on a word boundary or crosses memory region\n"));
+
+ offRegion /= 4;
+ *pu32Data = pRegion->au32Data[offRegion];
+ }
+ else /* No region, default value 0. */
+ *pu32Data = 0;
+
+ /* Memory access is always 32bit big. */
+ pThis->u32DiagMemAddr += sizeof(uint32_t);
+}
+
+/**
+ * Handles a write to the diagnostic memory address register.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param u32Addr Address to write.
+ */
+static void lsilogicR3DiagRegAddressWrite(PLSILOGICSCSI pThis, uint32_t u32Addr)
+{
+ pThis->u32DiagMemAddr = u32Addr & ~UINT32_C(0x3); /* 32bit alignment. */
+}
+
+/**
+ * Handles a read from the diagnostic memory address register.
+ *
+ * @returns nothing.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pu32Addr Where to store the current address.
+ */
+static void lsilogicR3DiagRegAddressRead(PLSILOGICSCSI pThis, uint32_t *pu32Addr)
+{
+ *pu32Addr = pThis->u32DiagMemAddr;
+}
+
/**
* Processes a given Request from the guest
*
* @returns VBox status code.
- * @param pLsiLogic Pointer to the device instance.
- * @param pMessageHdr Pointer to the message header of the request.
- * @param pReply Pointer to the reply.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pMessageHdr Pointer to the message header of the request.
+ * @param pReply Pointer to the reply.
*/
-static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr pMessageHdr, PMptReplyUnion pReply)
+static int lsilogicR3ProcessMessageRequest(PLSILOGICSCSI pThis, PMptMessageHdr pMessageHdr, PMptReplyUnion pReply)
{
int rc = VINF_SUCCESS;
bool fForceReplyPostFifo = false;
-#ifdef DEBUG
+# ifdef LOG_ENABLED
if (pMessageHdr->u8Function < RT_ELEMENTS(g_apszMPTFunctionNames))
Log(("Message request function: %s\n", g_apszMPTFunctionNames[pMessageHdr->u8Function]));
else
Log(("Message request function: <unknown>\n"));
-#endif
+# endif
memset(pReply, 0, sizeof(MptReplyUnion));
@@ -765,61 +1040,87 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr
PMptIOCInitRequest pIOCInitReq = (PMptIOCInitRequest)pMessageHdr;
/* Update configuration values. */
- pLsiLogic->enmWhoInit = (LSILOGICWHOINIT)pIOCInitReq->u8WhoInit;
- pLsiLogic->cbReplyFrame = pIOCInitReq->u16ReplyFrameSize;
- pLsiLogic->cMaxBuses = pIOCInitReq->u8MaxBuses;
- pLsiLogic->cMaxDevices = pIOCInitReq->u8MaxDevices;
- pLsiLogic->u32HostMFAHighAddr = pIOCInitReq->u32HostMfaHighAddr;
- pLsiLogic->u32SenseBufferHighAddr = pIOCInitReq->u32SenseBufferHighAddr;
-
- if (pLsiLogic->enmState == LSILOGICSTATE_READY)
+ pThis->enmWhoInit = (LSILOGICWHOINIT)pIOCInitReq->u8WhoInit;
+ pThis->cbReplyFrame = pIOCInitReq->u16ReplyFrameSize;
+ pThis->cMaxBuses = pIOCInitReq->u8MaxBuses;
+ pThis->cMaxDevices = pIOCInitReq->u8MaxDevices;
+ pThis->u32HostMFAHighAddr = pIOCInitReq->u32HostMfaHighAddr;
+ pThis->u32SenseBufferHighAddr = pIOCInitReq->u32SenseBufferHighAddr;
+
+ if (pThis->enmState == LSILOGICSTATE_READY)
{
- pLsiLogic->enmState = LSILOGICSTATE_OPERATIONAL;
+ pThis->enmState = LSILOGICSTATE_OPERATIONAL;
}
/* Return reply. */
pReply->IOCInit.u8MessageLength = 5;
- pReply->IOCInit.u8WhoInit = pLsiLogic->enmWhoInit;
- pReply->IOCInit.u8MaxDevices = pLsiLogic->cMaxDevices;
- pReply->IOCInit.u8MaxBuses = pLsiLogic->cMaxBuses;
+ pReply->IOCInit.u8WhoInit = pThis->enmWhoInit;
+ pReply->IOCInit.u8MaxDevices = pThis->cMaxDevices;
+ pReply->IOCInit.u8MaxBuses = pThis->cMaxBuses;
break;
}
case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS:
{
pReply->IOCFacts.u8MessageLength = 15; /* 15 32bit dwords. */
- if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
{
pReply->IOCFacts.u16MessageVersion = 0x0102; /* Version from the specification. */
- pReply->IOCFacts.u8NumberOfPorts = pLsiLogic->cPorts;
+ pReply->IOCFacts.u8NumberOfPorts = pThis->cPorts;
}
- else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
{
pReply->IOCFacts.u16MessageVersion = 0x0105; /* Version from the specification. */
- pReply->IOCFacts.u8NumberOfPorts = pLsiLogic->cPorts;
+ pReply->IOCFacts.u8NumberOfPorts = pThis->cPorts;
}
else
- AssertMsgFailed(("Invalid controller type %d\n", pLsiLogic->enmCtrlType));
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
pReply->IOCFacts.u8IOCNumber = 0; /* PCI function number. */
pReply->IOCFacts.u16IOCExceptions = 0;
pReply->IOCFacts.u8MaxChainDepth = LSILOGICSCSI_MAXIMUM_CHAIN_DEPTH;
- pReply->IOCFacts.u8WhoInit = pLsiLogic->enmWhoInit;
+ pReply->IOCFacts.u8WhoInit = pThis->enmWhoInit;
pReply->IOCFacts.u8BlockSize = 12; /* Block size in 32bit dwords. This is the largest request we can get (SCSI I/O). */
pReply->IOCFacts.u8Flags = 0; /* Bit 0 is set if the guest must upload the FW prior to using the controller. Obviously not needed here. */
- pReply->IOCFacts.u16ReplyQueueDepth = pLsiLogic->cReplyQueueEntries - 1; /* One entry is always free. */
+ pReply->IOCFacts.u16ReplyQueueDepth = pThis->cReplyQueueEntries - 1; /* One entry is always free. */
pReply->IOCFacts.u16RequestFrameSize = 128; /* @todo Figure out where it is needed. */
- pReply->IOCFacts.u16ProductID = 0xcafe; /* Our own product ID :) */
- pReply->IOCFacts.u32CurrentHostMFAHighAddr = pLsiLogic->u32HostMFAHighAddr;
- pReply->IOCFacts.u16GlobalCredits = pLsiLogic->cRequestQueueEntries - 1; /* One entry is always free. */
+ pReply->IOCFacts.u32CurrentHostMFAHighAddr = pThis->u32HostMFAHighAddr;
+ pReply->IOCFacts.u16GlobalCredits = pThis->cRequestQueueEntries - 1; /* One entry is always free. */
pReply->IOCFacts.u8EventState = 0; /* Event notifications not enabled. */
- pReply->IOCFacts.u32CurrentSenseBufferHighAddr = pLsiLogic->u32SenseBufferHighAddr;
- pReply->IOCFacts.u16CurReplyFrameSize = pLsiLogic->cbReplyFrame;
- pReply->IOCFacts.u8MaxDevices = pLsiLogic->cMaxDevices;
- pReply->IOCFacts.u8MaxBuses = pLsiLogic->cMaxBuses;
- pReply->IOCFacts.u32FwImageSize = 0; /* No image needed. */
- pReply->IOCFacts.u32FWVersion = 0;
+ pReply->IOCFacts.u32CurrentSenseBufferHighAddr = pThis->u32SenseBufferHighAddr;
+ pReply->IOCFacts.u16CurReplyFrameSize = pThis->cbReplyFrame;
+ pReply->IOCFacts.u8MaxDevices = pThis->cMaxDevices;
+ pReply->IOCFacts.u8MaxBuses = pThis->cMaxBuses;
+
+ /* Check for a valid firmware image in the IOC memory which was downlaoded by tzhe guest earlier. */
+ PLSILOGICMEMREGN pRegion = lsilogicR3MemRegionFindByAddr(pThis, LSILOGIC_FWIMGHDR_LOAD_ADDRESS);
+
+ if (pRegion)
+ {
+ uint32_t offImgHdr = (LSILOGIC_FWIMGHDR_LOAD_ADDRESS - pRegion->u32AddrStart) / 4;
+ PFwImageHdr pFwImgHdr = (PFwImageHdr)&pRegion->au32Data[offImgHdr];
+
+ /* Check for the signature. */
+ /** @todo: Checksum validation. */
+ if ( pFwImgHdr->u32Signature1 == LSILOGIC_FWIMGHDR_SIGNATURE1
+ && pFwImgHdr->u32Signature2 == LSILOGIC_FWIMGHDR_SIGNATURE2
+ && pFwImgHdr->u32Signature3 == LSILOGIC_FWIMGHDR_SIGNATURE3)
+ {
+ LogFlowFunc(("IOC Facts: Found valid firmware image header in memory, using version (%#x), size (%d) and product ID (%#x) from there\n",
+ pFwImgHdr->u32FwVersion, pFwImgHdr->u32ImageSize, pFwImgHdr->u16ProductId));
+
+ pReply->IOCFacts.u16ProductID = pFwImgHdr->u16ProductId;
+ pReply->IOCFacts.u32FwImageSize = pFwImgHdr->u32ImageSize;
+ pReply->IOCFacts.u32FWVersion = pFwImgHdr->u32FwVersion;
+ }
+ }
+ else
+ {
+ pReply->IOCFacts.u16ProductID = 0xcafe; /* Our own product ID :) */
+ pReply->IOCFacts.u32FwImageSize = 0; /* No image needed. */
+ pReply->IOCFacts.u32FWVersion = 0;
+ }
break;
}
case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS:
@@ -829,10 +1130,10 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr
pReply->PortFacts.u8MessageLength = 10;
pReply->PortFacts.u8PortNumber = pPortFactsReq->u8PortNumber;
- if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
{
/* This controller only supports one bus with bus number 0. */
- if (pPortFactsReq->u8PortNumber >= pLsiLogic->cPorts)
+ if (pPortFactsReq->u8PortNumber >= pThis->cPorts)
{
pReply->PortFacts.u8PortType = 0; /* Not existant. */
}
@@ -847,25 +1148,25 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr
pReply->PortFacts.u16MaxLANBuckets = 0; /* Only for the LAN controller. */
}
}
- else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
{
- if (pPortFactsReq->u8PortNumber >= pLsiLogic->cPorts)
+ if (pPortFactsReq->u8PortNumber >= pThis->cPorts)
{
pReply->PortFacts.u8PortType = 0; /* Not existant. */
}
else
{
pReply->PortFacts.u8PortType = 0x30; /* SAS Port. */
- pReply->PortFacts.u16MaxDevices = pLsiLogic->cPorts;
+ pReply->PortFacts.u16MaxDevices = pThis->cPorts;
pReply->PortFacts.u16ProtocolFlags = RT_BIT(3) | RT_BIT(0); /* SCSI initiator and LUN supported. */
- pReply->PortFacts.u16PortSCSIID = pLsiLogic->cPorts;
+ pReply->PortFacts.u16PortSCSIID = pThis->cPorts;
pReply->PortFacts.u16MaxPersistentIDs = 0;
pReply->PortFacts.u16MaxPostedCmdBuffers = 0; /* Only applies for target mode which we dont support. */
pReply->PortFacts.u16MaxLANBuckets = 0; /* Only for the LAN controller. */
}
}
else
- AssertMsgFailed(("Invalid controller type %d\n", pLsiLogic->enmCtrlType));
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
break;
}
case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE:
@@ -885,9 +1186,9 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr
PMptEventNotificationRequest pEventNotificationReq = (PMptEventNotificationRequest)pMessageHdr;
if (pEventNotificationReq->u8Switch)
- pLsiLogic->fEventNotificationEnabled = true;
+ pThis->fEventNotificationEnabled = true;
else
- pLsiLogic->fEventNotificationEnabled = false;
+ pThis->fEventNotificationEnabled = false;
pReply->EventNotification.u16EventDataLength = 1; /* 1 32bit D-Word. */
pReply->EventNotification.u8MessageLength = 8;
@@ -895,7 +1196,7 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr
pReply->EventNotification.u8AckRequired = 0;
pReply->EventNotification.u32Event = MPT_EVENT_EVENT_CHANGE;
pReply->EventNotification.u32EventContext = 0;
- pReply->EventNotification.u32EventData = pLsiLogic->fEventNotificationEnabled ? 1 : 0;
+ pReply->EventNotification.u32EventData = pThis->fEventNotificationEnabled ? 1 : 0;
break;
}
@@ -908,7 +1209,7 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr
{
PMptConfigurationRequest pConfigurationReq = (PMptConfigurationRequest)pMessageHdr;
- rc = lsilogicProcessConfigurationRequest(pLsiLogic, pConfigurationReq, &pReply->Configuration);
+ rc = lsilogicR3ProcessConfigurationRequest(pThis, pConfigurationReq, &pReply->Configuration);
AssertRC(rc);
break;
}
@@ -926,6 +1227,7 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr
//PMptFWDownloadRequest pFWDownloadReq = (PMptFWDownloadRequest)pMessageHdr;
pReply->FWDownload.u8MessageLength = 5;
+ LogFlowFunc(("FW Download request issued\n"));
break;
}
case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST: /* Should be handled already. */
@@ -937,27 +1239,24 @@ static int lsilogicProcessMessageRequest(PLSILOGICSCSI pLsiLogic, PMptMessageHdr
pReply->Header.u8Function = pMessageHdr->u8Function;
pReply->Header.u32MessageContext = pMessageHdr->u32MessageContext;
- lsilogicFinishAddressReply(pLsiLogic, pReply, fForceReplyPostFifo);
+ lsilogicFinishAddressReply(pThis, pReply, fForceReplyPostFifo);
return rc;
}
-#endif
+
+#endif /* IN_RING3 */
/**
* Writes a value to a register at a given offset.
*
* @returns VBox status code.
- * @param pThis Pointer to the LsiLogic SCSI controller instance data.
- * @param uOffset Offset of the register to write.
- * @param pv Pointer to the value to write
- * @param cb Number of bytes to write.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param offReg Offset of the register to write.
+ * @param u32 The value being written.
*/
-static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void const *pv, unsigned cb)
+static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t offReg, uint32_t u32)
{
- uint32_t u32 = *(uint32_t *)pv;
-
- LogFlowFunc(("pThis=%#p uOffset=%#x pv=%#p{%.*Rhxs} cb=%u\n", pThis, uOffset, pv, cb, pv, cb));
-
- switch (uOffset)
+ LogFlowFunc(("pThis=%#p offReg=%#x u32=%#x\n", pThis, offReg, u32));
+ switch (offReg)
{
case LSILOGIC_REG_REPLY_QUEUE:
{
@@ -986,10 +1285,16 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con
/* Send notification to R3 if there is not one send already. */
if (!ASMAtomicXchgBool(&pThis->fNotificationSend, true))
{
+#ifdef IN_RC
PPDMQUEUEITEMCORE pNotificationItem = PDMQueueAlloc(pThis->CTX_SUFF(pNotificationQueue));
AssertPtr(pNotificationItem);
PDMQueueInsert(pThis->CTX_SUFF(pNotificationQueue), pNotificationItem);
+#else
+ LogFlowFunc(("Signal event semaphore\n"));
+ int rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess);
+ AssertRC(rc);
+#endif
}
break;
}
@@ -1002,14 +1307,20 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con
* The guest needs to wait with posting new messages here until the bit is cleared.
* Because the guest is not continuing execution while we are here we can skip this.
*/
- if (!pThis->fDoorbellInProgress)
+ if (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_NOT_IN_USE)
{
uint32_t uFunction = LSILOGIC_REG_DOORBELL_GET_FUNCTION(u32);
switch (uFunction)
{
+ case LSILOGIC_DOORBELL_FUNCTION_IO_UNIT_RESET:
case LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET:
{
+ /*
+ * The I/O unit reset does much more on real hardware like
+ * reloading the firmware, nothing we need to do here,
+ * so this is like the IOC message unit reset.
+ */
pThis->enmState = LSILOGICSTATE_RESET;
/* Reset interrupt status. */
@@ -1023,12 +1334,10 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con
pThis->uReplyPostQueueNextAddressRead = 0;
pThis->uRequestQueueNextEntryFreeWrite = 0;
pThis->uRequestQueueNextAddressRead = 0;
- pThis->enmState = LSILOGICSTATE_READY;
- break;
- }
- case LSILOGIC_DOORBELL_FUNCTION_IO_UNIT_RESET:
- {
- AssertMsgFailed(("todo\n"));
+
+ /* Only the IOC message unit reset transisionts to the ready state. */
+ if (uFunction == LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET)
+ pThis->enmState = LSILOGICSTATE_READY;
break;
}
case LSILOGIC_DOORBELL_FUNCTION_HANDSHAKE:
@@ -1037,21 +1346,23 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con
pThis->iMessage = 0;
AssertMsg(pThis->cMessage <= RT_ELEMENTS(pThis->aMessage),
("Message doesn't fit into the buffer, cMessage=%u", pThis->cMessage));
- pThis->fDoorbellInProgress = true;
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE;
/* Update the interrupt status to notify the guest that a doorbell function was started. */
lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
break;
}
case LSILOGIC_DOORBELL_FUNCTION_REPLY_FRAME_REMOVAL:
{
- AssertMsgFailed(("todo\n"));
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW;
+ /* Update the interrupt status to notify the guest that a doorbell function was started. */
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
break;
}
default:
AssertMsgFailed(("Unknown function %u to perform\n", uFunction));
}
}
- else
+ else if (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
{
/*
* We are already performing a doorbell function.
@@ -1070,7 +1381,7 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con
#ifdef IN_RING3
if (pThis->iMessage == pThis->cMessage)
{
- int rc = lsilogicProcessMessageRequest(pThis, (PMptMessageHdr)pThis->aMessage, &pThis->ReplyBuffer);
+ int rc = lsilogicR3ProcessMessageRequest(pThis, (PMptMessageHdr)pThis->aMessage, &pThis->ReplyBuffer);
AssertRC(rc);
}
#endif
@@ -1093,17 +1404,26 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con
* We do not use lsilogicSetInterrupt here because the interrupt status
* is updated afterwards anyway.
*/
- if ( (pThis->fDoorbellInProgress)
+ if ( (pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
&& (pThis->cMessage == pThis->iMessage))
{
if (pThis->uNextReplyEntryRead == pThis->cReplySize)
{
/* Reply finished. Reset doorbell in progress status. */
Log(("%s: Doorbell function finished\n", __FUNCTION__));
- pThis->fDoorbellInProgress = false;
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
}
ASMAtomicOrU32(&pThis->uInterruptStatus, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
}
+ else if ( pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_NOT_IN_USE
+ && pThis->enmDoorbellState != LSILOGICDOORBELLSTATE_FN_HANDSHAKE)
+ {
+ /* Reply frame removal, check whether the reply free queue is empty. */
+ if ( pThis->uReplyFreeQueueNextAddressRead == pThis->uReplyFreeQueueNextEntryFreeWrite
+ && pThis->enmDoorbellState == LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW)
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
+ ASMAtomicOrU32(&pThis->uInterruptStatus, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ }
lsilogicUpdateInterrupt(pThis);
break;
@@ -1121,6 +1441,7 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con
/* Any value will cause a reset and disabling access. */
pThis->fDiagnosticEnabled = false;
pThis->iDiagnosticAccess = 0;
+ pThis->fDiagRegsEnabled = false;
}
else if ((u32 & 0xf) == g_lsilogicDiagnosticAccess[pThis->iDiagnosticAccess])
{
@@ -1143,15 +1464,42 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con
}
case LSILOGIC_REG_HOST_DIAGNOSTIC:
{
+ if (pThis->fDiagnosticEnabled)
+ {
#ifndef IN_RING3
- return VINF_IOM_R3_IOPORT_WRITE;
+ return VINF_IOM_R3_MMIO_WRITE;
#else
- if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER)
+ if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER)
+ lsilogicR3HardReset(pThis);
+ else if (u32 & LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE)
+ pThis->fDiagRegsEnabled = true;
+#endif
+ }
+ break;
+ }
+ case LSILOGIC_REG_DIAG_RW_DATA:
+ {
+ if (pThis->fDiagRegsEnabled)
{
- lsilogicHardReset(pThis);
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_WRITE;
+#else
+ lsilogicR3DiagRegDataWrite(pThis, u32);
+#endif
}
break;
+ }
+ case LSILOGIC_REG_DIAG_RW_ADDRESS:
+ {
+ if (pThis->fDiagRegsEnabled)
+ {
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_WRITE;
+#else
+ lsilogicR3DiagRegAddressWrite(pThis, u32);
#endif
+ }
+ break;
}
default: /* Ignore. */
{
@@ -1165,28 +1513,21 @@ static int lsilogicRegisterWrite(PLSILOGICSCSI pThis, uint32_t uOffset, void con
* Reads the content of a register at a given offset.
*
* @returns VBox status code.
- * @param pThis Pointer to the LsiLogic SCSI controller instance data.
- * @param uOffset Offset of the register to read.
- * @param pv Where to store the content of the register.
- * @param cb Number of bytes to read.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param offReg Offset of the register to read.
+ * @param pu32 Where to store the content of the register.
*/
-static int lsilogicRegisterRead(PLSILOGICSCSI pThis, uint32_t uOffset, void *pv, unsigned cb)
+static int lsilogicRegisterRead(PLSILOGICSCSI pThis, uint32_t offReg, uint32_t *pu32)
{
int rc = VINF_SUCCESS;
uint32_t u32 = 0;
+ Assert(!(offReg & 3));
/* Align to a 4 byte offset. */
- switch (uOffset & ~3)
+ switch (offReg)
{
case LSILOGIC_REG_REPLY_QUEUE:
{
- /*
- * Non 4-byte access may cause real strange behavior because the data is part of a physical guest address.
- * But some drivers use 1-byte access to scan for SCSI controllers.
- */
- if (RT_UNLIKELY(cb != 4))
- LogFlowFunc((": cb is not 4 (%u)\n", cb));
-
rc = PDMCritSectEnter(&pThis->ReplyPostQueueCritSect, VINF_IOM_R3_MMIO_READ);
if (rc != VINF_SUCCESS)
break;
@@ -1215,23 +1556,61 @@ static int lsilogicRegisterRead(PLSILOGICSCSI pThis, uint32_t uOffset, void *pv,
case LSILOGIC_REG_DOORBELL:
{
u32 = LSILOGIC_REG_DOORBELL_SET_STATE(pThis->enmState);
- u32 |= LSILOGIC_REG_DOORBELL_SET_USED(pThis->fDoorbellInProgress);
+ u32 |= LSILOGIC_REG_DOORBELL_SET_USED(pThis->enmDoorbellState);
u32 |= LSILOGIC_REG_DOORBELL_SET_WHOINIT(pThis->enmWhoInit);
/*
* If there is a doorbell function in progress we pass the return value
* instead of the status code. We transfer 16bit of the reply
* during one read.
*/
- if (pThis->fDoorbellInProgress)
+ switch (pThis->enmDoorbellState)
{
- /* Return next 16bit value. */
- u32 |= pThis->ReplyBuffer.au16Reply[pThis->uNextReplyEntryRead++];
- }
- else
- {
- /* We return the status code of the I/O controller. */
- u32 |= pThis->u16IOCFaultCode;
+ case LSILOGICDOORBELLSTATE_NOT_IN_USE:
+ /* We return the status code of the I/O controller. */
+ u32 |= pThis->u16IOCFaultCode;
+ break;
+ case LSILOGICDOORBELLSTATE_FN_HANDSHAKE:
+ /* Return next 16bit value. */
+ u32 |= pThis->ReplyBuffer.au16Reply[pThis->uNextReplyEntryRead++];
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ break;
+ case LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW:
+ {
+ uint32_t cReplyFrames = lsilogicReplyFreeQueueGetFrameCount(pThis);
+
+ u32 |= cReplyFrames & UINT32_C(0xffff);
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ break;
+ }
+ case LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH:
+ {
+ uint32_t cReplyFrames = lsilogicReplyFreeQueueGetFrameCount(pThis);
+
+ u32 |= cReplyFrames >> 16;
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ break;
+ }
+ case LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW:
+ if (pThis->uReplyFreeQueueNextEntryFreeWrite != pThis->uReplyFreeQueueNextAddressRead)
+ {
+ u32 |= pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead] & UINT32_C(0xffff);
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ }
+ break;
+ case LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH:
+ u32 |= pThis->CTX_SUFF(pReplyFreeQueueBase)[pThis->uReplyFreeQueueNextAddressRead] >> 16;
+ pThis->uReplyFreeQueueNextAddressRead++;
+ pThis->uReplyFreeQueueNextAddressRead %= pThis->cReplyQueueEntries;
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW;
+ lsilogicSetInterrupt(pThis, LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL);
+ break;
+ default:
+ AssertMsgFailed(("Invalid doorbell state %d\n", pThis->enmDoorbellState));
}
+
break;
}
case LSILOGIC_REG_HOST_INTR_STATUS:
@@ -1247,99 +1626,130 @@ static int lsilogicRegisterRead(PLSILOGICSCSI pThis, uint32_t uOffset, void *pv,
case LSILOGIC_REG_HOST_DIAGNOSTIC:
{
if (pThis->fDiagnosticEnabled)
- u32 = LSILOGIC_REG_HOST_DIAGNOSTIC_DRWE;
- else
- u32 = 0;
+ u32 |= LSILOGIC_REG_HOST_DIAGNOSTIC_DRWE;
+ if (pThis->fDiagRegsEnabled)
+ u32 |= LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE;
break;
}
- case LSILOGIC_REG_TEST_BASE_ADDRESS: /* The spec doesn't say anything about these registers, so we just ignore them */
case LSILOGIC_REG_DIAG_RW_DATA:
- case LSILOGIC_REG_DIAG_RW_ADDRESS:
- default: /* Ignore. */
{
- break;
- }
- }
-
- /* Clip data according to the read size. */
- switch (cb)
- {
- case 4:
- {
- *(uint32_t *)pv = u32;
- break;
+ if (pThis->fDiagRegsEnabled)
+ {
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_READ;
+#else
+ lsilogicR3DiagRegDataRead(pThis, &u32);
+#endif
+ }
}
- case 2:
+ case LSILOGIC_REG_DIAG_RW_ADDRESS:
{
- uint8_t uBitsOff = (uOffset - (uOffset & 3))*8;
-
- u32 &= (0xffff << uBitsOff);
- *(uint16_t *)pv = (uint16_t)(u32 >> uBitsOff);
- break;
+ if (pThis->fDiagRegsEnabled)
+ {
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_READ;
+#else
+ lsilogicR3DiagRegAddressRead(pThis, &u32);
+#endif
+ }
}
- case 1:
+ case LSILOGIC_REG_TEST_BASE_ADDRESS: /* The spec doesn't say anything about these registers, so we just ignore them */
+ default: /* Ignore. */
{
- uint8_t uBitsOff = (uOffset - (uOffset & 3))*8;
-
- u32 &= (0xff << uBitsOff);
- *(uint8_t *)pv = (uint8_t)(u32 >> uBitsOff);
+ /** @todo LSILOGIC_REG_DIAG_* should return all F's when accessed by MMIO. We
+ * return 0. Likely to apply to undefined offsets as well. */
break;
}
- default:
- AssertMsgFailed(("Invalid access size %u\n", cb));
}
- LogFlowFunc(("pThis=%#p uOffset=%#x pv=%#p{%.*Rhxs} cb=%u\n", pThis, uOffset, pv, cb, pv, cb));
-
+ *pu32 = u32;
+ LogFlowFunc(("pThis=%#p offReg=%#x u32=%#x\n", pThis, offReg, u32));
return rc;
}
-PDMBOTHCBDECL(int) lsilogicIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
- RTIOPORT Port, uint32_t u32, unsigned cb)
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+PDMBOTHCBDECL(int) lsilogicIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
- PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
- uint32_t uOffset = Port - pThis->IOPortBase;
-
- Assert(cb <= 4);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ uint32_t offReg = Port - pThis->IOPortBase;
+ int rc;
- int rc = lsilogicRegisterWrite(pThis, uOffset, &u32, cb);
- if (rc == VINF_IOM_R3_MMIO_WRITE)
- rc = VINF_IOM_R3_IOPORT_WRITE;
+ if (!(offReg & 3))
+ {
+ rc = lsilogicRegisterWrite(pThis, offReg, u32);
+ if (rc == VINF_IOM_R3_MMIO_WRITE)
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+ }
+ else
+ {
+ Log(("lsilogicIOPortWrite: Ignoring misaligned write - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb));
+ rc = VINF_SUCCESS;
+ }
return rc;
}
-PDMBOTHCBDECL(int) lsilogicIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
- RTIOPORT Port, uint32_t *pu32, unsigned cb)
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+PDMBOTHCBDECL(int) lsilogicIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
- PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
- uint32_t uOffset = Port - pThis->IOPortBase;
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ uint32_t offReg = Port - pThis->IOPortBase;
- Assert(cb <= 4);
-
- int rc = lsilogicRegisterRead(pThis, uOffset, pu32, cb);
+ int rc = lsilogicRegisterRead(pThis, offReg & ~(uint32_t)3, pu32);
if (rc == VINF_IOM_R3_MMIO_READ)
rc = VINF_IOM_R3_IOPORT_READ;
return rc;
}
-PDMBOTHCBDECL(int) lsilogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser,
- RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
+/**
+ * @callback_method_impl{FNIOMMMIOWRITE}
+ */
+PDMBOTHCBDECL(int) lsilogicMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
{
- PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
- uint32_t uOffset = GCPhysAddr - pThis->GCPhysMMIOBase;
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ uint32_t offReg = GCPhysAddr - pThis->GCPhysMMIOBase;
+ uint32_t u32;
+ int rc;
+
+ /* See comments in lsilogicR3Map regarding size and alignment. */
+ if (cb == 4)
+ u32 = *(uint32_t const *)pv;
+ else
+ {
+ if (cb > 4)
+ u32 = *(uint32_t const *)pv;
+ else if (cb >= 2)
+ u32 = *(uint16_t const *)pv;
+ else
+ u32 = *(uint8_t const *)pv;
+ Log(("lsilogicMMIOWrite: Non-DWORD write access - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb));
+ }
- return lsilogicRegisterWrite(pThis, uOffset, pv, cb);
+ if (!(offReg & 3))
+ rc = lsilogicRegisterWrite(pThis, offReg, u32);
+ else
+ {
+ Log(("lsilogicIOPortWrite: Ignoring misaligned write - offReg=%#x u32=%#x cb=%#x\n", offReg, u32, cb));
+ rc = VINF_SUCCESS;
+ }
+ return rc;
}
-PDMBOTHCBDECL(int) lsilogicMMIORead(PPDMDEVINS pDevIns, void *pvUser,
- RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
+/**
+ * @callback_method_impl{FNIOMMMIOREAD}
+ */
+PDMBOTHCBDECL(int) lsilogicMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
{
- PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
- uint32_t uOffset = GCPhysAddr - pThis->GCPhysMMIOBase;
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ uint32_t offReg = GCPhysAddr - pThis->GCPhysMMIOBase;
+ Assert(!(offReg & 3)); Assert(cb == 4);
- return lsilogicRegisterRead(pThis, uOffset, pv, cb);
+ return lsilogicRegisterRead(pThis, offReg, (uint32_t *)pv);
}
PDMBOTHCBDECL(int) lsilogicDiagnosticWrite(PPDMDEVINS pDevIns, void *pvUser,
@@ -1364,545 +1774,320 @@ PDMBOTHCBDECL(int) lsilogicDiagnosticRead(PPDMDEVINS pDevIns, void *pvUser,
#ifdef IN_RING3
+# ifdef LOG_ENABLED
/**
- * Copies a contiguous buffer into the scatter gather list provided by the guest.
+ * Dump an SG entry.
*
- * @returns nothing
- * @param pTaskState Pointer to the task state which contains the SGL.
- * @param pvBuf Pointer to the buffer to copy.
- * @param cbCopy Number of bytes to copy.
+ * @returns nothing.
+ * @param pSGEntry Pointer to the SG entry to dump
*/
-static void lsilogicScatterGatherListCopyFromBuffer(PLSILOGICTASKSTATE pTaskState, void *pvBuf, size_t cbCopy)
+static void lsilogicDumpSGEntry(PMptSGEntryUnion pSGEntry)
{
- unsigned cSGEntry = 0;
- PRTSGSEG pSGEntry = &pTaskState->pSGListHead[cSGEntry];
- uint8_t *pu8Buf = (uint8_t *)pvBuf;
-
- while (cSGEntry < pTaskState->cSGListEntries)
+ if (LogIsEnabled())
{
- size_t cbToCopy = (cbCopy < pSGEntry->cbSeg) ? cbCopy : pSGEntry->cbSeg;
-
- memcpy(pSGEntry->pvSeg, pu8Buf, cbToCopy);
-
- cbCopy -= cbToCopy;
- /* We finished. */
- if (!cbCopy)
- break;
-
- /* Advance the buffer. */
- pu8Buf += cbToCopy;
+ switch (pSGEntry->Simple32.u2ElementType)
+ {
+ case MPTSGENTRYTYPE_SIMPLE:
+ {
+ Log(("%s: Dumping info for SIMPLE SG entry:\n", __FUNCTION__));
+ Log(("%s: u24Length=%u\n", __FUNCTION__, pSGEntry->Simple32.u24Length));
+ Log(("%s: fEndOfList=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfList));
+ Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.f64BitAddress));
+ Log(("%s: fBufferContainsData=%d\n", __FUNCTION__, pSGEntry->Simple32.fBufferContainsData));
+ Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.fLocalAddress));
+ Log(("%s: fEndOfBuffer=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfBuffer));
+ Log(("%s: fLastElement=%d\n", __FUNCTION__, pSGEntry->Simple32.fLastElement));
+ Log(("%s: u32DataBufferAddressLow=%u\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
+ if (pSGEntry->Simple32.f64BitAddress)
+ {
+ Log(("%s: u32DataBufferAddressHigh=%u\n", __FUNCTION__, pSGEntry->Simple64.u32DataBufferAddressHigh));
+ Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__,
+ ((uint64_t)pSGEntry->Simple64.u32DataBufferAddressHigh << 32)
+ | pSGEntry->Simple64.u32DataBufferAddressLow));
+ }
+ else
+ Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
- /* Go to the next entry in the list. */
- pSGEntry++;
- cSGEntry++;
+ break;
+ }
+ case MPTSGENTRYTYPE_CHAIN:
+ {
+ Log(("%s: Dumping info for CHAIN SG entry:\n", __FUNCTION__));
+ Log(("%s: u16Length=%u\n", __FUNCTION__, pSGEntry->Chain.u16Length));
+ Log(("%s: u8NExtChainOffset=%d\n", __FUNCTION__, pSGEntry->Chain.u8NextChainOffset));
+ Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Chain.f64BitAddress));
+ Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Chain.fLocalAddress));
+ Log(("%s: u32SegmentAddressLow=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow));
+ Log(("%s: u32SegmentAddressHigh=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressHigh));
+ if (pSGEntry->Chain.f64BitAddress)
+ Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__,
+ ((uint64_t)pSGEntry->Chain.u32SegmentAddressHigh << 32) | pSGEntry->Chain.u32SegmentAddressLow));
+ else
+ Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow));
+ break;
+ }
+ }
}
}
+# endif /* LOG_ENABLED */
/**
- * Copy a temporary buffer into a part of the guest scatter gather list
- * described by the given descriptor entry.
+ * Walks the guest S/G buffer calling the given copy worker for every buffer.
*
* @returns nothing.
- * @param pDevIns Pointer to the device instance data.
- * @param pSGInfo Pointer to the segment info structure which describes the guest segments
- * to write to which are unaligned.
+ * @param pDevIns Device instance data.
+ * @param pLsiReq LSI request state.
+ * @param cbCopy How much bytes to copy.
+ * @param pfnIoBufCopy Copy worker to call.
*/
-static void lsilogicCopyFromBufferIntoSGList(PPDMDEVINS pDevIns, PLSILOGICTASKSTATESGENTRY pSGInfo)
+static void lsilogicSgBufWalker(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, size_t cbCopy,
+ PFNLSILOGICIOBUFCOPY pfnIoBufCopy)
{
- RTGCPHYS GCPhysBuffer = pSGInfo->u.GCPhysAddrBufferUnaligned;
+ bool fEndOfList = false;
+ RTGCPHYS GCPhysSgEntryNext = pLsiReq->GCPhysSgStart;
+ RTGCPHYS GCPhysSegmentStart = pLsiReq->GCPhysSgStart;
+ uint32_t cChainOffsetNext = pLsiReq->cChainOffset;
+ uint8_t *pbBuf = (uint8_t *)pLsiReq->SegIoBuf.pvSeg;
+
+ /* Go through the list until we reach the end. */
+ while ( !fEndOfList
+ && cbCopy)
+ {
+ bool fEndOfSegment = false;
- AssertMsg(!pSGInfo->fGuestMemory, ("This is not possible\n"));
+ while ( !fEndOfSegment
+ && cbCopy)
+ {
+ MptSGEntryUnion SGEntry;
- /* Copy into SG entry. */
- PDMDevHlpPhysWrite(pDevIns, GCPhysBuffer, pSGInfo->pvBuf, pSGInfo->cbBuf);
+ Log(("%s: Reading SG entry from %RGp\n", __FUNCTION__, GCPhysSgEntryNext));
-}
+ /* Read the entry. */
+ PDMDevHlpPhysRead(pDevIns, GCPhysSgEntryNext, &SGEntry, sizeof(MptSGEntryUnion));
-/**
- * Copy a part of the guest scatter gather list into a temporary buffer.
- *
- * @returns nothing.
- * @param pDevIns Pointer to the device instance data.
- * @param pSGInfo Pointer to the segment info structure which describes the guest segments
- * to read from which are unaligned.
- */
-static void lsilogicCopyFromSGListIntoBuffer(PPDMDEVINS pDevIns, PLSILOGICTASKSTATESGENTRY pSGInfo)
-{
- RTGCPHYS GCPhysBuffer = pSGInfo->u.GCPhysAddrBufferUnaligned;
+# ifdef LOG_ENABLED
+ lsilogicDumpSGEntry(&SGEntry);
+# endif
- AssertMsg(!pSGInfo->fGuestMemory, ("This is not possible\n"));
+ AssertMsg(SGEntry.Simple32.u2ElementType == MPTSGENTRYTYPE_SIMPLE, ("Invalid SG entry type\n"));
- /* Copy into temporary buffer. */
- PDMDevHlpPhysRead(pDevIns, GCPhysBuffer, pSGInfo->pvBuf, pSGInfo->cbBuf);
-}
+ /* Check if this is a zero element and abort. */
+ if ( !SGEntry.Simple32.u24Length
+ && SGEntry.Simple32.fEndOfList
+ && SGEntry.Simple32.fEndOfBuffer)
+ return;
-static int lsilogicScatterGatherListAllocate(PLSILOGICTASKSTATE pTaskState, uint32_t cSGList, uint32_t cSGInfo, uint32_t cbUnaligned)
-{
- if (pTaskState->cSGListSize < cSGList)
- {
- /* The entries are not allocated yet or the number is too small. */
- if (pTaskState->cSGListSize)
- RTMemFree(pTaskState->pSGListHead);
-
- /* Allocate R3 scatter gather list. */
- pTaskState->pSGListHead = (PRTSGSEG)RTMemAllocZ(cSGList * sizeof(RTSGSEG));
- if (!pTaskState->pSGListHead)
- return VERR_NO_MEMORY;
-
- /* Reset usage statistics. */
- pTaskState->cSGListSize = cSGList;
- pTaskState->cSGListEntries = cSGList;
- pTaskState->cSGListTooBig = 0;
- }
- else if (pTaskState->cSGListSize > cSGList)
- {
- /*
- * The list is too big. Increment counter.
- * So that the destroying function can free
- * the list if it is too big too many times
- * in a row.
- */
- pTaskState->cSGListEntries = cSGList;
- pTaskState->cSGListTooBig++;
- }
- else
- {
- /*
- * Needed entries matches current size.
- * Reset counter.
- */
- pTaskState->cSGListEntries = cSGList;
- pTaskState->cSGListTooBig = 0;
- }
+ uint32_t cbCopyThis = SGEntry.Simple32.u24Length;
+ RTGCPHYS GCPhysAddrDataBuffer = SGEntry.Simple32.u32DataBufferAddressLow;
- if (pTaskState->cSGInfoSize < cSGInfo)
- {
- /* The entries are not allocated yet or the number is too small. */
- if (pTaskState->cSGInfoSize)
- RTMemFree(pTaskState->paSGEntries);
-
- pTaskState->paSGEntries = (PLSILOGICTASKSTATESGENTRY)RTMemAllocZ(cSGInfo * sizeof(LSILOGICTASKSTATESGENTRY));
- if (!pTaskState->paSGEntries)
- return VERR_NO_MEMORY;
-
- /* Reset usage statistics. */
- pTaskState->cSGInfoSize = cSGInfo;
- pTaskState->cSGInfoEntries = cSGInfo;
- pTaskState->cSGInfoTooBig = 0;
- }
- else if (pTaskState->cSGInfoSize > cSGInfo)
- {
- /*
- * The list is too big. Increment counter.
- * So that the destroying function can free
- * the list if it is too big too many times
- * in a row.
- */
- pTaskState->cSGInfoEntries = cSGInfo;
- pTaskState->cSGInfoTooBig++;
- }
- else
- {
- /*
- * Needed entries matches current size.
- * Reset counter.
- */
- pTaskState->cSGInfoEntries = cSGInfo;
- pTaskState->cSGInfoTooBig = 0;
- }
+ if (SGEntry.Simple32.f64BitAddress)
+ {
+ GCPhysAddrDataBuffer |= ((uint64_t)SGEntry.Simple64.u32DataBufferAddressHigh) << 32;
+ GCPhysSgEntryNext += sizeof(MptSGEntrySimple64);
+ }
+ else
+ GCPhysSgEntryNext += sizeof(MptSGEntrySimple32);
- if (pTaskState->cbBufferUnaligned < cbUnaligned)
- {
- if (pTaskState->pvBufferUnaligned)
- RTMemPageFree(pTaskState->pvBufferUnaligned, pTaskState->cbBufferUnaligned);
+ pfnIoBufCopy(pDevIns, GCPhysAddrDataBuffer, pbBuf, cbCopyThis);
+ pbBuf += cbCopyThis;
+ cbCopy -= cbCopyThis;
- Log(("%s: Allocating buffer for unaligned segments cbUnaligned=%u\n", __FUNCTION__, cbUnaligned));
+ /* Check if we reached the end of the list. */
+ if (SGEntry.Simple32.fEndOfList)
+ {
+ /* We finished. */
+ fEndOfSegment = true;
+ fEndOfList = true;
+ }
+ else if (SGEntry.Simple32.fLastElement)
+ fEndOfSegment = true;
+ } /* while (!fEndOfSegment) */
- pTaskState->pvBufferUnaligned = RTMemPageAlloc(cbUnaligned);
- if (!pTaskState->pvBufferUnaligned)
- return VERR_NO_MEMORY;
+ /* Get next chain element. */
+ if (cChainOffsetNext)
+ {
+ MptSGEntryChain SGEntryChain;
- pTaskState->cbBufferUnaligned = cbUnaligned;
- }
+ PDMDevHlpPhysRead(pDevIns, GCPhysSegmentStart + cChainOffsetNext, &SGEntryChain, sizeof(MptSGEntryChain));
- /* Make debugging easier. */
-#ifdef DEBUG
- memset(pTaskState->pSGListHead, 0, pTaskState->cSGListSize * sizeof(RTSGSEG));
- memset(pTaskState->paSGEntries, 0, pTaskState->cSGInfoSize * sizeof(LSILOGICTASKSTATESGENTRY));
- if (pTaskState->pvBufferUnaligned)
- memset(pTaskState->pvBufferUnaligned, 0, pTaskState->cbBufferUnaligned);
-#endif
- return VINF_SUCCESS;
+ AssertMsg(SGEntryChain.u2ElementType == MPTSGENTRYTYPE_CHAIN, ("Invalid SG entry type\n"));
+
+ /* Set the next address now. */
+ GCPhysSgEntryNext = SGEntryChain.u32SegmentAddressLow;
+ if (SGEntryChain.f64BitAddress)
+ GCPhysSgEntryNext |= ((uint64_t)SGEntryChain.u32SegmentAddressHigh) << 32;
+
+ GCPhysSegmentStart = GCPhysSgEntryNext;
+ cChainOffsetNext = SGEntryChain.u8NextChainOffset * sizeof(uint32_t);
+ }
+ } /* while (!fEndOfList) */
+}
+
+static DECLCALLBACK(void) lsilogicCopyFromGuest(PPDMDEVINS pDevIns, RTGCPHYS GCPhysIoBuf,
+ void *pvBuf, size_t cbCopy)
+{
+ PDMDevHlpPhysRead(pDevIns, GCPhysIoBuf, pvBuf, cbCopy);
+}
+
+static DECLCALLBACK(void) lsilogicCopyToGuest(PPDMDEVINS pDevIns, RTGCPHYS GCPhysIoBuf,
+ void *pvBuf, size_t cbCopy)
+{
+ PDMDevHlpPCIPhysWrite(pDevIns, GCPhysIoBuf, pvBuf, cbCopy);
}
/**
- * Destroy a scatter gather list.
+ * Copy from a guest S/G buffer to the I/O buffer.
*
* @returns nothing.
- * @param pLsiLogic Pointer to the LsiLogic SCSI controller.
- * @param pTaskState Pointer to the task state.
+ * @param pDevIns Device instance data.
+ * @param pLsiReq Request data.
+ * @param cbCopy How much to copy over.
*/
-static void lsilogicScatterGatherListDestroy(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState)
+DECLINLINE(void) lsilogicCopyFromSgBuf(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, size_t cbCopy)
{
- PPDMDEVINS pDevIns = pLsiLogic->CTX_SUFF(pDevIns);
- PLSILOGICTASKSTATESGENTRY pSGInfoCurr = pTaskState->paSGEntries;
-
- for (unsigned i = 0; i < pTaskState->cSGInfoEntries; i++)
- {
- if (pSGInfoCurr->fGuestMemory)
- {
- /* Release the lock. */
- PDMDevHlpPhysReleasePageMappingLock(pDevIns, &pSGInfoCurr->u.PageLock);
- }
- else if (!pSGInfoCurr->fBufferContainsData)
- {
- /* Copy the data into the guest segments now. */
- lsilogicCopyFromBufferIntoSGList(pLsiLogic->CTX_SUFF(pDevIns), pSGInfoCurr);
- }
-
- pSGInfoCurr++;
- }
-
- /* Free allocated memory if the list was too big too many times. */
- if (pTaskState->cSGListTooBig >= LSILOGIC_NR_OF_ALLOWED_BIGGER_LISTS)
- lsilogicTaskStateClear(pTaskState);
+ lsilogicSgBufWalker(pDevIns, pLsiReq, cbCopy, lsilogicCopyFromGuest);
}
-#ifdef DEBUG
/**
- * Dump an SG entry.
+ * Copy from an I/O buffer to the guest S/G buffer.
*
* @returns nothing.
- * @param pSGEntry Pointer to the SG entry to dump
+ * @param pDevIns Device instance data.
+ * @param pLsiReq Request data.
+ * @param cbCopy How much to copy over.
*/
-static void lsilogicDumpSGEntry(PMptSGEntryUnion pSGEntry)
+DECLINLINE(void) lsilogicCopyToSgBuf(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq, size_t cbCopy)
{
- switch (pSGEntry->Simple32.u2ElementType)
- {
- case MPTSGENTRYTYPE_SIMPLE:
- {
- Log(("%s: Dumping info for SIMPLE SG entry:\n", __FUNCTION__));
- Log(("%s: u24Length=%u\n", __FUNCTION__, pSGEntry->Simple32.u24Length));
- Log(("%s: fEndOfList=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfList));
- Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.f64BitAddress));
- Log(("%s: fBufferContainsData=%d\n", __FUNCTION__, pSGEntry->Simple32.fBufferContainsData));
- Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Simple32.fLocalAddress));
- Log(("%s: fEndOfBuffer=%d\n", __FUNCTION__, pSGEntry->Simple32.fEndOfBuffer));
- Log(("%s: fLastElement=%d\n", __FUNCTION__, pSGEntry->Simple32.fLastElement));
- Log(("%s: u32DataBufferAddressLow=%u\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
- if (pSGEntry->Simple32.f64BitAddress)
- {
- Log(("%s: u32DataBufferAddressHigh=%u\n", __FUNCTION__, pSGEntry->Simple64.u32DataBufferAddressHigh));
- Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__,
- ((uint64_t)pSGEntry->Simple64.u32DataBufferAddressHigh << 32) | pSGEntry->Simple64.u32DataBufferAddressLow));
- }
- else
- Log(("%s: GCDataBufferAddress=%RGp\n", __FUNCTION__, pSGEntry->Simple32.u32DataBufferAddressLow));
-
- break;
- }
- case MPTSGENTRYTYPE_CHAIN:
- {
- Log(("%s: Dumping info for CHAIN SG entry:\n", __FUNCTION__));
- Log(("%s: u16Length=%u\n", __FUNCTION__, pSGEntry->Chain.u16Length));
- Log(("%s: u8NExtChainOffset=%d\n", __FUNCTION__, pSGEntry->Chain.u8NextChainOffset));
- Log(("%s: f64BitAddress=%d\n", __FUNCTION__, pSGEntry->Chain.f64BitAddress));
- Log(("%s: fLocalAddress=%d\n", __FUNCTION__, pSGEntry->Chain.fLocalAddress));
- Log(("%s: u32SegmentAddressLow=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow));
- Log(("%s: u32SegmentAddressHigh=%u\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressHigh));
- if (pSGEntry->Chain.f64BitAddress)
- Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__,
- ((uint64_t)pSGEntry->Chain.u32SegmentAddressHigh << 32) | pSGEntry->Chain.u32SegmentAddressLow));
- else
- Log(("%s: GCSegmentAddress=%RGp\n", __FUNCTION__, pSGEntry->Chain.u32SegmentAddressLow));
- break;
- }
- }
+ lsilogicSgBufWalker(pDevIns, pLsiReq, cbCopy, lsilogicCopyToGuest);
}
-#endif
/**
- * Create scatter gather list descriptors.
+ * Allocates memory for the given request using already allocated memory if possible.
*
- * @returns VBox status code.
- * @param pLsiLogic Pointer to the LsiLogic SCSI controller.
- * @param pTaskState Pointer to the task state.
- * @param GCPhysSGLStart Guest physical address of the first SG entry.
- * @param uChainOffset Offset in bytes from the beginning of the SGL segment to the chain element.
- * @thread EMT
+ * @returns Pointer to the memory or NULL on failure
+ * @param pLsiReq The request to allocate memory for.
+ * @param cb The amount of memory to allocate.
*/
-static int lsilogicScatterGatherListCreate(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState,
- RTGCPHYS GCPhysSGLStart, uint32_t uChainOffset)
+static void *lsilogicReqMemAlloc(PLSILOGICREQ pLsiReq, size_t cb)
{
- int rc = VINF_SUCCESS;
- PPDMDEVINS pDevIns = pLsiLogic->CTX_SUFF(pDevIns);
- PVM pVM = PDMDevHlpGetVM(pDevIns);
- bool fUnaligned; /* Flag whether the current buffer is unaligned. */
- uint32_t cbUnaligned; /* Size of the unaligned buffers. */
- uint32_t cSGEntriesR3 = 0;
- uint32_t cSGInfo = 0;
- uint32_t cbSegment = 0;
- PLSILOGICTASKSTATESGENTRY pSGInfoCurr = NULL;
- uint8_t *pu8BufferUnalignedPos = NULL;
- uint8_t *pbBufferUnalignedSGInfoPos = NULL;
- uint32_t cbUnalignedComplete = 0;
- bool fDoMapping = false;
- bool fEndOfList;
- RTGCPHYS GCPhysSGEntryNext;
- RTGCPHYS GCPhysSegmentStart;
- uint32_t uChainOffsetNext;
-
- /*
- * Two passes - one to count needed scatter gather list entries and needed unaligned
- * buffers and one to actually map the SG list into R3.
- */
- for (int i = 0; i < 2; i++)
- {
- fUnaligned = false;
- cbUnaligned = 0;
- fEndOfList = false;
-
- GCPhysSGEntryNext = GCPhysSGLStart;
- uChainOffsetNext = uChainOffset;
- GCPhysSegmentStart = GCPhysSGLStart;
-
- if (fDoMapping)
- {
- Log(("%s: cSGInfo=%u\n", __FUNCTION__, cSGInfo));
-
- /* The number of needed SG entries in R3 is known. Allocate needed memory. */
- rc = lsilogicScatterGatherListAllocate(pTaskState, cSGInfo, cSGInfo, cbUnalignedComplete);
- AssertMsgRC(rc, ("Failed to allocate scatter gather array rc=%Rrc\n", rc));
-
- /* We are now able to map the pages into R3. */
- pSGInfoCurr = pTaskState->paSGEntries;
- /* Initialize first segment to remove the need for additional if checks later in the code. */
- pSGInfoCurr->fGuestMemory= false;
- pu8BufferUnalignedPos = (uint8_t *)pTaskState->pvBufferUnaligned;
- pbBufferUnalignedSGInfoPos = pu8BufferUnalignedPos;
- }
-
- /* Go through the list until we reach the end. */
- while (!fEndOfList)
- {
- bool fEndOfSegment = false;
-
- while (!fEndOfSegment)
- {
- MptSGEntryUnion SGEntry;
-
- Log(("%s: Reading SG entry from %RGp\n", __FUNCTION__, GCPhysSGEntryNext));
-
- /* Read the entry. */
- PDMDevHlpPhysRead(pDevIns, GCPhysSGEntryNext, &SGEntry, sizeof(MptSGEntryUnion));
-
-#ifdef DEBUG
- lsilogicDumpSGEntry(&SGEntry);
-#endif
-
- AssertMsg(SGEntry.Simple32.u2ElementType == MPTSGENTRYTYPE_SIMPLE, ("Invalid SG entry type\n"));
-
- /* Check if this is a zero element. */
- if ( !SGEntry.Simple32.u24Length
- && SGEntry.Simple32.fEndOfList
- && SGEntry.Simple32.fEndOfBuffer)
- {
- pTaskState->cSGListEntries = 0;
- pTaskState->cSGInfoEntries = 0;
- return VINF_SUCCESS;
- }
-
- uint32_t cbDataToTransfer = SGEntry.Simple32.u24Length;
- bool fBufferContainsData = !!SGEntry.Simple32.fBufferContainsData;
- RTGCPHYS GCPhysAddrDataBuffer = SGEntry.Simple32.u32DataBufferAddressLow;
-
- if (SGEntry.Simple32.f64BitAddress)
- {
- GCPhysAddrDataBuffer |= ((uint64_t)SGEntry.Simple64.u32DataBufferAddressHigh) << 32;
- GCPhysSGEntryNext += sizeof(MptSGEntrySimple64);
- }
- else
- GCPhysSGEntryNext += sizeof(MptSGEntrySimple32);
-
- if (fDoMapping)
- {
- pSGInfoCurr->fGuestMemory = false;
- pSGInfoCurr->fBufferContainsData = fBufferContainsData;
- pSGInfoCurr->cbBuf = cbDataToTransfer;
- pSGInfoCurr->pvBuf = pbBufferUnalignedSGInfoPos;
- pbBufferUnalignedSGInfoPos += cbDataToTransfer;
- pSGInfoCurr->u.GCPhysAddrBufferUnaligned = GCPhysAddrDataBuffer;
- if (fBufferContainsData)
- lsilogicCopyFromSGListIntoBuffer(pDevIns, pSGInfoCurr);
- pSGInfoCurr++;
- }
- else
- {
- cbUnalignedComplete += cbDataToTransfer;
- cSGInfo++;
- }
-
- /* Check if we reached the end of the list. */
- if (SGEntry.Simple32.fEndOfList)
- {
- /* We finished. */
- fEndOfSegment = true;
- fEndOfList = true;
- }
- else if (SGEntry.Simple32.fLastElement)
- {
- fEndOfSegment = true;
- }
- } /* while (!fEndOfSegment) */
-
- /* Get next chain element. */
- if (uChainOffsetNext)
- {
- MptSGEntryChain SGEntryChain;
-
- PDMDevHlpPhysRead(pDevIns, GCPhysSegmentStart + uChainOffsetNext, &SGEntryChain, sizeof(MptSGEntryChain));
-
- AssertMsg(SGEntryChain.u2ElementType == MPTSGENTRYTYPE_CHAIN, ("Invalid SG entry type\n"));
-
- /* Set the next address now. */
- GCPhysSGEntryNext = SGEntryChain.u32SegmentAddressLow;
- if (SGEntryChain.f64BitAddress)
- GCPhysSGEntryNext |= ((uint64_t)SGEntryChain.u32SegmentAddressHigh) << 32;
-
- GCPhysSegmentStart = GCPhysSGEntryNext;
- uChainOffsetNext = SGEntryChain.u8NextChainOffset * sizeof(uint32_t);
- }
-
- } /* while (!fEndOfList) */
-
- fDoMapping = true;
- if (fUnaligned)
- cbUnalignedComplete += cbUnaligned;
- }
-
- uint32_t cSGEntries;
- PRTSGSEG pSGEntryCurr = pTaskState->pSGListHead;
- pSGInfoCurr = pTaskState->paSGEntries;
-
- /* Initialize first entry. */
- pSGEntryCurr->pvSeg = pSGInfoCurr->pvBuf;
- pSGEntryCurr->cbSeg = pSGInfoCurr->cbBuf;
- pSGInfoCurr++;
- cSGEntries = 1;
-
- /* Construct the scatter gather list. */
- for (unsigned i = 0; i < (pTaskState->cSGInfoEntries-1); i++)
+ if (pLsiReq->cbAlloc > cb)
+ pLsiReq->cAllocTooMuch++;
+ else if (pLsiReq->cbAlloc < cb)
{
- if (pSGEntryCurr->cbSeg % 512 != 0)
- {
- AssertMsg((uint8_t *)pSGEntryCurr->pvSeg + pSGEntryCurr->cbSeg == pSGInfoCurr->pvBuf,
- ("Buffer ist not sector aligned but the buffer addresses are not adjacent\n"));
-
- pSGEntryCurr->cbSeg += pSGInfoCurr->cbBuf;
- }
- else
- {
- if (((uint8_t *)pSGEntryCurr->pvSeg + pSGEntryCurr->cbSeg) == pSGInfoCurr->pvBuf)
- {
- pSGEntryCurr->cbSeg += pSGInfoCurr->cbBuf;
- }
- else
- {
- pSGEntryCurr++;
- cSGEntries++;
- pSGEntryCurr->pvSeg = pSGInfoCurr->pvBuf;
- pSGEntryCurr->cbSeg = pSGInfoCurr->cbBuf;
- }
- }
-
- pSGInfoCurr++;
+ if (pLsiReq->cbAlloc)
+ RTMemPageFree(pLsiReq->pvAlloc, pLsiReq->cbAlloc);
+
+ pLsiReq->cbAlloc = RT_ALIGN_Z(cb, _4K);
+ pLsiReq->pvAlloc = RTMemPageAlloc(pLsiReq->cbAlloc);
+ pLsiReq->cAllocTooMuch = 0;
+ if (RT_UNLIKELY(!pLsiReq->pvAlloc))
+ pLsiReq->cbAlloc = 0;
}
- pTaskState->cSGListEntries = cSGEntries;
-
- return rc;
+ return pLsiReq->pvAlloc;
}
-/*
- * Disabled because the sense buffer provided by the LsiLogic driver for Windows XP
- * crosses page boundaries.
- */
-#if 0
/**
- * Free the sense buffer.
+ * Frees memory allocated for the given request.
*
* @returns nothing.
- * @param pTaskState Pointer to the task state.
+ * @param pLsiReq The request.
*/
-static void lsilogicFreeGCSenseBuffer(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState)
+static void lsilogicReqMemFree(PLSILOGICREQ pLsiReq)
{
- PVM pVM = PDMDevHlpGetVM(pLsiLogic->CTX_SUFF(pDevIns));
-
- PGMPhysReleasePageMappingLock(pVM, &pTaskState->PageLockSense);
- pTaskState->pbSenseBuffer = NULL;
+ if (pLsiReq->cAllocTooMuch >= LSILOGIC_MAX_ALLOC_TOO_MUCH)
+ {
+ RTMemPageFree(pLsiReq->pvAlloc, pLsiReq->cbAlloc);
+ pLsiReq->cbAlloc = 0;
+ pLsiReq->cAllocTooMuch = 0;
+ }
}
/**
- * Map the sense buffer into R3.
+ * Allocate I/O memory and copies the guest buffer for writes.
*
* @returns VBox status code.
- * @param pTaskState Pointer to the task state.
- * @note Current assumption is that the sense buffer is not scattered and does not cross a page boundary.
+ * @param pDevIns The device instance.
+ * @param pLsiReq The request state.
+ * @param cbTransfer Amount of bytes to allocate.
*/
-static int lsilogicMapGCSenseBufferIntoR3(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState)
+static int lsilogicIoBufAllocate(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq,
+ size_t cbTransfer)
{
- int rc = VINF_SUCCESS;
- PPDMDEVINS pDevIns = pLsiLogic->CTX_SUFF(pDevIns);
- RTGCPHYS GCPhysAddrSenseBuffer;
+ uint8_t uTxDir = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control);
- GCPhysAddrSenseBuffer = pTaskState->GuestRequest.SCSIIO.u32SenseBufferLowAddress;
- GCPhysAddrSenseBuffer |= ((uint64_t)pLsiLogic->u32SenseBufferHighAddr << 32);
+ AssertMsg( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE
+ || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ
+ || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE,
+ ("Allocating I/O memory for a non I/O request is not allowed\n"));
-#ifdef RT_STRICT
- uint32_t cbSenseBuffer = pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength;
-#endif
- RTGCPHYS GCPhysAddrSenseBufferBase = PAGE_ADDRESS(GCPhysAddrSenseBuffer);
+ pLsiReq->SegIoBuf.pvSeg = lsilogicReqMemAlloc(pLsiReq, cbTransfer);
+ if (!pLsiReq->SegIoBuf.pvSeg)
+ return VERR_NO_MEMORY;
- AssertMsg(GCPhysAddrSenseBuffer >= GCPhysAddrSenseBufferBase,
- ("Impossible GCPhysAddrSenseBuffer < GCPhysAddrSenseBufferBase\n"));
+ pLsiReq->SegIoBuf.cbSeg = cbTransfer;
+ if ( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE
+ || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE)
+ lsilogicCopyFromSgBuf(pDevIns, pLsiReq, cbTransfer);
- /* Sanity checks for the assumption. */
- AssertMsg(((GCPhysAddrSenseBuffer + cbSenseBuffer) <= (GCPhysAddrSenseBufferBase + PAGE_SIZE)),
- ("Sense buffer crosses page boundary\n"));
+ return VINF_SUCCESS;
+}
- rc = PDMDevHlpPhysGCPhys2CCPtr(pDevIns, GCPhysAddrSenseBufferBase, (void **)&pTaskState->pbSenseBuffer, &pTaskState->PageLockSense);
- AssertMsgRC(rc, ("Mapping sense buffer failed rc=%Rrc\n", rc));
+/**
+ * Frees the I/O memory of the given request and updates the guest buffer if necessary.
+ *
+ * @returns nothing.
+ * @param pDevIns The device instance.
+ * @param pLsiReq The request state.
+ * @param fCopyToGuest Flag whether to update the guest buffer if necessary.
+ * Nothing is copied if false even if the request was a read.
+ */
+static void lsilogicIoBufFree(PPDMDEVINS pDevIns, PLSILOGICREQ pLsiReq,
+ bool fCopyToGuest)
+{
+ uint8_t uTxDir = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control);
- /* Correct start address of the sense buffer. */
- pTaskState->pbSenseBuffer += (GCPhysAddrSenseBuffer - GCPhysAddrSenseBufferBase);
+ AssertMsg( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE
+ || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ
+ || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE,
+ ("Allocating I/O memory for a non I/O request is not allowed\n"));
- return rc;
+ if ( ( uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ
+ || uTxDir == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE)
+ && fCopyToGuest)
+ lsilogicCopyToSgBuf(pDevIns, pLsiReq, pLsiReq->SegIoBuf.cbSeg);
+
+ lsilogicReqMemFree(pLsiReq);
+ pLsiReq->SegIoBuf.pvSeg = NULL;
+ pLsiReq->SegIoBuf.cbSeg = 0;
}
-#endif
-#ifdef DEBUG
-static void lsilogicDumpSCSIIORequest(PMptSCSIIORequest pSCSIIORequest)
+# ifdef LOG_ENABLED
+static void lsilogicR3DumpSCSIIORequest(PMptSCSIIORequest pSCSIIORequest)
{
- Log(("%s: u8TargetID=%d\n", __FUNCTION__, pSCSIIORequest->u8TargetID));
- Log(("%s: u8Bus=%d\n", __FUNCTION__, pSCSIIORequest->u8Bus));
- Log(("%s: u8ChainOffset=%d\n", __FUNCTION__, pSCSIIORequest->u8ChainOffset));
- Log(("%s: u8Function=%d\n", __FUNCTION__, pSCSIIORequest->u8Function));
- Log(("%s: u8CDBLength=%d\n", __FUNCTION__, pSCSIIORequest->u8CDBLength));
- Log(("%s: u8SenseBufferLength=%d\n", __FUNCTION__, pSCSIIORequest->u8SenseBufferLength));
- Log(("%s: u8MessageFlags=%d\n", __FUNCTION__, pSCSIIORequest->u8MessageFlags));
- Log(("%s: u32MessageContext=%#x\n", __FUNCTION__, pSCSIIORequest->u32MessageContext));
- for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8LUN); i++)
- Log(("%s: u8LUN[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8LUN[i]));
- Log(("%s: u32Control=%#x\n", __FUNCTION__, pSCSIIORequest->u32Control));
- for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8CDB); i++)
- Log(("%s: u8CDB[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8CDB[i]));
- Log(("%s: u32DataLength=%#x\n", __FUNCTION__, pSCSIIORequest->u32DataLength));
- Log(("%s: u32SenseBufferLowAddress=%#x\n", __FUNCTION__, pSCSIIORequest->u32SenseBufferLowAddress));
+ if (LogIsEnabled())
+ {
+ Log(("%s: u8TargetID=%d\n", __FUNCTION__, pSCSIIORequest->u8TargetID));
+ Log(("%s: u8Bus=%d\n", __FUNCTION__, pSCSIIORequest->u8Bus));
+ Log(("%s: u8ChainOffset=%d\n", __FUNCTION__, pSCSIIORequest->u8ChainOffset));
+ Log(("%s: u8Function=%d\n", __FUNCTION__, pSCSIIORequest->u8Function));
+ Log(("%s: u8CDBLength=%d\n", __FUNCTION__, pSCSIIORequest->u8CDBLength));
+ Log(("%s: u8SenseBufferLength=%d\n", __FUNCTION__, pSCSIIORequest->u8SenseBufferLength));
+ Log(("%s: u8MessageFlags=%d\n", __FUNCTION__, pSCSIIORequest->u8MessageFlags));
+ Log(("%s: u32MessageContext=%#x\n", __FUNCTION__, pSCSIIORequest->u32MessageContext));
+ for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8LUN); i++)
+ Log(("%s: u8LUN[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8LUN[i]));
+ Log(("%s: u32Control=%#x\n", __FUNCTION__, pSCSIIORequest->u32Control));
+ for (unsigned i = 0; i < RT_ELEMENTS(pSCSIIORequest->au8CDB); i++)
+ Log(("%s: u8CDB[%d]=%d\n", __FUNCTION__, i, pSCSIIORequest->au8CDB[i]));
+ Log(("%s: u32DataLength=%#x\n", __FUNCTION__, pSCSIIORequest->u32DataLength));
+ Log(("%s: u32SenseBufferLowAddress=%#x\n", __FUNCTION__, pSCSIIORequest->u32SenseBufferLowAddress));
+ }
}
-#endif
+# endif
-static void lsilogicWarningDiskFull(PPDMDEVINS pDevIns)
+static void lsilogicR3WarningDiskFull(PPDMDEVINS pDevIns)
{
int rc;
LogRel(("LsiLogic#%d: Host disk full\n", pDevIns->iInstance));
@@ -1911,7 +2096,7 @@ static void lsilogicWarningDiskFull(PPDMDEVINS pDevIns)
AssertRC(rc);
}
-static void lsilogicWarningFileTooBig(PPDMDEVINS pDevIns)
+static void lsilogicR3WarningFileTooBig(PPDMDEVINS pDevIns)
{
int rc;
LogRel(("LsiLogic#%d: File too big\n", pDevIns->iInstance));
@@ -1920,7 +2105,7 @@ static void lsilogicWarningFileTooBig(PPDMDEVINS pDevIns)
AssertRC(rc);
}
-static void lsilogicWarningISCSI(PPDMDEVINS pDevIns)
+static void lsilogicR3WarningISCSI(PPDMDEVINS pDevIns)
{
int rc;
LogRel(("LsiLogic#%d: iSCSI target unavailable\n", pDevIns->iInstance));
@@ -1929,7 +2114,7 @@ static void lsilogicWarningISCSI(PPDMDEVINS pDevIns)
AssertRC(rc);
}
-static void lsilogicWarningUnknown(PPDMDEVINS pDevIns, int rc)
+static void lsilogicR3WarningUnknown(PPDMDEVINS pDevIns, int rc)
{
int rc2;
LogRel(("LsiLogic#%d: Unknown but recoverable error has occurred (rc=%Rrc)\n", pDevIns->iInstance, rc));
@@ -1938,20 +2123,20 @@ static void lsilogicWarningUnknown(PPDMDEVINS pDevIns, int rc)
AssertRC(rc2);
}
-static void lsilogicRedoSetWarning(PLSILOGICSCSI pThis, int rc)
+static void lsilogicR3RedoSetWarning(PLSILOGICSCSI pThis, int rc)
{
if (rc == VERR_DISK_FULL)
- lsilogicWarningDiskFull(pThis->CTX_SUFF(pDevIns));
+ lsilogicR3WarningDiskFull(pThis->CTX_SUFF(pDevIns));
else if (rc == VERR_FILE_TOO_BIG)
- lsilogicWarningFileTooBig(pThis->CTX_SUFF(pDevIns));
+ lsilogicR3WarningFileTooBig(pThis->CTX_SUFF(pDevIns));
else if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
{
/* iSCSI connection abort (first error) or failure to reestablish
* connection (second error). Pause VM. On resume we'll retry. */
- lsilogicWarningISCSI(pThis->CTX_SUFF(pDevIns));
+ lsilogicR3WarningISCSI(pThis->CTX_SUFF(pDevIns));
}
else
- lsilogicWarningUnknown(pThis->CTX_SUFF(pDevIns), rc);
+ lsilogicR3WarningUnknown(pThis->CTX_SUFF(pDevIns), rc);
}
/**
@@ -1962,216 +2147,223 @@ static void lsilogicRedoSetWarning(PLSILOGICSCSI pThis, int rc)
* the request.
*
* @returns VBox status code.
- * @param pLsiLogic Pointer to the device instance which sends the request.
- * @param pTaskState Pointer to the task state data.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pLsiReq Pointer to the task state data.
*/
-static int lsilogicProcessSCSIIORequest(PLSILOGICSCSI pLsiLogic, PLSILOGICTASKSTATE pTaskState)
+static int lsilogicR3ProcessSCSIIORequest(PLSILOGICSCSI pThis, PLSILOGICREQ pLsiReq)
{
int rc = VINF_SUCCESS;
-#ifdef DEBUG
- lsilogicDumpSCSIIORequest(&pTaskState->GuestRequest.SCSIIO);
-#endif
+# ifdef LOG_ENABLED
+ lsilogicR3DumpSCSIIORequest(&pLsiReq->GuestRequest.SCSIIO);
+# endif
- pTaskState->fBIOS = false;
+ pLsiReq->fBIOS = false;
+ pLsiReq->GCPhysSgStart = pLsiReq->GCPhysMessageFrameAddr + sizeof(MptSCSIIORequest);
+ pLsiReq->cChainOffset = pLsiReq->GuestRequest.SCSIIO.u8ChainOffset;
+ if (pLsiReq->cChainOffset)
+ pLsiReq->cChainOffset = pLsiReq->cChainOffset * sizeof(uint32_t) - sizeof(MptSCSIIORequest);
- if (RT_LIKELY( (pTaskState->GuestRequest.SCSIIO.u8TargetID < pLsiLogic->cDeviceStates)
- && (pTaskState->GuestRequest.SCSIIO.u8Bus == 0)))
+ if (RT_LIKELY( (pLsiReq->GuestRequest.SCSIIO.u8TargetID < pThis->cDeviceStates)
+ && (pLsiReq->GuestRequest.SCSIIO.u8Bus == 0)))
{
PLSILOGICDEVICE pTargetDevice;
- pTargetDevice = &pLsiLogic->paDeviceStates[pTaskState->GuestRequest.SCSIIO.u8TargetID];
+ pTargetDevice = &pThis->paDeviceStates[pLsiReq->GuestRequest.SCSIIO.u8TargetID];
if (pTargetDevice->pDrvBase)
{
- uint32_t uChainOffset;
-
- /* Create Scatter gather list. */
- uChainOffset = pTaskState->GuestRequest.SCSIIO.u8ChainOffset;
- if (uChainOffset)
- uChainOffset = uChainOffset * sizeof(uint32_t) - sizeof(MptSCSIIORequest);
-
- rc = lsilogicScatterGatherListCreate(pLsiLogic, pTaskState,
- pTaskState->GCPhysMessageFrameAddr + sizeof(MptSCSIIORequest),
- uChainOffset);
- AssertRC(rc);
+ if (pLsiReq->GuestRequest.SCSIIO.u32DataLength)
+ {
-#if 0
- /* Map sense buffer. */
- rc = lsilogicMapGCSenseBufferIntoR3(pLsiLogic, pTaskState);
- AssertRC(rc);
-#endif
+ rc = lsilogicIoBufAllocate(pThis->CTX_SUFF(pDevIns), pLsiReq,
+ pLsiReq->GuestRequest.SCSIIO.u32DataLength);
+ AssertRC(rc); /** @todo: Insufficient resources error. */
+ }
/* Setup the SCSI request. */
- pTaskState->pTargetDevice = pTargetDevice;
- pTaskState->PDMScsiRequest.uLogicalUnit = pTaskState->GuestRequest.SCSIIO.au8LUN[1];
+ pLsiReq->pTargetDevice = pTargetDevice;
+ pLsiReq->PDMScsiRequest.uLogicalUnit = pLsiReq->GuestRequest.SCSIIO.au8LUN[1];
- uint8_t uDataDirection = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pTaskState->GuestRequest.SCSIIO.u32Control);
+ uint8_t uDataDirection = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_GET(pLsiReq->GuestRequest.SCSIIO.u32Control);
if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE)
- pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_NONE;
+ pLsiReq->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_NONE;
else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE)
- pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_TO_DEVICE;
+ pLsiReq->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_TO_DEVICE;
else if (uDataDirection == MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ)
- pTaskState->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_FROM_DEVICE;
-
- pTaskState->PDMScsiRequest.cbCDB = pTaskState->GuestRequest.SCSIIO.u8CDBLength;
- pTaskState->PDMScsiRequest.pbCDB = pTaskState->GuestRequest.SCSIIO.au8CDB;
- pTaskState->PDMScsiRequest.cbScatterGather = pTaskState->GuestRequest.SCSIIO.u32DataLength;
- pTaskState->PDMScsiRequest.cScatterGatherEntries = pTaskState->cSGListEntries;
- pTaskState->PDMScsiRequest.paScatterGatherHead = pTaskState->pSGListHead;
- pTaskState->PDMScsiRequest.cbSenseBuffer = sizeof(pTaskState->abSenseBuffer);
- memset(pTaskState->abSenseBuffer, 0, pTaskState->PDMScsiRequest.cbSenseBuffer);
- pTaskState->PDMScsiRequest.pbSenseBuffer = pTaskState->abSenseBuffer;
- pTaskState->PDMScsiRequest.pvUser = pTaskState;
+ pLsiReq->PDMScsiRequest.uDataDirection = PDMSCSIREQUESTTXDIR_FROM_DEVICE;
+
+ pLsiReq->PDMScsiRequest.cbCDB = pLsiReq->GuestRequest.SCSIIO.u8CDBLength;
+ pLsiReq->PDMScsiRequest.pbCDB = pLsiReq->GuestRequest.SCSIIO.au8CDB;
+ pLsiReq->PDMScsiRequest.cbScatterGather = pLsiReq->GuestRequest.SCSIIO.u32DataLength;
+ if (pLsiReq->PDMScsiRequest.cbScatterGather)
+ {
+ pLsiReq->PDMScsiRequest.cScatterGatherEntries = 1;
+ pLsiReq->PDMScsiRequest.paScatterGatherHead = &pLsiReq->SegIoBuf;
+ }
+ else
+ {
+ pLsiReq->PDMScsiRequest.cScatterGatherEntries = 0;
+ pLsiReq->PDMScsiRequest.paScatterGatherHead = NULL;
+ }
+ pLsiReq->PDMScsiRequest.cbSenseBuffer = sizeof(pLsiReq->abSenseBuffer);
+ memset(pLsiReq->abSenseBuffer, 0, pLsiReq->PDMScsiRequest.cbSenseBuffer);
+ pLsiReq->PDMScsiRequest.pbSenseBuffer = pLsiReq->abSenseBuffer;
+ pLsiReq->PDMScsiRequest.pvUser = pLsiReq;
ASMAtomicIncU32(&pTargetDevice->cOutstandingRequests);
- rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pTaskState->PDMScsiRequest);
+ rc = pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTargetDevice->pDrvSCSIConnector, &pLsiReq->PDMScsiRequest);
AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc));
return VINF_SUCCESS;
}
else
{
/* Device is not present report SCSI selection timeout. */
- pTaskState->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE;
+ pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_DEVICE_NOT_THERE;
}
}
else
{
/* Report out of bounds target ID or bus. */
- if (pTaskState->GuestRequest.SCSIIO.u8Bus != 0)
- pTaskState->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_BUS;
+ if (pLsiReq->GuestRequest.SCSIIO.u8Bus != 0)
+ pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_BUS;
else
- pTaskState->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_TARGETID;
+ pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = MPT_SCSI_IO_ERROR_IOCSTATUS_INVALID_TARGETID;
}
static int g_cLogged = 0;
if (g_cLogged++ < MAX_REL_LOG_ERRORS)
{
- LogRel(("LsiLogic#%d: %d/%d (Bus/Target) doesn't exist\n", pLsiLogic->CTX_SUFF(pDevIns)->iInstance,
- pTaskState->GuestRequest.SCSIIO.u8TargetID, pTaskState->GuestRequest.SCSIIO.u8Bus));
+ LogRel(("LsiLogic#%d: %d/%d (Bus/Target) doesn't exist\n", pThis->CTX_SUFF(pDevIns)->iInstance,
+ pLsiReq->GuestRequest.SCSIIO.u8TargetID, pLsiReq->GuestRequest.SCSIIO.u8Bus));
/* Log the CDB too */
LogRel(("LsiLogic#%d: Guest issued CDB {%#x",
- pLsiLogic->CTX_SUFF(pDevIns)->iInstance, pTaskState->GuestRequest.SCSIIO.au8CDB[0]));
- for (unsigned i = 1; i < pTaskState->GuestRequest.SCSIIO.u8CDBLength; i++)
- LogRel((", %#x", pTaskState->GuestRequest.SCSIIO.au8CDB[i]));
+ pThis->CTX_SUFF(pDevIns)->iInstance, pLsiReq->GuestRequest.SCSIIO.au8CDB[0]));
+ for (unsigned i = 1; i < pLsiReq->GuestRequest.SCSIIO.u8CDBLength; i++)
+ LogRel((", %#x", pLsiReq->GuestRequest.SCSIIO.au8CDB[i]));
LogRel(("}\n"));
}
/* The rest is equal to both errors. */
- pTaskState->IOCReply.SCSIIOError.u8TargetID = pTaskState->GuestRequest.SCSIIO.u8TargetID;
- pTaskState->IOCReply.SCSIIOError.u8Bus = pTaskState->GuestRequest.SCSIIO.u8Bus;
- pTaskState->IOCReply.SCSIIOError.u8MessageLength = sizeof(MptSCSIIOErrorReply) / 4;
- pTaskState->IOCReply.SCSIIOError.u8Function = pTaskState->GuestRequest.SCSIIO.u8Function;
- pTaskState->IOCReply.SCSIIOError.u8CDBLength = pTaskState->GuestRequest.SCSIIO.u8CDBLength;
- pTaskState->IOCReply.SCSIIOError.u8SenseBufferLength = pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength;
- pTaskState->IOCReply.SCSIIOError.u32MessageContext = pTaskState->GuestRequest.SCSIIO.u32MessageContext;
- pTaskState->IOCReply.SCSIIOError.u8SCSIStatus = SCSI_STATUS_OK;
- pTaskState->IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_TERMINATED;
- pTaskState->IOCReply.SCSIIOError.u32IOCLogInfo = 0;
- pTaskState->IOCReply.SCSIIOError.u32TransferCount = 0;
- pTaskState->IOCReply.SCSIIOError.u32SenseCount = 0;
- pTaskState->IOCReply.SCSIIOError.u32ResponseInfo = 0;
-
- lsilogicFinishAddressReply(pLsiLogic, &pTaskState->IOCReply, false);
- RTMemCacheFree(pLsiLogic->hTaskCache, pTaskState);
+ pLsiReq->IOCReply.SCSIIOError.u8TargetID = pLsiReq->GuestRequest.SCSIIO.u8TargetID;
+ pLsiReq->IOCReply.SCSIIOError.u8Bus = pLsiReq->GuestRequest.SCSIIO.u8Bus;
+ pLsiReq->IOCReply.SCSIIOError.u8MessageLength = sizeof(MptSCSIIOErrorReply) / 4;
+ pLsiReq->IOCReply.SCSIIOError.u8Function = pLsiReq->GuestRequest.SCSIIO.u8Function;
+ pLsiReq->IOCReply.SCSIIOError.u8CDBLength = pLsiReq->GuestRequest.SCSIIO.u8CDBLength;
+ pLsiReq->IOCReply.SCSIIOError.u8SenseBufferLength = pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength;
+ pLsiReq->IOCReply.SCSIIOError.u32MessageContext = pLsiReq->GuestRequest.SCSIIO.u32MessageContext;
+ pLsiReq->IOCReply.SCSIIOError.u8SCSIStatus = SCSI_STATUS_OK;
+ pLsiReq->IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_TERMINATED;
+ pLsiReq->IOCReply.SCSIIOError.u32IOCLogInfo = 0;
+ pLsiReq->IOCReply.SCSIIOError.u32TransferCount = 0;
+ pLsiReq->IOCReply.SCSIIOError.u32SenseCount = 0;
+ pLsiReq->IOCReply.SCSIIOError.u32ResponseInfo = 0;
+
+ lsilogicFinishAddressReply(pThis, &pLsiReq->IOCReply, false);
+ RTMemCacheFree(pThis->hTaskCache, pLsiReq);
return rc;
}
-static DECLCALLBACK(int) lsilogicDeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
- int rcCompletion, bool fRedo, int rcReq)
+/**
+ * @interface_method_impl{PDMISCSIPORT,pfnSCSIRequestCompleted}
+ */
+static DECLCALLBACK(int) lsilogicR3DeviceSCSIRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
+ int rcCompletion, bool fRedo, int rcReq)
{
- PLSILOGICTASKSTATE pTaskState = (PLSILOGICTASKSTATE)pSCSIRequest->pvUser;
- PLSILOGICDEVICE pLsiLogicDevice = pTaskState->pTargetDevice;
- PLSILOGICSCSI pLsiLogic = pLsiLogicDevice->CTX_SUFF(pLsiLogic);
+ PLSILOGICREQ pLsiReq = (PLSILOGICREQ)pSCSIRequest->pvUser;
+ PLSILOGICDEVICE pLsiLogicDevice = pLsiReq->pTargetDevice;
+ PLSILOGICSCSI pThis = pLsiLogicDevice->CTX_SUFF(pLsiLogic);
/* If the task failed but it is possible to redo it again after a suspend
* add it to the list. */
if (fRedo)
{
- if (!pTaskState->fBIOS)
- lsilogicScatterGatherListDestroy(pLsiLogic, pTaskState);
+ if (!pLsiReq->fBIOS && pLsiReq->PDMScsiRequest.cbScatterGather)
+ lsilogicIoBufFree(pThis->CTX_SUFF(pDevIns), pLsiReq, false /* fCopyToGuest */);
/* Add to the list. */
do
{
- pTaskState->pRedoNext = ASMAtomicReadPtrT(&pLsiLogic->pTasksRedoHead, PLSILOGICTASKSTATE);
- } while (!ASMAtomicCmpXchgPtr(&pLsiLogic->pTasksRedoHead, pTaskState, pTaskState->pRedoNext));
+ pLsiReq->pRedoNext = ASMAtomicReadPtrT(&pThis->pTasksRedoHead, PLSILOGICREQ);
+ } while (!ASMAtomicCmpXchgPtr(&pThis->pTasksRedoHead, pLsiReq, pLsiReq->pRedoNext));
/* Suspend the VM if not done already. */
- if (!ASMAtomicXchgBool(&pLsiLogic->fRedo, true))
- lsilogicRedoSetWarning(pLsiLogic, rcReq);
+ if (!ASMAtomicXchgBool(&pThis->fRedo, true))
+ lsilogicR3RedoSetWarning(pThis, rcReq);
}
else
{
- if (RT_UNLIKELY(pTaskState->fBIOS))
+ if (RT_UNLIKELY(pLsiReq->fBIOS))
{
- int rc = vboxscsiRequestFinished(&pLsiLogic->VBoxSCSI, pSCSIRequest);
+ int rc = vboxscsiRequestFinished(&pThis->VBoxSCSI, pSCSIRequest, rcCompletion);
AssertMsgRC(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc));
}
else
{
-#if 0
- lsilogicFreeGCSenseBuffer(pLsiLogic, pTaskState);
-#else
RTGCPHYS GCPhysAddrSenseBuffer;
- GCPhysAddrSenseBuffer = pTaskState->GuestRequest.SCSIIO.u32SenseBufferLowAddress;
- GCPhysAddrSenseBuffer |= ((uint64_t)pLsiLogic->u32SenseBufferHighAddr << 32);
+ GCPhysAddrSenseBuffer = pLsiReq->GuestRequest.SCSIIO.u32SenseBufferLowAddress;
+ GCPhysAddrSenseBuffer |= ((uint64_t)pThis->u32SenseBufferHighAddr << 32);
/* Copy the sense buffer over. */
- PDMDevHlpPhysWrite(pLsiLogic->CTX_SUFF(pDevIns), GCPhysAddrSenseBuffer, pTaskState->abSenseBuffer,
- RT_UNLIKELY(pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength < pTaskState->PDMScsiRequest.cbSenseBuffer)
- ? pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength
- : pTaskState->PDMScsiRequest.cbSenseBuffer);
-#endif
- lsilogicScatterGatherListDestroy(pLsiLogic, pTaskState);
+ PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysAddrSenseBuffer, pLsiReq->abSenseBuffer,
+ RT_UNLIKELY( pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength
+ < pLsiReq->PDMScsiRequest.cbSenseBuffer)
+ ? pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength
+ : pLsiReq->PDMScsiRequest.cbSenseBuffer);
+
+ if (pLsiReq->PDMScsiRequest.cbScatterGather)
+ lsilogicIoBufFree(pThis->CTX_SUFF(pDevIns), pLsiReq, true /* fCopyToGuest */);
if (RT_LIKELY(rcCompletion == SCSI_STATUS_OK))
- lsilogicFinishContextReply(pLsiLogic, pTaskState->GuestRequest.SCSIIO.u32MessageContext);
+ lsilogicR3FinishContextReply(pThis, pLsiReq->GuestRequest.SCSIIO.u32MessageContext);
else
{
/* The SCSI target encountered an error during processing post a reply. */
- memset(&pTaskState->IOCReply, 0, sizeof(MptReplyUnion));
- pTaskState->IOCReply.SCSIIOError.u8TargetID = pTaskState->GuestRequest.SCSIIO.u8TargetID;
- pTaskState->IOCReply.SCSIIOError.u8Bus = pTaskState->GuestRequest.SCSIIO.u8Bus;
- pTaskState->IOCReply.SCSIIOError.u8MessageLength = 8;
- pTaskState->IOCReply.SCSIIOError.u8Function = pTaskState->GuestRequest.SCSIIO.u8Function;
- pTaskState->IOCReply.SCSIIOError.u8CDBLength = pTaskState->GuestRequest.SCSIIO.u8CDBLength;
- pTaskState->IOCReply.SCSIIOError.u8SenseBufferLength = pTaskState->GuestRequest.SCSIIO.u8SenseBufferLength;
- pTaskState->IOCReply.SCSIIOError.u8MessageFlags = pTaskState->GuestRequest.SCSIIO.u8MessageFlags;
- pTaskState->IOCReply.SCSIIOError.u32MessageContext = pTaskState->GuestRequest.SCSIIO.u32MessageContext;
- pTaskState->IOCReply.SCSIIOError.u8SCSIStatus = rcCompletion;
- pTaskState->IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_AUTOSENSE_VALID;
- pTaskState->IOCReply.SCSIIOError.u16IOCStatus = 0;
- pTaskState->IOCReply.SCSIIOError.u32IOCLogInfo = 0;
- pTaskState->IOCReply.SCSIIOError.u32TransferCount = 0;
- pTaskState->IOCReply.SCSIIOError.u32SenseCount = sizeof(pTaskState->abSenseBuffer);
- pTaskState->IOCReply.SCSIIOError.u32ResponseInfo = 0;
-
- lsilogicFinishAddressReply(pLsiLogic, &pTaskState->IOCReply, true);
+ memset(&pLsiReq->IOCReply, 0, sizeof(MptReplyUnion));
+ pLsiReq->IOCReply.SCSIIOError.u8TargetID = pLsiReq->GuestRequest.SCSIIO.u8TargetID;
+ pLsiReq->IOCReply.SCSIIOError.u8Bus = pLsiReq->GuestRequest.SCSIIO.u8Bus;
+ pLsiReq->IOCReply.SCSIIOError.u8MessageLength = 8;
+ pLsiReq->IOCReply.SCSIIOError.u8Function = pLsiReq->GuestRequest.SCSIIO.u8Function;
+ pLsiReq->IOCReply.SCSIIOError.u8CDBLength = pLsiReq->GuestRequest.SCSIIO.u8CDBLength;
+ pLsiReq->IOCReply.SCSIIOError.u8SenseBufferLength = pLsiReq->GuestRequest.SCSIIO.u8SenseBufferLength;
+ pLsiReq->IOCReply.SCSIIOError.u8MessageFlags = pLsiReq->GuestRequest.SCSIIO.u8MessageFlags;
+ pLsiReq->IOCReply.SCSIIOError.u32MessageContext = pLsiReq->GuestRequest.SCSIIO.u32MessageContext;
+ pLsiReq->IOCReply.SCSIIOError.u8SCSIStatus = rcCompletion;
+ pLsiReq->IOCReply.SCSIIOError.u8SCSIState = MPT_SCSI_IO_ERROR_SCSI_STATE_AUTOSENSE_VALID;
+ pLsiReq->IOCReply.SCSIIOError.u16IOCStatus = 0;
+ pLsiReq->IOCReply.SCSIIOError.u32IOCLogInfo = 0;
+ pLsiReq->IOCReply.SCSIIOError.u32TransferCount = 0;
+ pLsiReq->IOCReply.SCSIIOError.u32SenseCount = sizeof(pLsiReq->abSenseBuffer);
+ pLsiReq->IOCReply.SCSIIOError.u32ResponseInfo = 0;
+
+ lsilogicFinishAddressReply(pThis, &pLsiReq->IOCReply, false);
}
}
- RTMemCacheFree(pLsiLogic->hTaskCache, pTaskState);
+ RTMemCacheFree(pThis->hTaskCache, pLsiReq);
}
ASMAtomicDecU32(&pLsiLogicDevice->cOutstandingRequests);
- if (pLsiLogicDevice->cOutstandingRequests == 0 && pLsiLogic->fSignalIdle)
- PDMDevHlpAsyncNotificationCompleted(pLsiLogic->pDevInsR3);
+ if (pLsiLogicDevice->cOutstandingRequests == 0 && pThis->fSignalIdle)
+ PDMDevHlpAsyncNotificationCompleted(pThis->pDevInsR3);
return VINF_SUCCESS;
}
-static DECLCALLBACK(int) lsilogicQueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
- uint32_t *piInstance, uint32_t *piLUN)
+/**
+ * @interface_method_impl{PDMISCSIPORT,pfnQueryDeviceLocation}
+ */
+static DECLCALLBACK(int) lsilogicR3QueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
+ uint32_t *piInstance, uint32_t *piLUN)
{
- PLSILOGICDEVICE pLsiLogicDevice = PDMISCSIPORT_2_PLSILOGICDEVICE(pInterface);
+ PLSILOGICDEVICE pLsiLogicDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, ISCSIPort);
PPDMDEVINS pDevIns = pLsiLogicDevice->CTX_SUFF(pLsiLogic)->CTX_SUFF(pDevIns);
AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
@@ -2195,17 +2387,17 @@ static DECLCALLBACK(int) lsilogicQueryDeviceLocation(PPDMISCSIPORT pInterface, c
* @param ppPageHeader Where to store the pointer to the page header.
* @param ppbPageData Where to store the pointer to the page data.
*/
-static int lsilogicConfigurationIOUnitPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
- PMptConfigurationPagesSupported pPages,
- uint8_t u8PageNumber,
- PMptConfigurationPageHeader *ppPageHeader,
- uint8_t **ppbPageData, size_t *pcbPage)
+static int lsilogicR3ConfigurationIOUnitPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
{
int rc = VINF_SUCCESS;
- AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n"));
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
- switch(u8PageNumber)
+ switch (u8PageNumber)
{
case 0:
*ppPageHeader = &pPages->IOUnitPage0.u.fields.Header;
@@ -2249,17 +2441,17 @@ static int lsilogicConfigurationIOUnitPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
* @param ppPageHeader Where to store the pointer to the page header.
* @param ppbPageData Where to store the pointer to the page data.
*/
-static int lsilogicConfigurationIOCPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
- PMptConfigurationPagesSupported pPages,
- uint8_t u8PageNumber,
- PMptConfigurationPageHeader *ppPageHeader,
- uint8_t **ppbPageData, size_t *pcbPage)
+static int lsilogicR3ConfigurationIOCPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
{
int rc = VINF_SUCCESS;
- AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n"));
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
- switch(u8PageNumber)
+ switch (u8PageNumber)
{
case 0:
*ppPageHeader = &pPages->IOCPage0.u.fields.Header;
@@ -2308,17 +2500,17 @@ static int lsilogicConfigurationIOCPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
* @param ppPageHeader Where to store the pointer to the page header.
* @param ppbPageData Where to store the pointer to the page data.
*/
-static int lsilogicConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
- PMptConfigurationPagesSupported pPages,
- uint8_t u8PageNumber,
- PMptConfigurationPageHeader *ppPageHeader,
- uint8_t **ppbPageData, size_t *pcbPage)
+static int lsilogicR3ConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
{
int rc = VINF_SUCCESS;
- AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n"));
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
- switch(u8PageNumber)
+ switch (u8PageNumber)
{
case 0:
*ppPageHeader = &pPages->ManufacturingPage0.u.fields.Header;
@@ -2356,7 +2548,7 @@ static int lsilogicConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pLs
*pcbPage = sizeof(pPages->ManufacturingPage6);
break;
case 7:
- if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
{
*ppPageHeader = &pPages->u.SasPages.pManufacturingPage7->u.fields.Header;
*ppbPageData = pPages->u.SasPages.pManufacturingPage7->u.abPageData;
@@ -2397,17 +2589,17 @@ static int lsilogicConfigurationManufacturingPageGetFromNumber(PLSILOGICSCSI pLs
* @param ppPageHeader Where to store the pointer to the page header.
* @param ppbPageData Where to store the pointer to the page data.
*/
-static int lsilogicConfigurationBiosPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
- PMptConfigurationPagesSupported pPages,
- uint8_t u8PageNumber,
- PMptConfigurationPageHeader *ppPageHeader,
- uint8_t **ppbPageData, size_t *pcbPage)
+static int lsilogicR3ConfigurationBiosPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
{
int rc = VINF_SUCCESS;
- AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n"));
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
- switch(u8PageNumber)
+ switch (u8PageNumber)
{
case 1:
*ppPageHeader = &pPages->BIOSPage1.u.fields.Header;
@@ -2441,21 +2633,21 @@ static int lsilogicConfigurationBiosPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
* @param ppPageHeader Where to store the pointer to the page header.
* @param ppbPageData Where to store the pointer to the page data.
*/
-static int lsilogicConfigurationSCSISPIPortPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
- PMptConfigurationPagesSupported pPages,
- uint8_t u8Port,
- uint8_t u8PageNumber,
- PMptConfigurationPageHeader *ppPageHeader,
- uint8_t **ppbPageData, size_t *pcbPage)
+static int lsilogicR3ConfigurationSCSISPIPortPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8Port,
+ uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
{
int rc = VINF_SUCCESS;
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
- AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n"));
if (u8Port >= RT_ELEMENTS(pPages->u.SpiPages.aPortPages))
return VERR_NOT_FOUND;
- switch(u8PageNumber)
+ switch (u8PageNumber)
{
case 0:
*ppPageHeader = &pPages->u.SpiPages.aPortPages[u8Port].SCSISPIPortPage0.u.fields.Header;
@@ -2489,16 +2681,15 @@ static int lsilogicConfigurationSCSISPIPortPageGetFromNumber(PLSILOGICSCSI pLsiL
* @param ppPageHeader Where to store the pointer to the page header.
* @param ppbPageData Where to store the pointer to the page data.
*/
-static int lsilogicConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pLsiLogic,
- PMptConfigurationPagesSupported pPages,
- uint8_t u8Bus,
- uint8_t u8TargetID, uint8_t u8PageNumber,
- PMptConfigurationPageHeader *ppPageHeader,
- uint8_t **ppbPageData, size_t *pcbPage)
+static int lsilogicR3ConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8Bus,
+ uint8_t u8TargetID, uint8_t u8PageNumber,
+ PMptConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
{
int rc = VINF_SUCCESS;
-
- AssertMsg(VALID_PTR(ppPageHeader) && VALID_PTR(ppbPageData), ("Invalid parameters\n"));
+ AssertPtr(ppPageHeader); Assert(ppbPageData);
if (u8Bus >= RT_ELEMENTS(pPages->u.SpiPages.aBuses))
return VERR_NOT_FOUND;
@@ -2506,7 +2697,7 @@ static int lsilogicConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pLs
if (u8TargetID >= RT_ELEMENTS(pPages->u.SpiPages.aBuses[u8Bus].aDevicePages))
return VERR_NOT_FOUND;
- switch(u8PageNumber)
+ switch (u8PageNumber)
{
case 0:
*ppPageHeader = &pPages->u.SpiPages.aBuses[u8Bus].aDevicePages[u8TargetID].SCSISPIDevicePage0.u.fields.Header;
@@ -2535,15 +2726,15 @@ static int lsilogicConfigurationSCSISPIDevicePageGetFromNumber(PLSILOGICSCSI pLs
return rc;
}
-static int lsilogicConfigurationSASIOUnitPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
- PMptConfigurationPagesSupported pPages,
- uint8_t u8PageNumber,
- PMptExtendedConfigurationPageHeader *ppPageHeader,
- uint8_t **ppbPageData, size_t *pcbPage)
+static int lsilogicR3ConfigurationSASIOUnitPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ PMptExtendedConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
{
int rc = VINF_SUCCESS;
- switch(u8PageNumber)
+ switch (u8PageNumber)
{
case 0:
*ppPageHeader = &pPages->u.SasPages.pSASIOUnitPage0->u.fields.ExtHeader;
@@ -2572,12 +2763,12 @@ static int lsilogicConfigurationSASIOUnitPageGetFromNumber(PLSILOGICSCSI pLsiLog
return rc;
}
-static int lsilogicConfigurationSASPHYPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
- PMptConfigurationPagesSupported pPages,
- uint8_t u8PageNumber,
- MptConfigurationPageAddress PageAddress,
- PMptExtendedConfigurationPageHeader *ppPageHeader,
- uint8_t **ppbPageData, size_t *pcbPage)
+static int lsilogicR3ConfigurationSASPHYPageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ MptConfigurationPageAddress PageAddress,
+ PMptExtendedConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
{
int rc = VINF_SUCCESS;
uint8_t uAddressForm = MPT_CONFIGURATION_PAGE_ADDRESS_GET_SAS_FORM(PageAddress);
@@ -2613,7 +2804,7 @@ static int lsilogicConfigurationSASPHYPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
if (pPHYPages)
{
- switch(u8PageNumber)
+ switch (u8PageNumber)
{
case 0:
*ppPageHeader = &pPHYPages->SASPHYPage0.u.fields.ExtHeader;
@@ -2635,12 +2826,12 @@ static int lsilogicConfigurationSASPHYPageGetFromNumber(PLSILOGICSCSI pLsiLogic,
return rc;
}
-static int lsilogicConfigurationSASDevicePageGetFromNumber(PLSILOGICSCSI pLsiLogic,
- PMptConfigurationPagesSupported pPages,
- uint8_t u8PageNumber,
- MptConfigurationPageAddress PageAddress,
- PMptExtendedConfigurationPageHeader *ppPageHeader,
- uint8_t **ppbPageData, size_t *pcbPage)
+static int lsilogicR3ConfigurationSASDevicePageGetFromNumber(PLSILOGICSCSI pThis,
+ PMptConfigurationPagesSupported pPages,
+ uint8_t u8PageNumber,
+ MptConfigurationPageAddress PageAddress,
+ PMptExtendedConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
{
int rc = VINF_SUCCESS;
uint8_t uAddressForm = MPT_CONFIGURATION_PAGE_ADDRESS_GET_SAS_FORM(PageAddress);
@@ -2699,7 +2890,7 @@ static int lsilogicConfigurationSASDevicePageGetFromNumber(PLSILOGICSCSI pLsiLog
if (pSASDevice)
{
- switch(u8PageNumber)
+ switch (u8PageNumber)
{
case 0:
*ppPageHeader = &pSASDevice->SASDevicePage0.u.fields.ExtHeader;
@@ -2730,15 +2921,15 @@ static int lsilogicConfigurationSASDevicePageGetFromNumber(PLSILOGICSCSI pLsiLog
* Returns the extended configuration page header and data.
* @returns VINF_SUCCESS if successful
* VERR_NOT_FOUND if the requested page could be found.
- * @param pLsiLogic The LsiLogic controller instance.
+ * @param pThis Pointer to the LsiLogic device state.
* @param pConfigurationReq The configuration request.
* @param u8PageNumber Number of the page to get.
* @param ppPageHeader Where to store the pointer to the page header.
* @param ppbPageData Where to store the pointer to the page data.
*/
-static int lsilogicConfigurationPageGetExtended(PLSILOGICSCSI pLsiLogic, PMptConfigurationRequest pConfigurationReq,
- PMptExtendedConfigurationPageHeader *ppPageHeader,
- uint8_t **ppbPageData, size_t *pcbPage)
+static int lsilogicR3ConfigurationPageGetExtended(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
+ PMptExtendedConfigurationPageHeader *ppPageHeader,
+ uint8_t **ppbPageData, size_t *pcbPage)
{
int rc = VINF_SUCCESS;
@@ -2750,16 +2941,16 @@ static int lsilogicConfigurationPageGetExtended(PLSILOGICSCSI pLsiLogic, PMptCon
{
case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASIOUNIT:
{
- rc = lsilogicConfigurationSASIOUnitPageGetFromNumber(pLsiLogic,
- pLsiLogic->pConfigurationPages,
+ rc = lsilogicR3ConfigurationSASIOUnitPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
pConfigurationReq->u8PageNumber,
ppPageHeader, ppbPageData, pcbPage);
break;
}
case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASPHYS:
{
- rc = lsilogicConfigurationSASPHYPageGetFromNumber(pLsiLogic,
- pLsiLogic->pConfigurationPages,
+ rc = lsilogicR3ConfigurationSASPHYPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
pConfigurationReq->u8PageNumber,
pConfigurationReq->PageAddress,
ppPageHeader, ppbPageData, pcbPage);
@@ -2767,8 +2958,8 @@ static int lsilogicConfigurationPageGetExtended(PLSILOGICSCSI pLsiLogic, PMptCon
}
case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED_SASDEVICE:
{
- rc = lsilogicConfigurationSASDevicePageGetFromNumber(pLsiLogic,
- pLsiLogic->pConfigurationPages,
+ rc = lsilogicR3ConfigurationSASDevicePageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
pConfigurationReq->u8PageNumber,
pConfigurationReq->PageAddress,
ppPageHeader, ppbPageData, pcbPage);
@@ -2787,12 +2978,12 @@ static int lsilogicConfigurationPageGetExtended(PLSILOGICSCSI pLsiLogic, PMptCon
* Processes a Configuration request.
*
* @returns VBox status code.
- * @param pLsiLogic Pointer to the device instance which sends the request.
- * @param pConfigurationReq Pointer to the request structure.
- * @param pReply Pointer to the reply message frame
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pConfigurationReq Pointer to the request structure.
+ * @param pReply Pointer to the reply message frame
*/
-static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConfigurationRequest pConfigurationReq,
- PMptConfigurationReply pReply)
+static int lsilogicR3ProcessConfigurationRequest(PLSILOGICSCSI pThis, PMptConfigurationRequest pConfigurationReq,
+ PMptConfigurationReply pReply)
{
int rc = VINF_SUCCESS;
uint8_t *pbPageData = NULL;
@@ -2802,7 +2993,7 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf
uint8_t u8PageAttribute;
size_t cbPage = 0;
- LogFlowFunc(("pLsiLogic=%#p\n", pLsiLogic));
+ LogFlowFunc(("pThis=%#p\n", pThis));
u8PageType = MPT_CONFIGURATION_PAGE_TYPE_GET(pConfigurationReq->u8PageType);
u8PageAttribute = MPT_CONFIGURATION_PAGE_ATTRIBUTE_GET(pConfigurationReq->u8PageType);
@@ -2825,8 +3016,8 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf
case MPT_CONFIGURATION_PAGE_TYPE_IO_UNIT:
{
/* Get the page data. */
- rc = lsilogicConfigurationIOUnitPageGetFromNumber(pLsiLogic,
- pLsiLogic->pConfigurationPages,
+ rc = lsilogicR3ConfigurationIOUnitPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
pConfigurationReq->u8PageNumber,
&pPageHeader, &pbPageData, &cbPage);
break;
@@ -2834,8 +3025,8 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf
case MPT_CONFIGURATION_PAGE_TYPE_IOC:
{
/* Get the page data. */
- rc = lsilogicConfigurationIOCPageGetFromNumber(pLsiLogic,
- pLsiLogic->pConfigurationPages,
+ rc = lsilogicR3ConfigurationIOCPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
pConfigurationReq->u8PageNumber,
&pPageHeader, &pbPageData, &cbPage);
break;
@@ -2843,8 +3034,8 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf
case MPT_CONFIGURATION_PAGE_TYPE_MANUFACTURING:
{
/* Get the page data. */
- rc = lsilogicConfigurationManufacturingPageGetFromNumber(pLsiLogic,
- pLsiLogic->pConfigurationPages,
+ rc = lsilogicR3ConfigurationManufacturingPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
pConfigurationReq->u8PageNumber,
&pPageHeader, &pbPageData, &cbPage);
break;
@@ -2852,8 +3043,8 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf
case MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_PORT:
{
/* Get the page data. */
- rc = lsilogicConfigurationSCSISPIPortPageGetFromNumber(pLsiLogic,
- pLsiLogic->pConfigurationPages,
+ rc = lsilogicR3ConfigurationSCSISPIPortPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
pConfigurationReq->PageAddress.MPIPortNumber.u8PortNumber,
pConfigurationReq->u8PageNumber,
&pPageHeader, &pbPageData, &cbPage);
@@ -2862,8 +3053,8 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf
case MPT_CONFIGURATION_PAGE_TYPE_SCSI_SPI_DEVICE:
{
/* Get the page data. */
- rc = lsilogicConfigurationSCSISPIDevicePageGetFromNumber(pLsiLogic,
- pLsiLogic->pConfigurationPages,
+ rc = lsilogicR3ConfigurationSCSISPIDevicePageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
pConfigurationReq->PageAddress.BusAndTargetId.u8Bus,
pConfigurationReq->PageAddress.BusAndTargetId.u8TargetID,
pConfigurationReq->u8PageNumber,
@@ -2872,15 +3063,15 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf
}
case MPT_CONFIGURATION_PAGE_TYPE_BIOS:
{
- rc = lsilogicConfigurationBiosPageGetFromNumber(pLsiLogic,
- pLsiLogic->pConfigurationPages,
+ rc = lsilogicR3ConfigurationBiosPageGetFromNumber(pThis,
+ pThis->pConfigurationPages,
pConfigurationReq->u8PageNumber,
&pPageHeader, &pbPageData, &cbPage);
break;
}
case MPT_CONFIGURATION_PAGE_TYPE_EXTENDED:
{
- rc = lsilogicConfigurationPageGetExtended(pLsiLogic,
+ rc = lsilogicR3ConfigurationPageGetExtended(pThis,
pConfigurationReq,
&pExtPageHeader, &pbPageData, &cbPage);
break;
@@ -2945,8 +3136,7 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf
if (pConfigurationReq->SimpleSGElement.f64BitAddress)
GCPhysAddrPageBuffer |= (uint64_t)pConfigurationReq->SimpleSGElement.u32DataBufferAddressHigh << 32;
- PDMDevHlpPhysWrite(pLsiLogic->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData,
- RT_MIN(cbBuffer, cbPage));
+ PDMDevHlpPCIPhysWrite(pThis->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData, RT_MIN(cbBuffer, cbPage));
}
break;
}
@@ -2962,7 +3152,7 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf
LogFlow(("cbBuffer=%u cbPage=%u\n", cbBuffer, cbPage));
- PDMDevHlpPhysRead(pLsiLogic->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData,
+ PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), GCPhysAddrPageBuffer, pbPageData,
RT_MIN(cbBuffer, cbPage));
}
break;
@@ -2978,18 +3168,18 @@ static int lsilogicProcessConfigurationRequest(PLSILOGICSCSI pLsiLogic, PMptConf
* Initializes the configuration pages for the SPI SCSI controller.
*
* @returns nothing
- * @param pLsiLogic Pointer to the Lsilogic SCSI instance.
+ * @param pThis Pointer to the LsiLogic device state.
*/
-static void lsilogicInitializeConfigurationPagesSpi(PLSILOGICSCSI pLsiLogic)
+static void lsilogicR3InitializeConfigurationPagesSpi(PLSILOGICSCSI pThis)
{
- PMptConfigurationPagesSpi pPages = &pLsiLogic->pConfigurationPages->u.SpiPages;
+ PMptConfigurationPagesSpi pPages = &pThis->pConfigurationPages->u.SpiPages;
- AssertMsg(pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI, ("Controller is not the SPI SCSI one\n"));
+ AssertMsg(pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI, ("Controller is not the SPI SCSI one\n"));
- LogFlowFunc(("pLsiLogic=%#p\n", pLsiLogic));
+ LogFlowFunc(("pThis=%#p\n", pThis));
/* Clear everything first. */
- memset(pPages, 0, sizeof(PMptConfigurationPagesSpi));
+ memset(pPages, 0, sizeof(MptConfigurationPagesSpi));
for (unsigned i = 0; i < RT_ELEMENTS(pPages->aPortPages); i++)
{
@@ -3069,7 +3259,7 @@ static void lsilogicInitializeConfigurationPagesSpi(PLSILOGICSCSI pLsiLogic)
* Generates a handle.
*
* @returns the handle.
- * @param pThis The LsiLogic instance.
+ * @param pThis Pointer to the LsiLogic device state.
*/
DECLINLINE(uint16_t) lsilogicGetHandle(PLSILOGICSCSI pThis)
{
@@ -3102,9 +3292,9 @@ void lsilogicSASAddressGenerate(PSASADDRESS pSASAddress, unsigned iId)
* Initializes the configuration pages for the SAS SCSI controller.
*
* @returns nothing
- * @param pThis Pointer to the Lsilogic SCSI instance.
+ * @param pThis Pointer to the LsiLogic device state.
*/
-static void lsilogicInitializeConfigurationPagesSas(PLSILOGICSCSI pThis)
+static void lsilogicR3InitializeConfigurationPagesSas(PLSILOGICSCSI pThis)
{
PMptConfigurationPagesSas pPages = &pThis->pConfigurationPages->u.SasPages;
@@ -3298,16 +3488,16 @@ static void lsilogicInitializeConfigurationPagesSas(PLSILOGICSCSI pThis)
* Initializes the configuration pages.
*
* @returns nothing
- * @param pLsiLogic Pointer to the Lsilogic SCSI instance.
+ * @param pThis Pointer to the LsiLogic device state.
*/
-static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic)
+static void lsilogicR3InitializeConfigurationPages(PLSILOGICSCSI pThis)
{
/* Initialize the common pages. */
PMptConfigurationPagesSupported pPages = (PMptConfigurationPagesSupported)RTMemAllocZ(sizeof(MptConfigurationPagesSupported));
- pLsiLogic->pConfigurationPages = pPages;
+ pThis->pConfigurationPages = pPages;
- LogFlowFunc(("pLsiLogic=%#p\n", pLsiLogic));
+ LogFlowFunc(("pThis=%#p\n", pThis));
/* Clear everything first. */
memset(pPages, 0, sizeof(MptConfigurationPagesSupported));
@@ -3332,12 +3522,12 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic)
MptConfigurationPageManufacturing2, 2,
MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
- if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
{
pPages->ManufacturingPage2.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
pPages->ManufacturingPage2.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SPI_REVISION_ID;
}
- else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
{
pPages->ManufacturingPage2.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
pPages->ManufacturingPage2.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SAS_REVISION_ID;
@@ -3348,12 +3538,12 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic)
MptConfigurationPageManufacturing3, 3,
MPT_CONFIGURATION_PAGE_ATTRIBUTE_PERSISTENT_READONLY);
- if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
{
pPages->ManufacturingPage3.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
pPages->ManufacturingPage3.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SPI_REVISION_ID;
}
- else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
{
pPages->ManufacturingPage3.u.fields.u16PCIDeviceID = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
pPages->ManufacturingPage3.u.fields.u8PCIRevisionID = LSILOGICSCSI_PCI_SAS_REVISION_ID;
@@ -3416,7 +3606,7 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic)
pPages->IOUnitPage2.u.fields.aAdapterOrder[0].fAdapterEnabled = true;
pPages->IOUnitPage2.u.fields.aAdapterOrder[0].fAdapterEmbedded = true;
pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIBusNumber = 0;
- pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIDevFn = pLsiLogic->PciDev.devfn;
+ pPages->IOUnitPage2.u.fields.aAdapterOrder[0].u8PCIDevFn = pThis->PciDev.devfn;
/* I/O Unit page 3. */
MPT_CONFIG_PAGE_HEADER_INIT_IO_UNIT(&pPages->IOUnitPage3,
@@ -3436,7 +3626,7 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic)
pPages->IOCPage0.u.fields.u32TotalNVStore = 0;
pPages->IOCPage0.u.fields.u32FreeNVStore = 0;
- if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
{
pPages->IOCPage0.u.fields.u16VendorId = LSILOGICSCSI_PCI_VENDOR_ID;
pPages->IOCPage0.u.fields.u16DeviceId = LSILOGICSCSI_PCI_SPI_DEVICE_ID;
@@ -3445,7 +3635,7 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic)
pPages->IOCPage0.u.fields.u16SubsystemVendorId = LSILOGICSCSI_PCI_SPI_SUBSYSTEM_VENDOR_ID;
pPages->IOCPage0.u.fields.u16SubsystemId = LSILOGICSCSI_PCI_SPI_SUBSYSTEM_ID;
}
- else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
{
pPages->IOCPage0.u.fields.u16VendorId = LSILOGICSCSI_PCI_VENDOR_ID;
pPages->IOCPage0.u.fields.u16DeviceId = LSILOGICSCSI_PCI_SAS_DEVICE_ID;
@@ -3502,122 +3692,26 @@ static void lsilogicInitializeConfigurationPages(PLSILOGICSCSI pLsiLogic)
MptConfigurationPageBIOS4, 4,
MPT_CONFIGURATION_PAGE_ATTRIBUTE_CHANGEABLE);
- if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
- lsilogicInitializeConfigurationPagesSpi(pLsiLogic);
- else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
- lsilogicInitializeConfigurationPagesSas(pLsiLogic);
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ lsilogicR3InitializeConfigurationPagesSpi(pThis);
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ lsilogicR3InitializeConfigurationPagesSas(pThis);
else
- AssertMsgFailed(("Invalid controller type %d\n", pLsiLogic->enmCtrlType));
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
}
/**
- * Transmit queue consumer
- * Queue a new async task.
- *
- * @returns Success indicator.
- * If false the item will not be removed and the flushing will stop.
- * @param pDevIns The device instance.
- * @param pItem The item to consume. Upon return this item will be freed.
+ * @callback_method_impl{FNPDMQUEUEDEV, Transmit queue consumer.}
*/
-static DECLCALLBACK(bool) lsilogicNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
+static DECLCALLBACK(bool) lsilogicR3NotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
{
- PLSILOGICSCSI pLsiLogic = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
int rc = VINF_SUCCESS;
LogFlowFunc(("pDevIns=%#p pItem=%#p\n", pDevIns, pItem));
- /* Reset notification event. */
- ASMAtomicXchgBool(&pLsiLogic->fNotificationSend, false);
-
- /* Only process request which arrived before we received the notification. */
- uint32_t uRequestQueueNextEntryWrite = ASMAtomicReadU32(&pLsiLogic->uRequestQueueNextEntryFreeWrite);
-
- /* Go through the messages now and process them. */
- while ( RT_LIKELY(pLsiLogic->enmState == LSILOGICSTATE_OPERATIONAL)
- && (pLsiLogic->uRequestQueueNextAddressRead != uRequestQueueNextEntryWrite))
- {
- uint32_t u32RequestMessageFrameDesc = pLsiLogic->CTX_SUFF(pRequestQueueBase)[pLsiLogic->uRequestQueueNextAddressRead];
- RTGCPHYS GCPhysMessageFrameAddr = LSILOGIC_RTGCPHYS_FROM_U32(pLsiLogic->u32HostMFAHighAddr,
- (u32RequestMessageFrameDesc & ~0x07));
-
- PLSILOGICTASKSTATE pTaskState;
-
- /* Get new task state. */
- rc = RTMemCacheAllocEx(pLsiLogic->hTaskCache, (void **)&pTaskState);
- AssertRC(rc);
-
- pTaskState->GCPhysMessageFrameAddr = GCPhysMessageFrameAddr;
-
- /* Read the message header from the guest first. */
- PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pTaskState->GuestRequest, sizeof(MptMessageHdr));
-
- /* Determine the size of the request. */
- uint32_t cbRequest = 0;
-
- switch (pTaskState->GuestRequest.Header.u8Function)
- {
- case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST:
- cbRequest = sizeof(MptSCSIIORequest);
- break;
- case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT:
- cbRequest = sizeof(MptSCSITaskManagementRequest);
- break;
- case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT:
- cbRequest = sizeof(MptIOCInitRequest);
- break;
- case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS:
- cbRequest = sizeof(MptIOCFactsRequest);
- break;
- case MPT_MESSAGE_HDR_FUNCTION_CONFIG:
- cbRequest = sizeof(MptConfigurationRequest);
- break;
- case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS:
- cbRequest = sizeof(MptPortFactsRequest);
- break;
- case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE:
- cbRequest = sizeof(MptPortEnableRequest);
- break;
- case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION:
- cbRequest = sizeof(MptEventNotificationRequest);
- break;
- case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK:
- AssertMsgFailed(("todo\n"));
- //cbRequest = sizeof(MptEventAckRequest);
- break;
- case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD:
- cbRequest = sizeof(MptFWDownloadRequest);
- break;
- case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD:
- cbRequest = sizeof(MptFWUploadRequest);
- break;
- default:
- AssertMsgFailed(("Unknown function issued %u\n", pTaskState->GuestRequest.Header.u8Function));
- lsilogicSetIOCFaultCode(pLsiLogic, LSILOGIC_IOCSTATUS_INVALID_FUNCTION);
- }
-
- if (cbRequest != 0)
- {
- /* Read the complete message frame from guest memory now. */
- PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pTaskState->GuestRequest, cbRequest);
-
- /* Handle SCSI I/O requests now. */
- if (pTaskState->GuestRequest.Header.u8Function == MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST)
- {
- rc = lsilogicProcessSCSIIORequest(pLsiLogic, pTaskState);
- AssertRC(rc);
- }
- else
- {
- MptReplyUnion Reply;
- rc = lsilogicProcessMessageRequest(pLsiLogic, &pTaskState->GuestRequest.Header, &Reply);
- AssertRC(rc);
- RTMemCacheFree(pLsiLogic->hTaskCache, pTaskState);
- }
-
- pLsiLogic->uRequestQueueNextAddressRead++;
- pLsiLogic->uRequestQueueNextAddressRead %= pLsiLogic->cRequestQueueEntries;
- }
- }
+ rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess);
+ AssertRC(rc);
return true;
}
@@ -3627,10 +3721,10 @@ static DECLCALLBACK(bool) lsilogicNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQU
*
* @returns VBox status code.
*
- * @param pThis The LsiLogic devi state.
- * @param pcszCtrlType The string to use.
+ * @param pThis Pointer to the LsiLogic device state.
+ * @param pcszCtrlType The string to use.
*/
-static int lsilogicGetCtrlTypeFromString(PLSILOGICSCSI pThis, const char *pcszCtrlType)
+static int lsilogicR3GetCtrlTypeFromString(PLSILOGICSCSI pThis, const char *pcszCtrlType)
{
int rc = VERR_INVALID_PARAMETER;
@@ -3649,28 +3743,18 @@ static int lsilogicGetCtrlTypeFromString(PLSILOGICSCSI pThis, const char *pcszCt
}
/**
- * Port I/O Handler for IN operations - legacy port.
- *
- * @returns VBox status code.
- *
- * @param pDevIns The device instance.
- * @param pvUser User argument.
- * @param uPort Port number used for the IN operation.
- * @param pu32 Where to store the result.
- * @param cb Number of bytes read.
+ * @callback_method_impl{FNIOMIOPORTIN, Legacy ISA port.}
*/
-static int lsilogicIsaIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
- RTIOPORT Port, uint32_t *pu32, unsigned cb)
+static DECLCALLBACK(int) lsilogicR3IsaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
- int rc;
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
Assert(cb == 1);
- uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
- ? Port - LSILOGIC_BIOS_IO_PORT
- : Port - LSILOGIC_SAS_BIOS_IO_PORT;
- rc = vboxscsiReadRegister(&pThis->VBoxSCSI, iRegister, pu32);
+ uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? Port - LSILOGIC_BIOS_IO_PORT
+ : Port - LSILOGIC_SAS_BIOS_IO_PORT;
+ int rc = vboxscsiReadRegister(&pThis->VBoxSCSI, iRegister, pu32);
Log2(("%s: pu32=%p:{%.*Rhxs} iRegister=%d rc=%Rrc\n",
__FUNCTION__, pu32, 1, pu32, iRegister, rc));
@@ -3682,41 +3766,41 @@ static int lsilogicIsaIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
* Prepares a request from the BIOS.
*
* @returns VBox status code.
- * @param pLsiLogic Pointer to the LsiLogic device instance.
+ * @param pThis Pointer to the LsiLogic device state.
*/
-static int lsilogicPrepareBIOSSCSIRequest(PLSILOGICSCSI pLsiLogic)
+static int lsilogicR3PrepareBiosScsiRequest(PLSILOGICSCSI pThis)
{
int rc;
- PLSILOGICTASKSTATE pTaskState;
+ PLSILOGICREQ pLsiReq;
uint32_t uTargetDevice;
- rc = RTMemCacheAllocEx(pLsiLogic->hTaskCache, (void **)&pTaskState);
+ rc = RTMemCacheAllocEx(pThis->hTaskCache, (void **)&pLsiReq);
AssertMsgRCReturn(rc, ("Getting task from cache failed rc=%Rrc\n", rc), rc);
- pTaskState->fBIOS = true;
+ pLsiReq->fBIOS = true;
- rc = vboxscsiSetupRequest(&pLsiLogic->VBoxSCSI, &pTaskState->PDMScsiRequest, &uTargetDevice);
+ rc = vboxscsiSetupRequest(&pThis->VBoxSCSI, &pLsiReq->PDMScsiRequest, &uTargetDevice);
AssertMsgRCReturn(rc, ("Setting up SCSI request failed rc=%Rrc\n", rc), rc);
- pTaskState->PDMScsiRequest.pvUser = pTaskState;
+ pLsiReq->PDMScsiRequest.pvUser = pLsiReq;
- if (uTargetDevice < pLsiLogic->cDeviceStates)
+ if (uTargetDevice < pThis->cDeviceStates)
{
- pTaskState->pTargetDevice = &pLsiLogic->paDeviceStates[uTargetDevice];
+ pLsiReq->pTargetDevice = &pThis->paDeviceStates[uTargetDevice];
- if (pTaskState->pTargetDevice->pDrvBase)
+ if (pLsiReq->pTargetDevice->pDrvBase)
{
- ASMAtomicIncU32(&pTaskState->pTargetDevice->cOutstandingRequests);
+ ASMAtomicIncU32(&pLsiReq->pTargetDevice->cOutstandingRequests);
- rc = pTaskState->pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pTaskState->pTargetDevice->pDrvSCSIConnector,
- &pTaskState->PDMScsiRequest);
+ rc = pLsiReq->pTargetDevice->pDrvSCSIConnector->pfnSCSIRequestSend(pLsiReq->pTargetDevice->pDrvSCSIConnector,
+ &pLsiReq->PDMScsiRequest);
AssertMsgRCReturn(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc), rc);
return VINF_SUCCESS;
}
}
/* Device is not present. */
- AssertMsg(pTaskState->PDMScsiRequest.pbCDB[0] == SCSI_INQUIRY,
+ AssertMsg(pLsiReq->PDMScsiRequest.pbCDB[0] == SCSI_INQUIRY,
("Device is not present but command is not inquiry\n"));
SCSIINQUIRYDATA ScsiInquiryData;
@@ -3725,44 +3809,32 @@ static int lsilogicPrepareBIOSSCSIRequest(PLSILOGICSCSI pLsiLogic)
ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
- memcpy(pLsiLogic->VBoxSCSI.pBuf, &ScsiInquiryData, 5);
+ memcpy(pThis->VBoxSCSI.pbBuf, &ScsiInquiryData, 5);
- rc = vboxscsiRequestFinished(&pLsiLogic->VBoxSCSI, &pTaskState->PDMScsiRequest);
+ rc = vboxscsiRequestFinished(&pThis->VBoxSCSI, &pLsiReq->PDMScsiRequest, SCSI_STATUS_OK);
AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc);
- RTMemCacheFree(pLsiLogic->hTaskCache, pTaskState);
+ RTMemCacheFree(pThis->hTaskCache, pLsiReq);
return rc;
}
/**
- * Port I/O Handler for OUT operations - legacy port.
- *
- * @returns VBox status code.
- *
- * @param pDevIns The device instance.
- * @param pvUser User argument.
- * @param uPort Port number used for the IN operation.
- * @param u32 The value to output.
- * @param cb The value size in bytes.
+ * @callback_method_impl{FNIOMIOPORTOUT, Legacy ISA port.}
*/
-static int lsilogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
- RTIOPORT Port, uint32_t u32, unsigned cb)
+static DECLCALLBACK(int) lsilogicR3IsaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
- int rc;
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
-
- Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n",
- pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port));
+ Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port));
Assert(cb == 1);
- uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
- ? Port - LSILOGIC_BIOS_IO_PORT
- : Port - LSILOGIC_SAS_BIOS_IO_PORT;
- rc = vboxscsiWriteRegister(&pThis->VBoxSCSI, iRegister, (uint8_t)u32);
+ uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? Port - LSILOGIC_BIOS_IO_PORT
+ : Port - LSILOGIC_SAS_BIOS_IO_PORT;
+ int rc = vboxscsiWriteRegister(&pThis->VBoxSCSI, iRegister, (uint8_t)u32);
if (rc == VERR_MORE_DATA)
{
- rc = lsilogicPrepareBIOSSCSIRequest(pThis);
+ rc = lsilogicR3PrepareBiosScsiRequest(pThis);
AssertRC(rc);
}
else if (RT_FAILURE(rc))
@@ -3772,25 +3844,22 @@ static int lsilogicIsaIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
}
/**
- * Port I/O Handler for primary port range OUT string operations.
- * @see FNIOMIOPORTOUTSTRING for details.
+ * @callback_method_impl{FNIOMIOPORTOUTSTRING,
+ * Port I/O Handler for primary port range OUT string operations.}
*/
-static DECLCALLBACK(int) lsilogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
+static DECLCALLBACK(int) lsilogicR3IsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port,
+ PRTGCPTR pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
- int rc;
-
- Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
- pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
+ Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n", pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
- uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
- ? Port - LSILOGIC_BIOS_IO_PORT
- : Port - LSILOGIC_SAS_BIOS_IO_PORT;
- rc = vboxscsiWriteString(pDevIns, &pThis->VBoxSCSI, iRegister,
- pGCPtrSrc, pcTransfer, cb);
+ uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? Port - LSILOGIC_BIOS_IO_PORT
+ : Port - LSILOGIC_SAS_BIOS_IO_PORT;
+ int rc = vboxscsiWriteString(pDevIns, &pThis->VBoxSCSI, iRegister, pGCPtrSrc, pcTransfer, cb);
if (rc == VERR_MORE_DATA)
{
- rc = lsilogicPrepareBIOSSCSIRequest(pThis);
+ rc = lsilogicR3PrepareBiosScsiRequest(pThis);
AssertRC(rc);
}
else if (RT_FAILURE(rc))
@@ -3800,26 +3869,29 @@ static DECLCALLBACK(int) lsilogicIsaIOPortWriteStr(PPDMDEVINS pDevIns, void *pvU
}
/**
- * Port I/O Handler for primary port range IN string operations.
- * @see FNIOMIOPORTINSTRING for details.
+ * @callback_method_impl{FNIOMIOPORTINSTRING,
+ * Port I/O Handler for primary port range IN string operations.}
*/
-static DECLCALLBACK(int) lsilogicIsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
+static DECLCALLBACK(int) lsilogicR3IsaIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst,
+ PRTGCUINTREG pcTransfer, unsigned cb)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
- uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
- ? Port - LSILOGIC_BIOS_IO_PORT
- : Port - LSILOGIC_SAS_BIOS_IO_PORT;
- return vboxscsiReadString(pDevIns, &pThis->VBoxSCSI, iRegister,
- pGCPtrDst, pcTransfer, cb);
+ uint8_t iRegister = pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
+ ? Port - LSILOGIC_BIOS_IO_PORT
+ : Port - LSILOGIC_SAS_BIOS_IO_PORT;
+ return vboxscsiReadString(pDevIns, &pThis->VBoxSCSI, iRegister, pGCPtrDst, pcTransfer, cb);
}
-static DECLCALLBACK(int) lsilogicMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
- RTGCPHYS GCPhysAddress, uint32_t cb,
- PCIADDRESSSPACE enmType)
+/**
+ * @callback_method_impl{FNPCIIOREGIONMAP}
+ */
+static DECLCALLBACK(int) lsilogicR3Map(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
+ RTGCPHYS GCPhysAddress, uint32_t cb,
+ PCIADDRESSSPACE enmType)
{
PPDMDEVINS pDevIns = pPciDev->pDevIns;
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
@@ -3837,11 +3909,24 @@ static DECLCALLBACK(int) lsilogicMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegio
|| (enmType == PCI_ADDRESS_SPACE_IO && cb >= LSILOGIC_PCI_SPACE_IO_SIZE),
("PCI region type and size do not match\n"));
- if ((enmType == PCI_ADDRESS_SPACE_MEM) && (iRegion == 1))
+ if (enmType == PCI_ADDRESS_SPACE_MEM && iRegion == 1)
{
- /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
+ /*
+ * Non-4-byte read access to LSILOGIC_REG_REPLY_QUEUE may cause real strange behavior
+ * because the data is part of a physical guest address. But some drivers use 1-byte
+ * access to scan for SCSI controllers. So, we simplify our code by telling IOM to
+ * read DWORDs.
+ *
+ * Regarding writes, we couldn't find anything specific in the specs about what should
+ * happen. So far we've ignored unaligned writes and assumed the missing bytes of
+ * byte and word access to be zero. We suspect that IOMMMIO_FLAGS_WRITE_ONLY_DWORD
+ * or IOMMMIO_FLAGS_WRITE_DWORD_ZEROED would be the most appropriate here, but since we
+ * don't have real hw to test one, the old behavior is kept exactly like it used to be.
+ */
+ /** @todo Check out unaligned writes and non-dword writes on real LsiLogic
+ * hardware. */
rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
- IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_PASSTHRU,
lsilogicMMIOWrite, lsilogicMMIORead, pcszCtrl);
if (RT_FAILURE(rc))
return rc;
@@ -3921,13 +4006,9 @@ static DECLCALLBACK(int) lsilogicMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegio
}
/**
- * LsiLogic status info callback.
- *
- * @param pDevIns The device instance.
- * @param pHlp The output helpers.
- * @param pszArgs The arguments.
+ * @callback_method_impl{PFNDBGFHANDLERDEV}
*/
-static DECLCALLBACK(void) lsilogicInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+static DECLCALLBACK(void) lsilogicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
bool fVerbose = false;
@@ -3955,7 +4036,7 @@ static DECLCALLBACK(void) lsilogicInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, c
*/
pHlp->pfnPrintf(pHlp, "enmState=%u\n", pThis->enmState);
pHlp->pfnPrintf(pHlp, "enmWhoInit=%u\n", pThis->enmWhoInit);
- pHlp->pfnPrintf(pHlp, "fDoorbellInProgress=%RTbool\n", pThis->fDoorbellInProgress);
+ pHlp->pfnPrintf(pHlp, "enmDoorbellState=%d\n", pThis->enmDoorbellState);
pHlp->pfnPrintf(pHlp, "fDiagnosticEnabled=%RTbool\n", pThis->fDiagnosticEnabled);
pHlp->pfnPrintf(pHlp, "fNotificationSend=%RTbool\n", pThis->fNotificationSend);
pHlp->pfnPrintf(pHlp, "fEventNotificationEnabled=%RTbool\n", pThis->fEventNotificationEnabled);
@@ -4019,9 +4100,9 @@ static DECLCALLBACK(void) lsilogicInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, c
*
* @returns VBox status code.
*
- * @param pThis The LsiLogic device instance.
+ * @param pThis Pointer to the LsiLogic device state.
*/
-static int lsilogicQueuesAlloc(PLSILOGICSCSI pThis)
+static int lsilogicR3QueuesAlloc(PLSILOGICSCSI pThis)
{
PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
uint32_t cbQueues;
@@ -4053,9 +4134,9 @@ static int lsilogicQueuesAlloc(PLSILOGICSCSI pThis)
*
* @returns nothing.
*
- * @param pThis The LsiLogic device instance.
+ * @param pThis Pointer to the LsiLogic device state.
*/
-static void lsilogicQueuesFree(PLSILOGICSCSI pThis)
+static void lsilogicR3QueuesFree(PLSILOGICSCSI pThis)
{
PVM pVM = PDMDevHlpGetVM(pThis->pDevInsR3);
int rc = VINF_SUCCESS;
@@ -4070,14 +4151,152 @@ static void lsilogicQueuesFree(PLSILOGICSCSI pThis)
pThis->pRequestQueueBaseR3 = NULL;
}
+
+/* The worker thread. */
+static DECLCALLBACK(int) lsilogicR3Worker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ PLSILOGICSCSI pThis = (PLSILOGICSCSI)pThread->pvUser;
+ int rc = VINF_SUCCESS;
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ bool fNotificationSend;
+
+ ASMAtomicWriteBool(&pThis->fWrkThreadSleeping, true);
+ fNotificationSend = ASMAtomicXchgBool(&pThis->fNotificationSend, false);
+ if (!fNotificationSend)
+ {
+ Assert(ASMAtomicReadBool(&pThis->fWrkThreadSleeping));
+ rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pThis->hEvtProcess, RT_INDEFINITE_WAIT);
+ AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
+ if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
+ break;
+ LogFlowFunc(("Woken up with rc=%Rrc\n", rc));
+ fNotificationSend = ASMAtomicXchgBool(&pThis->fNotificationSend, false);
+ }
+
+ ASMAtomicWriteBool(&pThis->fWrkThreadSleeping, false);
+
+ /* Only process request which arrived before we received the notification. */
+ uint32_t uRequestQueueNextEntryWrite = ASMAtomicReadU32(&pThis->uRequestQueueNextEntryFreeWrite);
+
+ /* Go through the messages now and process them. */
+ while ( RT_LIKELY(pThis->enmState == LSILOGICSTATE_OPERATIONAL)
+ && (pThis->uRequestQueueNextAddressRead != uRequestQueueNextEntryWrite))
+ {
+ uint32_t u32RequestMessageFrameDesc = pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextAddressRead];
+ RTGCPHYS GCPhysMessageFrameAddr = LSILOGIC_RTGCPHYS_FROM_U32(pThis->u32HostMFAHighAddr,
+ (u32RequestMessageFrameDesc & ~0x07));
+
+ PLSILOGICREQ pLsiReq;
+
+ /* Get new task state. */
+ rc = RTMemCacheAllocEx(pThis->hTaskCache, (void **)&pLsiReq);
+ AssertRC(rc);
+
+ pLsiReq->GCPhysMessageFrameAddr = GCPhysMessageFrameAddr;
+
+ /* Read the message header from the guest first. */
+ PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pLsiReq->GuestRequest, sizeof(MptMessageHdr));
+
+ /* Determine the size of the request. */
+ uint32_t cbRequest = 0;
+
+ switch (pLsiReq->GuestRequest.Header.u8Function)
+ {
+ case MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST:
+ cbRequest = sizeof(MptSCSIIORequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_SCSI_TASK_MGMT:
+ cbRequest = sizeof(MptSCSITaskManagementRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_IOC_INIT:
+ cbRequest = sizeof(MptIOCInitRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_IOC_FACTS:
+ cbRequest = sizeof(MptIOCFactsRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_CONFIG:
+ cbRequest = sizeof(MptConfigurationRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_PORT_FACTS:
+ cbRequest = sizeof(MptPortFactsRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_PORT_ENABLE:
+ cbRequest = sizeof(MptPortEnableRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_EVENT_NOTIFICATION:
+ cbRequest = sizeof(MptEventNotificationRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_EVENT_ACK:
+ AssertMsgFailed(("todo\n"));
+ //cbRequest = sizeof(MptEventAckRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_FW_DOWNLOAD:
+ cbRequest = sizeof(MptFWDownloadRequest);
+ break;
+ case MPT_MESSAGE_HDR_FUNCTION_FW_UPLOAD:
+ cbRequest = sizeof(MptFWUploadRequest);
+ break;
+ default:
+ AssertMsgFailed(("Unknown function issued %u\n", pLsiReq->GuestRequest.Header.u8Function));
+ lsilogicSetIOCFaultCode(pThis, LSILOGIC_IOCSTATUS_INVALID_FUNCTION);
+ }
+
+ if (cbRequest != 0)
+ {
+ /* Read the complete message frame from guest memory now. */
+ PDMDevHlpPhysRead(pDevIns, GCPhysMessageFrameAddr, &pLsiReq->GuestRequest, cbRequest);
+
+ /* Handle SCSI I/O requests now. */
+ if (pLsiReq->GuestRequest.Header.u8Function == MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST)
+ {
+ rc = lsilogicR3ProcessSCSIIORequest(pThis, pLsiReq);
+ AssertRC(rc);
+ }
+ else
+ {
+ MptReplyUnion Reply;
+ rc = lsilogicR3ProcessMessageRequest(pThis, &pLsiReq->GuestRequest.Header, &Reply);
+ AssertRC(rc);
+ RTMemCacheFree(pThis->hTaskCache, pLsiReq);
+ }
+
+ pThis->uRequestQueueNextAddressRead++;
+ pThis->uRequestQueueNextAddressRead %= pThis->cRequestQueueEntries;
+ }
+ } /* While request frames available. */
+ } /* While running */
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Unblock the worker thread so it can respond to a state change.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The pcnet device instance.
+ * @param pThread The send thread.
+ */
+static DECLCALLBACK(int) lsilogicR3WorkerWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ return SUPSemEventSignal(pThis->pSupDrvSession, pThis->hEvtProcess);
+}
+
+
/**
* Kicks the controller to process pending tasks after the VM was resumed
* or loaded from a saved state.
*
* @returns nothing.
- * @param pThis The LsiLogic device instance.
+ * @param pThis Pointer to the LsiLogic device state.
*/
-static void lsilogicKick(PLSILOGICSCSI pThis)
+static void lsilogicR3Kick(PLSILOGICSCSI pThis)
{
if (pThis->fNotificationSend)
{
@@ -4089,13 +4308,21 @@ static void lsilogicKick(PLSILOGICSCSI pThis)
else if (pThis->VBoxSCSI.fBusy)
{
/* The BIOS had a request active when we got suspended. Resume it. */
- int rc = lsilogicPrepareBIOSSCSIRequest(pThis);
+ int rc = lsilogicR3PrepareBiosScsiRequest(pThis);
AssertRC(rc);
}
}
-static DECLCALLBACK(int) lsilogicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+
+/*
+ * Saved state.
+ */
+
+/**
+ * @callback_method_impl{FNSSMDEVLIVEEXEC}
+ */
+static DECLCALLBACK(int) lsilogicR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
@@ -4110,62 +4337,77 @@ static DECLCALLBACK(int) lsilogicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
return VINF_SSM_DONT_CALL_AGAIN;
}
-static DECLCALLBACK(int) lsilogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) lsilogicR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
- PLSILOGICSCSI pLsiLogic = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
/* Every device first. */
- lsilogicLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
- for (unsigned i = 0; i < pLsiLogic->cDeviceStates; i++)
+ lsilogicR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
+ for (unsigned i = 0; i < pThis->cDeviceStates; i++)
{
- PLSILOGICDEVICE pDevice = &pLsiLogic->paDeviceStates[i];
+ PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
AssertMsg(!pDevice->cOutstandingRequests,
("There are still outstanding requests on this device\n"));
SSMR3PutU32(pSSM, pDevice->cOutstandingRequests);
}
/* Now the main device state. */
- SSMR3PutU32 (pSSM, pLsiLogic->enmState);
- SSMR3PutU32 (pSSM, pLsiLogic->enmWhoInit);
- SSMR3PutBool (pSSM, pLsiLogic->fDoorbellInProgress);
- SSMR3PutBool (pSSM, pLsiLogic->fDiagnosticEnabled);
- SSMR3PutBool (pSSM, pLsiLogic->fNotificationSend);
- SSMR3PutBool (pSSM, pLsiLogic->fEventNotificationEnabled);
- SSMR3PutU32 (pSSM, pLsiLogic->uInterruptMask);
- SSMR3PutU32 (pSSM, pLsiLogic->uInterruptStatus);
- for (unsigned i = 0; i < RT_ELEMENTS(pLsiLogic->aMessage); i++)
- SSMR3PutU32 (pSSM, pLsiLogic->aMessage[i]);
- SSMR3PutU32 (pSSM, pLsiLogic->iMessage);
- SSMR3PutU32 (pSSM, pLsiLogic->cMessage);
- SSMR3PutMem (pSSM, &pLsiLogic->ReplyBuffer, sizeof(pLsiLogic->ReplyBuffer));
- SSMR3PutU32 (pSSM, pLsiLogic->uNextReplyEntryRead);
- SSMR3PutU32 (pSSM, pLsiLogic->cReplySize);
- SSMR3PutU16 (pSSM, pLsiLogic->u16IOCFaultCode);
- SSMR3PutU32 (pSSM, pLsiLogic->u32HostMFAHighAddr);
- SSMR3PutU32 (pSSM, pLsiLogic->u32SenseBufferHighAddr);
- SSMR3PutU8 (pSSM, pLsiLogic->cMaxDevices);
- SSMR3PutU8 (pSSM, pLsiLogic->cMaxBuses);
- SSMR3PutU16 (pSSM, pLsiLogic->cbReplyFrame);
- SSMR3PutU32 (pSSM, pLsiLogic->iDiagnosticAccess);
- SSMR3PutU32 (pSSM, pLsiLogic->cReplyQueueEntries);
- SSMR3PutU32 (pSSM, pLsiLogic->cRequestQueueEntries);
- SSMR3PutU32 (pSSM, pLsiLogic->uReplyFreeQueueNextEntryFreeWrite);
- SSMR3PutU32 (pSSM, pLsiLogic->uReplyFreeQueueNextAddressRead);
- SSMR3PutU32 (pSSM, pLsiLogic->uReplyPostQueueNextEntryFreeWrite);
- SSMR3PutU32 (pSSM, pLsiLogic->uReplyPostQueueNextAddressRead);
- SSMR3PutU32 (pSSM, pLsiLogic->uRequestQueueNextEntryFreeWrite);
- SSMR3PutU32 (pSSM, pLsiLogic->uRequestQueueNextAddressRead);
-
- for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++)
- SSMR3PutU32(pSSM, pLsiLogic->pReplyFreeQueueBaseR3[i]);
- for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++)
- SSMR3PutU32(pSSM, pLsiLogic->pReplyPostQueueBaseR3[i]);
- for (unsigned i = 0; i < pLsiLogic->cRequestQueueEntries; i++)
- SSMR3PutU32(pSSM, pLsiLogic->pRequestQueueBaseR3[i]);
-
- SSMR3PutU16 (pSSM, pLsiLogic->u16NextHandle);
-
- PMptConfigurationPagesSupported pPages = pLsiLogic->pConfigurationPages;
+ SSMR3PutU32 (pSSM, pThis->enmState);
+ SSMR3PutU32 (pSSM, pThis->enmWhoInit);
+ SSMR3PutU32 (pSSM, pThis->enmDoorbellState);
+ SSMR3PutBool (pSSM, pThis->fDiagnosticEnabled);
+ SSMR3PutBool (pSSM, pThis->fNotificationSend);
+ SSMR3PutBool (pSSM, pThis->fEventNotificationEnabled);
+ SSMR3PutU32 (pSSM, pThis->uInterruptMask);
+ SSMR3PutU32 (pSSM, pThis->uInterruptStatus);
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++)
+ SSMR3PutU32 (pSSM, pThis->aMessage[i]);
+ SSMR3PutU32 (pSSM, pThis->iMessage);
+ SSMR3PutU32 (pSSM, pThis->cMessage);
+ SSMR3PutMem (pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer));
+ SSMR3PutU32 (pSSM, pThis->uNextReplyEntryRead);
+ SSMR3PutU32 (pSSM, pThis->cReplySize);
+ SSMR3PutU16 (pSSM, pThis->u16IOCFaultCode);
+ SSMR3PutU32 (pSSM, pThis->u32HostMFAHighAddr);
+ SSMR3PutU32 (pSSM, pThis->u32SenseBufferHighAddr);
+ SSMR3PutU8 (pSSM, pThis->cMaxDevices);
+ SSMR3PutU8 (pSSM, pThis->cMaxBuses);
+ SSMR3PutU16 (pSSM, pThis->cbReplyFrame);
+ SSMR3PutU32 (pSSM, pThis->iDiagnosticAccess);
+ SSMR3PutU32 (pSSM, pThis->cReplyQueueEntries);
+ SSMR3PutU32 (pSSM, pThis->cRequestQueueEntries);
+ SSMR3PutU32 (pSSM, pThis->uReplyFreeQueueNextEntryFreeWrite);
+ SSMR3PutU32 (pSSM, pThis->uReplyFreeQueueNextAddressRead);
+ SSMR3PutU32 (pSSM, pThis->uReplyPostQueueNextEntryFreeWrite);
+ SSMR3PutU32 (pSSM, pThis->uReplyPostQueueNextAddressRead);
+ SSMR3PutU32 (pSSM, pThis->uRequestQueueNextEntryFreeWrite);
+ SSMR3PutU32 (pSSM, pThis->uRequestQueueNextAddressRead);
+
+ for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
+ SSMR3PutU32(pSSM, pThis->pReplyFreeQueueBaseR3[i]);
+ for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
+ SSMR3PutU32(pSSM, pThis->pReplyPostQueueBaseR3[i]);
+ for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
+ SSMR3PutU32(pSSM, pThis->pRequestQueueBaseR3[i]);
+
+ SSMR3PutU16 (pSSM, pThis->u16NextHandle);
+
+ /* Save diagnostic memory register and data regions. */
+ SSMR3PutU32 (pSSM, pThis->u32DiagMemAddr);
+ SSMR3PutU32 (pSSM, lsilogicR3MemRegionsCount(pThis));
+
+ PLSILOGICMEMREGN pIt = NULL;
+ RTListForEach(&pThis->ListMemRegns, pIt, LSILOGICMEMREGN, NodeList)
+ {
+ SSMR3PutU32(pSSM, pIt->u32AddrStart);
+ SSMR3PutU32(pSSM, pIt->u32AddrEnd);
+ SSMR3PutMem(pSSM, &pIt->au32Data[0], (pIt->u32AddrEnd - pIt->u32AddrStart + 1) * sizeof(uint32_t));
+ }
+
+ PMptConfigurationPagesSupported pPages = pThis->pConfigurationPages;
SSMR3PutMem (pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0));
SSMR3PutMem (pSSM, &pPages->ManufacturingPage1, sizeof(MptConfigurationPageManufacturing1));
@@ -4193,7 +4435,7 @@ static DECLCALLBACK(int) lsilogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
SSMR3PutMem (pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4));
/* Device dependent pages */
- if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
{
PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
@@ -4209,7 +4451,7 @@ static DECLCALLBACK(int) lsilogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
SSMR3PutMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3));
}
}
- else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
{
PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages;
@@ -4246,39 +4488,47 @@ static DECLCALLBACK(int) lsilogicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
}
}
else
- AssertMsgFailed(("Invalid controller type %d\n", pLsiLogic->enmCtrlType));
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
/* Now the data for the BIOS interface. */
- SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.regIdentify);
- SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.uTargetDevice);
- SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.uTxDir);
- SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.cbCDB);
- SSMR3PutMem (pSSM, pLsiLogic->VBoxSCSI.aCDB, sizeof(pLsiLogic->VBoxSCSI.aCDB));
- SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.iCDB);
- SSMR3PutU32 (pSSM, pLsiLogic->VBoxSCSI.cbBuf);
- SSMR3PutU32 (pSSM, pLsiLogic->VBoxSCSI.iBuf);
- SSMR3PutBool (pSSM, pLsiLogic->VBoxSCSI.fBusy);
- SSMR3PutU8 (pSSM, pLsiLogic->VBoxSCSI.enmState);
- if (pLsiLogic->VBoxSCSI.cbBuf)
- SSMR3PutMem(pSSM, pLsiLogic->VBoxSCSI.pBuf, pLsiLogic->VBoxSCSI.cbBuf);
+ SSMR3PutU8 (pSSM, pThis->VBoxSCSI.regIdentify);
+ SSMR3PutU8 (pSSM, pThis->VBoxSCSI.uTargetDevice);
+ SSMR3PutU8 (pSSM, pThis->VBoxSCSI.uTxDir);
+ SSMR3PutU8 (pSSM, pThis->VBoxSCSI.cbCDB);
+ SSMR3PutMem (pSSM, pThis->VBoxSCSI.abCDB, sizeof(pThis->VBoxSCSI.abCDB));
+ SSMR3PutU8 (pSSM, pThis->VBoxSCSI.iCDB);
+ SSMR3PutU32 (pSSM, pThis->VBoxSCSI.cbBuf);
+ SSMR3PutU32 (pSSM, pThis->VBoxSCSI.iBuf);
+ SSMR3PutBool (pSSM, pThis->VBoxSCSI.fBusy);
+ SSMR3PutU8 (pSSM, pThis->VBoxSCSI.enmState);
+ if (pThis->VBoxSCSI.cbBuf)
+ SSMR3PutMem(pSSM, pThis->VBoxSCSI.pbBuf, pThis->VBoxSCSI.cbBuf);
return SSMR3PutU32(pSSM, ~0);
}
-static DECLCALLBACK(int) lsilogicLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+/**
+ * @callback_method_impl{FNSSMDEVLOADDONE}
+ */
+static DECLCALLBACK(int) lsilogicR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
- lsilogicKick(pThis);
+ lsilogicR3Kick(pThis);
return VINF_SUCCESS;
}
-static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) lsilogicR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
- PLSILOGICSCSI pLsiLogic = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
int rc;
if ( uVersion != LSILOGIC_SAVED_STATE_VERSION
+ && uVersion != LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM
+ && uVersion != LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL
&& uVersion != LSILOGIC_SAVED_STATE_VERSION_PRE_SAS
&& uVersion != LSILOGIC_SAVED_STATE_VERSION_VBOX_30)
return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
@@ -4296,96 +4546,111 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
rc = SSMR3GetU32(pSSM, &cPorts);
AssertRCReturn(rc, rc);
- if (enmCtrlType != pLsiLogic->enmCtrlType)
+ if (enmCtrlType != pThis->enmCtrlType)
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Controller type): config=%d state=%d"),
- pLsiLogic->enmCtrlType, enmCtrlType);
- if (cDeviceStates != pLsiLogic->cDeviceStates)
+ pThis->enmCtrlType, enmCtrlType);
+ if (cDeviceStates != pThis->cDeviceStates)
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Device states): config=%u state=%u"),
- pLsiLogic->cDeviceStates, cDeviceStates);
- if (cPorts != pLsiLogic->cPorts)
+ pThis->cDeviceStates, cDeviceStates);
+ if (cPorts != pThis->cPorts)
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target config mismatch (Ports): config=%u state=%u"),
- pLsiLogic->cPorts, cPorts);
+ pThis->cPorts, cPorts);
}
if (uVersion > LSILOGIC_SAVED_STATE_VERSION_VBOX_30)
{
- for (unsigned i = 0; i < pLsiLogic->cDeviceStates; i++)
+ for (unsigned i = 0; i < pThis->cDeviceStates; i++)
{
bool fPresent;
rc = SSMR3GetBool(pSSM, &fPresent);
AssertRCReturn(rc, rc);
- if (fPresent != (pLsiLogic->paDeviceStates[i].pDrvBase != NULL))
+ if (fPresent != (pThis->paDeviceStates[i].pDrvBase != NULL))
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Target %u config mismatch: config=%RTbool state=%RTbool"),
- i, pLsiLogic->paDeviceStates[i].pDrvBase != NULL, fPresent);
+ i, pThis->paDeviceStates[i].pDrvBase != NULL, fPresent);
}
}
if (uPass != SSM_PASS_FINAL)
return VINF_SUCCESS;
/* Every device first. */
- for (unsigned i = 0; i < pLsiLogic->cDeviceStates; i++)
+ for (unsigned i = 0; i < pThis->cDeviceStates; i++)
{
- PLSILOGICDEVICE pDevice = &pLsiLogic->paDeviceStates[i];
+ PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[i];
AssertMsg(!pDevice->cOutstandingRequests,
("There are still outstanding requests on this device\n"));
SSMR3GetU32(pSSM, (uint32_t *)&pDevice->cOutstandingRequests);
}
/* Now the main device state. */
- SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->enmState);
- SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->enmWhoInit);
- SSMR3GetBool (pSSM, &pLsiLogic->fDoorbellInProgress);
- SSMR3GetBool (pSSM, &pLsiLogic->fDiagnosticEnabled);
- SSMR3GetBool (pSSM, &pLsiLogic->fNotificationSend);
- SSMR3GetBool (pSSM, &pLsiLogic->fEventNotificationEnabled);
- SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uInterruptMask);
- SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uInterruptStatus);
- for (unsigned i = 0; i < RT_ELEMENTS(pLsiLogic->aMessage); i++)
- SSMR3GetU32 (pSSM, &pLsiLogic->aMessage[i]);
- SSMR3GetU32 (pSSM, &pLsiLogic->iMessage);
- SSMR3GetU32 (pSSM, &pLsiLogic->cMessage);
- SSMR3GetMem (pSSM, &pLsiLogic->ReplyBuffer, sizeof(pLsiLogic->ReplyBuffer));
- SSMR3GetU32 (pSSM, &pLsiLogic->uNextReplyEntryRead);
- SSMR3GetU32 (pSSM, &pLsiLogic->cReplySize);
- SSMR3GetU16 (pSSM, &pLsiLogic->u16IOCFaultCode);
- SSMR3GetU32 (pSSM, &pLsiLogic->u32HostMFAHighAddr);
- SSMR3GetU32 (pSSM, &pLsiLogic->u32SenseBufferHighAddr);
- SSMR3GetU8 (pSSM, &pLsiLogic->cMaxDevices);
- SSMR3GetU8 (pSSM, &pLsiLogic->cMaxBuses);
- SSMR3GetU16 (pSSM, &pLsiLogic->cbReplyFrame);
- SSMR3GetU32 (pSSM, &pLsiLogic->iDiagnosticAccess);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->enmState);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->enmWhoInit);
+ if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_BOOL_DOORBELL)
+ {
+ bool fDoorbellInProgress = false;
+
+ /*
+ * The doorbell status flag distinguishes only between
+ * doorbell not in use or a Function handshake is currently in progress.
+ */
+ SSMR3GetBool (pSSM, &fDoorbellInProgress);
+ if (fDoorbellInProgress)
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_FN_HANDSHAKE;
+ else
+ pThis->enmDoorbellState = LSILOGICDOORBELLSTATE_NOT_IN_USE;
+ }
+ else
+ SSMR3GetU32(pSSM, (uint32_t *)&pThis->enmDoorbellState);
+ SSMR3GetBool (pSSM, &pThis->fDiagnosticEnabled);
+ SSMR3GetBool (pSSM, &pThis->fNotificationSend);
+ SSMR3GetBool (pSSM, &pThis->fEventNotificationEnabled);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uInterruptMask);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uInterruptStatus);
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->aMessage); i++)
+ SSMR3GetU32 (pSSM, &pThis->aMessage[i]);
+ SSMR3GetU32 (pSSM, &pThis->iMessage);
+ SSMR3GetU32 (pSSM, &pThis->cMessage);
+ SSMR3GetMem (pSSM, &pThis->ReplyBuffer, sizeof(pThis->ReplyBuffer));
+ SSMR3GetU32 (pSSM, &pThis->uNextReplyEntryRead);
+ SSMR3GetU32 (pSSM, &pThis->cReplySize);
+ SSMR3GetU16 (pSSM, &pThis->u16IOCFaultCode);
+ SSMR3GetU32 (pSSM, &pThis->u32HostMFAHighAddr);
+ SSMR3GetU32 (pSSM, &pThis->u32SenseBufferHighAddr);
+ SSMR3GetU8 (pSSM, &pThis->cMaxDevices);
+ SSMR3GetU8 (pSSM, &pThis->cMaxBuses);
+ SSMR3GetU16 (pSSM, &pThis->cbReplyFrame);
+ SSMR3GetU32 (pSSM, &pThis->iDiagnosticAccess);
uint32_t cReplyQueueEntries, cRequestQueueEntries;
SSMR3GetU32 (pSSM, &cReplyQueueEntries);
SSMR3GetU32 (pSSM, &cRequestQueueEntries);
- if ( cReplyQueueEntries != pLsiLogic->cReplyQueueEntries
- || cRequestQueueEntries != pLsiLogic->cRequestQueueEntries)
+ if ( cReplyQueueEntries != pThis->cReplyQueueEntries
+ || cRequestQueueEntries != pThis->cRequestQueueEntries)
{
LogFlow(("Reallocating queues cReplyQueueEntries=%u cRequestQueuEntries=%u\n",
cReplyQueueEntries, cRequestQueueEntries));
- lsilogicQueuesFree(pLsiLogic);
- pLsiLogic->cReplyQueueEntries = cReplyQueueEntries;
- pLsiLogic->cRequestQueueEntries = cRequestQueueEntries;
- rc = lsilogicQueuesAlloc(pLsiLogic);
+ lsilogicR3QueuesFree(pThis);
+ pThis->cReplyQueueEntries = cReplyQueueEntries;
+ pThis->cRequestQueueEntries = cRequestQueueEntries;
+ rc = lsilogicR3QueuesAlloc(pThis);
if (RT_FAILURE(rc))
return rc;
}
- SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uReplyFreeQueueNextEntryFreeWrite);
- SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uReplyFreeQueueNextAddressRead);
- SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uReplyPostQueueNextEntryFreeWrite);
- SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uReplyPostQueueNextAddressRead);
- SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uRequestQueueNextEntryFreeWrite);
- SSMR3GetU32 (pSSM, (uint32_t *)&pLsiLogic->uRequestQueueNextAddressRead);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyFreeQueueNextEntryFreeWrite);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyFreeQueueNextAddressRead);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyPostQueueNextEntryFreeWrite);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uReplyPostQueueNextAddressRead);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uRequestQueueNextEntryFreeWrite);
+ SSMR3GetU32 (pSSM, (uint32_t *)&pThis->uRequestQueueNextAddressRead);
- PMptConfigurationPagesSupported pPages = pLsiLogic->pConfigurationPages;
+ PMptConfigurationPagesSupported pPages = pThis->pConfigurationPages;
if (uVersion <= LSILOGIC_SAVED_STATE_VERSION_PRE_SAS)
{
PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
MptConfigurationPagesSupported_SSM_V2 ConfigPagesV2;
- if (pLsiLogic->enmCtrlType != LSILOGICCTRLTYPE_SCSI_SPI)
+ if (pThis->enmCtrlType != LSILOGICCTRLTYPE_SCSI_SPI)
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch: Expected SPI SCSI controller"));
SSMR3GetMem(pSSM, &ConfigPagesV2,
@@ -4422,14 +4687,52 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
else
{
/* Queue content */
- for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++)
- SSMR3GetU32(pSSM, (uint32_t *)&pLsiLogic->pReplyFreeQueueBaseR3[i]);
- for (unsigned i = 0; i < pLsiLogic->cReplyQueueEntries; i++)
- SSMR3GetU32(pSSM, (uint32_t *)&pLsiLogic->pReplyPostQueueBaseR3[i]);
- for (unsigned i = 0; i < pLsiLogic->cRequestQueueEntries; i++)
- SSMR3GetU32(pSSM, (uint32_t *)&pLsiLogic->pRequestQueueBaseR3[i]);
+ for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
+ SSMR3GetU32(pSSM, (uint32_t *)&pThis->pReplyFreeQueueBaseR3[i]);
+ for (unsigned i = 0; i < pThis->cReplyQueueEntries; i++)
+ SSMR3GetU32(pSSM, (uint32_t *)&pThis->pReplyPostQueueBaseR3[i]);
+ for (unsigned i = 0; i < pThis->cRequestQueueEntries; i++)
+ SSMR3GetU32(pSSM, (uint32_t *)&pThis->pRequestQueueBaseR3[i]);
+
+ SSMR3GetU16(pSSM, &pThis->u16NextHandle);
+
+ if (uVersion > LSILOGIC_SAVED_STATE_VERSION_PRE_DIAG_MEM)
+ {
+ uint32_t cMemRegions = 0;
+
+ /* Save diagnostic memory register and data regions. */
+ SSMR3GetU32 (pSSM, &pThis->u32DiagMemAddr);
+ SSMR3GetU32 (pSSM, &cMemRegions);
+
+ while (cMemRegions)
+ {
+ uint32_t u32AddrStart = 0;
+ uint32_t u32AddrEnd = 0;
+ uint32_t cRegion = 0;
+ PLSILOGICMEMREGN pRegion = NULL;
- SSMR3GetU16(pSSM, &pLsiLogic->u16NextHandle);
+ SSMR3GetU32(pSSM, &u32AddrStart);
+ SSMR3GetU32(pSSM, &u32AddrEnd);
+
+ cRegion = u32AddrEnd - u32AddrStart + 1;
+ pRegion = (PLSILOGICMEMREGN)RTMemAllocZ(RT_OFFSETOF(LSILOGICMEMREGN, au32Data[cRegion]));
+ if (pRegion)
+ {
+ pRegion->u32AddrStart = u32AddrStart;
+ pRegion->u32AddrEnd = u32AddrEnd;
+ SSMR3GetMem(pSSM, &pRegion->au32Data[0], cRegion * sizeof(uint32_t));
+ lsilogicR3MemRegionInsert(pThis, pRegion);
+ pThis->cbMemRegns += cRegion * sizeof(uint32_t);
+ }
+ else
+ {
+ /* Leave a log message but continue. */
+ LogRel(("LsiLogic: Out of memory while restoring the state, might not work as expected\n"));
+ SSMR3Skip(pSSM, cRegion * sizeof(uint32_t));
+ }
+ cMemRegions--;
+ }
+ }
/* Configuration pages */
SSMR3GetMem(pSSM, &pPages->ManufacturingPage0, sizeof(MptConfigurationPageManufacturing0));
@@ -4458,7 +4761,7 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
SSMR3GetMem(pSSM, &pPages->BIOSPage4, sizeof(MptConfigurationPageBIOS4));
/* Device dependent pages */
- if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
+ if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
{
PMptConfigurationPagesSpi pSpiPages = &pPages->u.SpiPages;
@@ -4474,7 +4777,7 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
SSMR3GetMem(pSSM, &pSpiPages->aBuses[0].aDevicePages[i].SCSISPIDevicePage3, sizeof(MptConfigurationPageSCSISPIDevice3));
}
}
- else if (pLsiLogic->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
+ else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
{
uint32_t cbPage0, cbPage1, cPHYs, cbManufacturingPage7;
PMptConfigurationPagesSas pSasPages = &pPages->u.SasPages;
@@ -4527,30 +4830,30 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
Assert(!pCurr);
}
else
- AssertMsgFailed(("Invalid controller type %d\n", pLsiLogic->enmCtrlType));
+ AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
}
/* Now the data for the BIOS interface. */
- SSMR3GetU8 (pSSM, &pLsiLogic->VBoxSCSI.regIdentify);
- SSMR3GetU8 (pSSM, &pLsiLogic->VBoxSCSI.uTargetDevice);
- SSMR3GetU8 (pSSM, &pLsiLogic->VBoxSCSI.uTxDir);
- SSMR3GetU8 (pSSM, &pLsiLogic->VBoxSCSI.cbCDB);
- SSMR3GetMem (pSSM, pLsiLogic->VBoxSCSI.aCDB, sizeof(pLsiLogic->VBoxSCSI.aCDB));
- SSMR3GetU8 (pSSM, &pLsiLogic->VBoxSCSI.iCDB);
- SSMR3GetU32 (pSSM, &pLsiLogic->VBoxSCSI.cbBuf);
- SSMR3GetU32 (pSSM, &pLsiLogic->VBoxSCSI.iBuf);
- SSMR3GetBool(pSSM, (bool *)&pLsiLogic->VBoxSCSI.fBusy);
- SSMR3GetU8 (pSSM, (uint8_t *)&pLsiLogic->VBoxSCSI.enmState);
- if (pLsiLogic->VBoxSCSI.cbBuf)
+ SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.regIdentify);
+ SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.uTargetDevice);
+ SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.uTxDir);
+ SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.cbCDB);
+ SSMR3GetMem (pSSM, pThis->VBoxSCSI.abCDB, sizeof(pThis->VBoxSCSI.abCDB));
+ SSMR3GetU8 (pSSM, &pThis->VBoxSCSI.iCDB);
+ SSMR3GetU32 (pSSM, &pThis->VBoxSCSI.cbBuf);
+ SSMR3GetU32 (pSSM, &pThis->VBoxSCSI.iBuf);
+ SSMR3GetBool(pSSM, (bool *)&pThis->VBoxSCSI.fBusy);
+ SSMR3GetU8 (pSSM, (uint8_t *)&pThis->VBoxSCSI.enmState);
+ if (pThis->VBoxSCSI.cbBuf)
{
- pLsiLogic->VBoxSCSI.pBuf = (uint8_t *)RTMemAllocZ(pLsiLogic->VBoxSCSI.cbBuf);
- if (!pLsiLogic->VBoxSCSI.pBuf)
+ pThis->VBoxSCSI.pbBuf = (uint8_t *)RTMemAllocZ(pThis->VBoxSCSI.cbBuf);
+ if (!pThis->VBoxSCSI.pbBuf)
{
LogRel(("LsiLogic: Out of memory during restore.\n"));
return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY,
N_("LsiLogic: Out of memory during restore\n"));
}
- SSMR3GetMem(pSSM, pLsiLogic->VBoxSCSI.pBuf, pLsiLogic->VBoxSCSI.cbBuf);
+ SSMR3GetMem(pSSM, pThis->VBoxSCSI.pbBuf, pThis->VBoxSCSI.cbBuf);
}
uint32_t u32;
@@ -4562,18 +4865,19 @@ static DECLCALLBACK(int) lsilogicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, u
return VINF_SUCCESS;
}
+
+/*
+ * The device level IBASE and LED interfaces.
+ */
+
/**
- * Gets the pointer to the status LED of a device - called from the SCSi driver.
+ * @interface_method_impl{PDMILEDPORTS,pfnQueryInterface, For a SCSI device.}
*
- * @returns VBox status code.
- * @param pInterface Pointer to the interface structure containing the called function pointer.
- * @param iLUN The unit which status LED we desire. Always 0 here as the driver
- * doesn't know about other LUN's.
- * @param ppLed Where to store the LED pointer.
+ * @remarks Called by the scsi driver, proxying the main calls.
*/
-static DECLCALLBACK(int) lsilogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+static DECLCALLBACK(int) lsilogicR3DeviceQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
- PLSILOGICDEVICE pDevice = PDMILEDPORTS_2_PLSILOGICDEVICE(pInterface);
+ PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, ILed);
if (iLUN == 0)
{
*ppLed = &pDevice->Led;
@@ -4587,9 +4891,9 @@ static DECLCALLBACK(int) lsilogicDeviceQueryStatusLed(PPDMILEDPORTS pInterface,
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
-static DECLCALLBACK(void *) lsilogicDeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+static DECLCALLBACK(void *) lsilogicR3DeviceQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
- PLSILOGICDEVICE pDevice = PDMIBASE_2_PLSILOGICDEVICE(pInterface);
+ PLSILOGICDEVICE pDevice = RT_FROM_MEMBER(pInterface, LSILOGICDEVICE, IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevice->IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSIPORT, &pDevice->ISCSIPort);
@@ -4597,6 +4901,11 @@ static DECLCALLBACK(void *) lsilogicDeviceQueryInterface(PPDMIBASE pInterface, c
return NULL;
}
+
+/*
+ * The controller level IBASE and LED interfaces.
+ */
+
/**
* Gets the pointer to the status LED of a unit.
*
@@ -4605,12 +4914,12 @@ static DECLCALLBACK(void *) lsilogicDeviceQueryInterface(PPDMIBASE pInterface, c
* @param iLUN The unit which status LED we desire.
* @param ppLed Where to store the LED pointer.
*/
-static DECLCALLBACK(int) lsilogicStatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+static DECLCALLBACK(int) lsilogicR3StatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
- PLSILOGICSCSI pLsiLogic = PDMILEDPORTS_2_PLSILOGICSCSI(pInterface);
- if (iLUN < pLsiLogic->cDeviceStates)
+ PLSILOGICSCSI pThis = RT_FROM_MEMBER(pInterface, LSILOGICSCSI, ILeds);
+ if (iLUN < pThis->cDeviceStates)
{
- *ppLed = &pLsiLogic->paDeviceStates[iLUN].Led;
+ *ppLed = &pThis->paDeviceStates[iLUN].Led;
Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
return VINF_SUCCESS;
}
@@ -4620,20 +4929,23 @@ static DECLCALLBACK(int) lsilogicStatusQueryStatusLed(PPDMILEDPORTS pInterface,
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
-static DECLCALLBACK(void *) lsilogicStatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+static DECLCALLBACK(void *) lsilogicR3StatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
- PLSILOGICSCSI pThis = PDMIBASE_2_PLSILOGICSCSI(pInterface);
+ PLSILOGICSCSI pThis = RT_FROM_MEMBER(pInterface, LSILOGICSCSI, IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
return NULL;
}
-/* -=-=-=-=- Helper -=-=-=-=- */
+
+/*
+ * The PDM device interface and some helpers.
+ */
/**
* Checks if all asynchronous I/O is finished.
*
- * Used by lsilogicReset, lsilogicSuspend and lsilogicPowerOff.
+ * Used by lsilogicR3Reset, lsilogicR3Suspend and lsilogicR3PowerOff.
*
* @returns true if quiesced, false if busy.
* @param pDevIns The device instance.
@@ -4656,10 +4968,8 @@ static bool lsilogicR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns)
}
/**
- * Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff..
- *
- * @returns true if we've quiesced, false if we're still working.
- * @param pDevIns The device instance.
+ * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
+ * Callback employed by lsilogicR3Suspend and lsilogicR3PowerOff.}
*/
static DECLCALLBACK(bool) lsilogicR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
{
@@ -4695,19 +5005,19 @@ static void lsilogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns)
* Guest execution is suspended at this point so there is no race between us and
* lsilogicRegisterWrite.
*/
- PLSILOGICTASKSTATE pTaskState = pThis->pTasksRedoHead;
+ PLSILOGICREQ pLsiReq = pThis->pTasksRedoHead;
pThis->pTasksRedoHead = NULL;
- while (pTaskState)
+ while (pLsiReq)
{
- PLSILOGICTASKSTATE pFree;
+ PLSILOGICREQ pFree;
- if (!pTaskState->fBIOS)
+ if (!pLsiReq->fBIOS)
{
/* Write only the lower 32bit part of the address. */
ASMAtomicWriteU32(&pThis->CTX_SUFF(pRequestQueueBase)[pThis->uRequestQueueNextEntryFreeWrite],
- pTaskState->GCPhysMessageFrameAddr & UINT32_C(0xffffffff));
+ pLsiReq->GCPhysMessageFrameAddr & UINT32_C(0xffffffff));
pThis->uRequestQueueNextEntryFreeWrite++;
pThis->uRequestQueueNextEntryFreeWrite %= pThis->cRequestQueueEntries;
@@ -4716,12 +5026,12 @@ static void lsilogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns)
}
else
{
- AssertMsg(!pTaskState->pRedoNext, ("Only one BIOS task can be active!\n"));
- vboxscsiSetRequestRedo(&pThis->VBoxSCSI, &pTaskState->PDMScsiRequest);
+ AssertMsg(!pLsiReq->pRedoNext, ("Only one BIOS task can be active!\n"));
+ vboxscsiSetRequestRedo(&pThis->VBoxSCSI, &pLsiReq->PDMScsiRequest);
}
- pFree = pTaskState;
- pTaskState = pTaskState->pRedoNext;
+ pFree = pLsiReq;
+ pLsiReq = pLsiReq->pRedoNext;
RTMemCacheFree(pThis->hTaskCache, pFree);
}
@@ -4731,41 +5041,33 @@ static void lsilogicR3SuspendOrPowerOff(PPDMDEVINS pDevIns)
}
/**
- * Suspend notification.
- *
- * @param pDevIns The device instance data.
+ * @interface_method_impl{PDMDEVREG,pfnSuspend}
*/
-static DECLCALLBACK(void) lsilogicSuspend(PPDMDEVINS pDevIns)
+static DECLCALLBACK(void) lsilogicR3Suspend(PPDMDEVINS pDevIns)
{
- Log(("lsilogicSuspend\n"));
+ Log(("lsilogicR3Suspend\n"));
lsilogicR3SuspendOrPowerOff(pDevIns);
}
/**
- * Resume notification.
- *
- * @param pDevIns The device instance data.
+ * @interface_method_impl{PDMDEVREG,pfnResume}
*/
-static DECLCALLBACK(void) lsilogicResume(PPDMDEVINS pDevIns)
+static DECLCALLBACK(void) lsilogicR3Resume(PPDMDEVINS pDevIns)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
- Log(("lsilogicResume\n"));
+ Log(("lsilogicR3Resume\n"));
- lsilogicKick(pThis);
+ lsilogicR3Kick(pThis);
}
/**
- * Detach notification.
+ * @interface_method_impl{PDMDEVREG,pfnDetach}
*
* One harddisk at one port has been unplugged.
* The VM is suspended at this point.
- *
- * @param pDevIns The device instance.
- * @param iLUN The logical unit which is being detached.
- * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
-static DECLCALLBACK(void) lsilogicDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+static DECLCALLBACK(void) lsilogicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[iLUN];
@@ -4786,16 +5088,9 @@ static DECLCALLBACK(void) lsilogicDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint
}
/**
- * Attach command.
- *
- * This is called when we change block driver.
- *
- * @returns VBox status code.
- * @param pDevIns The device instance.
- * @param iLUN The logical unit which is being detached.
- * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ * @interface_method_impl{PDMDEVREG,pfnAttach}
*/
-static DECLCALLBACK(int) lsilogicAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+static DECLCALLBACK(int) lsilogicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
PLSILOGICDEVICE pDevice = &pThis->paDeviceStates[iLUN];
@@ -4842,20 +5137,18 @@ static DECLCALLBACK(int) lsilogicAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint
*/
static void lsilogicR3ResetCommon(PPDMDEVINS pDevIns)
{
- PLSILOGICSCSI pLsiLogic = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
+ PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
int rc;
- rc = lsilogicHardReset(pLsiLogic);
+ rc = lsilogicR3HardReset(pThis);
AssertRC(rc);
- vboxscsiInitialize(&pLsiLogic->VBoxSCSI);
+ vboxscsiInitialize(&pThis->VBoxSCSI);
}
/**
- * Callback employed by lsilogicR3Reset.
- *
- * @returns true if we've quiesced, false if we're still working.
- * @param pDevIns The device instance.
+ * @callback_method_impl{FNPDMDEVASYNCNOTIFY,
+ * Callback employed by lsilogicR3Reset.}
*/
static DECLCALLBACK(bool) lsilogicR3IsAsyncResetDone(PPDMDEVINS pDevIns)
{
@@ -4870,9 +5163,9 @@ static DECLCALLBACK(bool) lsilogicR3IsAsyncResetDone(PPDMDEVINS pDevIns)
}
/**
- * @copydoc FNPDMDEVRESET
+ * @interface_method_impl{PDMDEVREG,pfnReset}
*/
-static DECLCALLBACK(void) lsilogicReset(PPDMDEVINS pDevIns)
+static DECLCALLBACK(void) lsilogicR3Reset(PPDMDEVINS pDevIns)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
@@ -4887,9 +5180,9 @@ static DECLCALLBACK(void) lsilogicReset(PPDMDEVINS pDevIns)
}
/**
- * @copydoc FNPDMDEVRELOCATE
+ * @interface_method_impl{PDMDEVREG,pfnRelocate}
*/
-static DECLCALLBACK(void) lsilogicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+static DECLCALLBACK(void) lsilogicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
@@ -4903,9 +5196,18 @@ static DECLCALLBACK(void) lsilogicRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDel
}
/**
- * @copydoc FNPDMDEVDESTRUCT
+ * @interface_method_impl{PDMDEVREG,pfnPowerOff}
*/
-static DECLCALLBACK(int) lsilogicDestruct(PPDMDEVINS pDevIns)
+static DECLCALLBACK(void) lsilogicR3PowerOff(PPDMDEVINS pDevIns)
+{
+ Log(("lsilogicR3PowerOff\n"));
+ lsilogicR3SuspendOrPowerOff(pDevIns);
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) lsilogicR3Destruct(PPDMDEVINS pDevIns)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
@@ -4913,43 +5215,45 @@ static DECLCALLBACK(int) lsilogicDestruct(PPDMDEVINS pDevIns)
PDMR3CritSectDelete(&pThis->ReplyFreeQueueCritSect);
PDMR3CritSectDelete(&pThis->ReplyPostQueueCritSect);
- if (pThis->paDeviceStates)
- RTMemFree(pThis->paDeviceStates);
+ RTMemFree(pThis->paDeviceStates);
+ pThis->paDeviceStates = NULL;
/* Destroy task cache. */
- int rc = VINF_SUCCESS;
if (pThis->hTaskCache != NIL_RTMEMCACHE)
- rc = RTMemCacheDestroy(pThis->hTaskCache);
+ {
+ int rc = RTMemCacheDestroy(pThis->hTaskCache); AssertRC(rc);
+ pThis->hTaskCache = NIL_RTMEMCACHE;
+ }
- lsilogicConfigurationPagesFree(pThis);
+ if (pThis->hEvtProcess != NIL_SUPSEMEVENT)
+ {
+ SUPSemEventClose(pThis->pSupDrvSession, pThis->hEvtProcess);
+ pThis->hEvtProcess = NIL_SUPSEMEVENT;
+ }
- return rc;
-}
+ lsilogicR3ConfigurationPagesFree(pThis);
+ lsilogicR3MemRegionsFree(pThis);
-/**
- * Poweroff notification.
- *
- * @param pDevIns Pointer to the device instance
- */
-static DECLCALLBACK(void) lsilogicPowerOff(PPDMDEVINS pDevIns)
-{
- Log(("lsilogicPowerOff\n"));
- lsilogicR3SuspendOrPowerOff(pDevIns);
+ return VINF_SUCCESS;
}
/**
- * @copydoc FNPDMDEVCONSTRUCT
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
-static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+static DECLCALLBACK(int) lsilogicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
PLSILOGICSCSI pThis = PDMINS_2_DATA(pDevIns, PLSILOGICSCSI);
- int rc = VINF_SUCCESS;
- char *pszCtrlType = NULL;
- char szDevTag[20];
- bool fBootable = true;
+ int rc = VINF_SUCCESS;
PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
/*
+ * Initialize enought of the state to make the destructure not trip up.
+ */
+ pThis->hTaskCache = NIL_RTMEMCACHE;
+ pThis->hEvtProcess = NIL_SUPSEMEVENT;
+ RTListInit(&pThis->ListMemRegns);
+
+ /*
* Validate and read configuration.
*/
rc = CFGMR3AreValuesValid(pCfg, "GCEnabled\0"
@@ -4990,6 +5294,7 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
N_("LsiLogic configuration error: failed to read RequestQueue as integer"));
Log(("%s: RequestQueueDepth=%u\n", __FUNCTION__, pThis->cRequestQueueEntries));
+ char *pszCtrlType;
rc = CFGMR3QueryStringAllocDef(pCfg, "ControllerType",
&pszCtrlType, LSILOGICSCSI_PCI_SPI_CTRLNAME);
if (RT_FAILURE(rc))
@@ -4997,9 +5302,10 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
N_("LsiLogic configuration error: failed to read ControllerType as string"));
Log(("%s: ControllerType=%s\n", __FUNCTION__, pszCtrlType));
- rc = lsilogicGetCtrlTypeFromString(pThis, pszCtrlType);
+ rc = lsilogicR3GetCtrlTypeFromString(pThis, pszCtrlType);
MMR3HeapFree(pszCtrlType);
+ char szDevTag[20];
RTStrPrintf(szDevTag, sizeof(szDevTag), "LSILOGIC%s-%u",
pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI ? "SPI" : "SAS",
iInstance);
@@ -5024,6 +5330,7 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("LsiLogic configuration error: failed to read NumPorts as integer"));
+ bool fBootable;
rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &fBootable, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
@@ -5053,56 +5360,72 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
PCIDevSetClassBase (&pThis->PciDev, 0x01); /* Mass storage */
PCIDevSetInterruptPin(&pThis->PciDev, 0x01); /* Interrupt pin A */
-#ifdef VBOX_WITH_MSI_DEVICES
+# ifdef VBOX_WITH_MSI_DEVICES
PCIDevSetStatus(&pThis->PciDev, VBOX_PCI_STATUS_CAP_LIST);
PCIDevSetCapabilityList(&pThis->PciDev, 0x80);
-#endif
+# endif
pThis->pDevInsR3 = pDevIns;
pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
- pThis->IBase.pfnQueryInterface = lsilogicStatusQueryInterface;
- pThis->ILeds.pfnQueryStatusLed = lsilogicStatusQueryStatusLed;
+ pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns);
+ pThis->IBase.pfnQueryInterface = lsilogicR3StatusQueryInterface;
+ pThis->ILeds.pfnQueryStatusLed = lsilogicR3StatusQueryStatusLed;
+
+ /*
+ * Create critical sections protecting the reply post and free queues.
+ * Note! We do our own syncronization, so NOP the default crit sect for the device.
+ */
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyFreeQueueCritSect, RT_SRC_POS, "%sRFQ", szDevTag);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply free queue"));
+
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyPostQueueCritSect, RT_SRC_POS, "%sRPQ", szDevTag);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic: cannot create critical section for reply post queue"));
/*
* Register the PCI device, it's I/O regions.
*/
- rc = PDMDevHlpPCIRegister (pDevIns, &pThis->PciDev);
+ rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
if (RT_FAILURE(rc))
return rc;
-#ifdef VBOX_WITH_MSI_DEVICES
- PDMMSIREG aMsiReg;
- RT_ZERO(aMsiReg);
+# ifdef VBOX_WITH_MSI_DEVICES
+ PDMMSIREG MsiReg;
+ RT_ZERO(MsiReg);
/* use this code for MSI-X support */
-#if 0
- aMsiReg.cMsixVectors = 1;
- aMsiReg.iMsixCapOffset = 0x80;
- aMsiReg.iMsixNextOffset = 0x0;
- aMsiReg.iMsixBar = 3;
-#else
- aMsiReg.cMsiVectors = 1;
- aMsiReg.iMsiCapOffset = 0x80;
- aMsiReg.iMsiNextOffset = 0x0;
-#endif
- rc = PDMDevHlpPCIRegisterMsi(pDevIns, &aMsiReg);
+# if 0
+ MsiReg.cMsixVectors = 1;
+ MsiReg.iMsixCapOffset = 0x80;
+ MsiReg.iMsixNextOffset = 0x00;
+ MsiReg.iMsixBar = 3;
+# else
+ MsiReg.cMsiVectors = 1;
+ MsiReg.iMsiCapOffset = 0x80;
+ MsiReg.iMsiNextOffset = 0x00;
+# endif
+ rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
if (RT_FAILURE (rc))
{
LogRel(("Chipset cannot do MSI: %Rrc\n", rc));
/* That's OK, we can work without MSI */
PCIDevSetCapabilityList(&pThis->PciDev, 0x0);
}
-#endif
+# endif
- rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, LSILOGIC_PCI_SPACE_IO_SIZE, PCI_ADDRESS_SPACE_IO, lsilogicMap);
+ rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, LSILOGIC_PCI_SPACE_IO_SIZE, PCI_ADDRESS_SPACE_IO, lsilogicR3Map);
if (RT_FAILURE(rc))
return rc;
- rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicMap);
+ rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicR3Map);
if (RT_FAILURE(rc))
return rc;
- rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicMap);
+ rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2, LSILOGIC_PCI_SPACE_MEM_SIZE, PCI_ADDRESS_SPACE_MEM, lsilogicR3Map);
if (RT_FAILURE(rc))
return rc;
@@ -5110,7 +5433,7 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
char szTaggedText[64];
RTStrPrintf(szTaggedText, sizeof(szTaggedText), "%s-Task", szDevTag);
rc = PDMDevHlpQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 2, 0,
- lsilogicNotifyQueueConsumer, true,
+ lsilogicR3NotifyQueueConsumer, true,
szTaggedText,
&pThis->pNotificationQueueR3);
if (RT_FAILURE(rc))
@@ -5127,31 +5450,17 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
/*
* Allocate memory for the queues.
*/
- rc = lsilogicQueuesAlloc(pThis);
+ rc = lsilogicR3QueuesAlloc(pThis);
if (RT_FAILURE(rc))
return rc;
/*
- * Create critical sections protecting the reply post and free queues.
- */
- rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyFreeQueueCritSect, RT_SRC_POS, "%sRFQ", szDevTag);
- if (RT_FAILURE(rc))
- return PDMDEV_SET_ERROR(pDevIns, rc,
- N_("LsiLogic: cannot create critical section for reply free queue"));
-
- rc = PDMDevHlpCritSectInit(pDevIns, &pThis->ReplyPostQueueCritSect, RT_SRC_POS, "%sRPQ", szDevTag);
- if (RT_FAILURE(rc))
- return PDMDEV_SET_ERROR(pDevIns, rc,
- N_("LsiLogic: cannot create critical section for reply post queue"));
-
- /*
* Allocate task cache.
*/
- rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(LSILOGICTASKSTATE), 0, UINT32_MAX,
- lsilogicTaskStateCtor, lsilogicTaskStateDtor, NULL, 0);
+ rc = RTMemCacheCreate(&pThis->hTaskCache, sizeof(LSILOGICREQ), 0, UINT32_MAX,
+ NULL, NULL, NULL, 0);
if (RT_FAILURE(rc))
- return PDMDEV_SET_ERROR(pDevIns, rc,
- N_("Cannot create task cache"));
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Cannot create task cache"));
if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
pThis->cDeviceStates = pThis->cPorts * LSILOGICSCSI_PCI_SPI_DEVICES_PER_BUS_MAX;
@@ -5161,12 +5470,25 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
AssertMsgFailed(("Invalid controller type: %d\n", pThis->enmCtrlType));
/*
+ * Create event semaphore and worker thread.
+ */
+ rc = PDMDevHlpThreadCreate(pDevIns, &pThis->pThreadWrk, pThis, lsilogicR3Worker,
+ lsilogicR3WorkerWakeUp, 0, RTTHREADTYPE_IO, szDevTag);
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("LsiLogic: Failed to create worker thread %s"), szDevTag);
+
+ rc = SUPSemEventCreate(pThis->pSupDrvSession, &pThis->hEvtProcess);
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("LsiLogic: Failed to create SUP event semaphore"));
+
+ /*
* Allocate device states.
*/
pThis->paDeviceStates = (PLSILOGICDEVICE)RTMemAllocZ(sizeof(LSILOGICDEVICE) * pThis->cDeviceStates);
if (!pThis->paDeviceStates)
- return PDMDEV_SET_ERROR(pDevIns, rc,
- N_("Failed to allocate memory for device states"));
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate memory for device states"));
for (unsigned i = 0; i < pThis->cDeviceStates; i++)
{
@@ -5177,12 +5499,12 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
pDevice->iLUN = i;
pDevice->pLsiLogicR3 = pThis;
pDevice->Led.u32Magic = PDMLED_MAGIC;
- pDevice->IBase.pfnQueryInterface = lsilogicDeviceQueryInterface;
- pDevice->ISCSIPort.pfnSCSIRequestCompleted = lsilogicDeviceSCSIRequestCompleted;
- pDevice->ISCSIPort.pfnQueryDeviceLocation = lsilogicQueryDeviceLocation;
- pDevice->ILed.pfnQueryStatusLed = lsilogicDeviceQueryStatusLed;
+ pDevice->IBase.pfnQueryInterface = lsilogicR3DeviceQueryInterface;
+ pDevice->ISCSIPort.pfnSCSIRequestCompleted = lsilogicR3DeviceSCSIRequestCompleted;
+ pDevice->ISCSIPort.pfnQueryDeviceLocation = lsilogicR3QueryDeviceLocation;
+ pDevice->ILed.pfnQueryStatusLed = lsilogicR3DeviceQueryStatusLed;
- RTStrPrintf(szName, sizeof(szName), "Device%d", i);
+ RTStrPrintf(szName, sizeof(szName), "Device%u", i);
/* Attach SCSI driver. */
rc = PDMDevHlpDriverAttach(pDevIns, pDevice->iLUN, &pDevice->IBase, &pDevice->pDrvBase, szName);
@@ -5230,13 +5552,13 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
{
if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI)
rc = PDMDevHlpIOPortRegister(pDevIns, LSILOGIC_BIOS_IO_PORT, 4, NULL,
- lsilogicIsaIOPortWrite, lsilogicIsaIOPortRead,
- lsilogicIsaIOPortWriteStr, lsilogicIsaIOPortReadStr,
+ lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead,
+ lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr,
"LsiLogic BIOS");
else if (pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SAS)
rc = PDMDevHlpIOPortRegister(pDevIns, LSILOGIC_SAS_BIOS_IO_PORT, 4, NULL,
- lsilogicIsaIOPortWrite, lsilogicIsaIOPortRead,
- lsilogicIsaIOPortWriteStr, lsilogicIsaIOPortReadStr,
+ lsilogicR3IsaIOPortWrite, lsilogicR3IsaIOPortRead,
+ lsilogicR3IsaIOPortWriteStr, lsilogicR3IsaIOPortReadStr,
"LsiLogic SAS BIOS");
else
AssertMsgFailed(("Invalid controller type %d\n", pThis->enmCtrlType));
@@ -5247,9 +5569,9 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
/* Register save state handlers. */
rc = PDMDevHlpSSMRegisterEx(pDevIns, LSILOGIC_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
- NULL, lsilogicLiveExec, NULL,
- NULL, lsilogicSaveExec, NULL,
- NULL, lsilogicLoadExec, lsilogicLoadDone);
+ NULL, lsilogicR3LiveExec, NULL,
+ NULL, lsilogicR3SaveExec, NULL,
+ NULL, lsilogicR3LoadExec, lsilogicR3LoadDone);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("LsiLogic cannot register save state handlers"));
@@ -5259,14 +5581,14 @@ static DECLCALLBACK(int) lsilogicConstruct(PPDMDEVINS pDevIns, int iInstance, PC
* Register the info item.
*/
char szTmp[128];
- RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance);
+ RTStrPrintf(szTmp, sizeof(szTmp), "%s%u", pDevIns->pReg->szName, pDevIns->iInstance);
PDMDevHlpDBGFInfoRegister(pDevIns, szTmp,
pThis->enmCtrlType == LSILOGICCTRLTYPE_SCSI_SPI
? "LsiLogic SPI info."
- : "LsiLogic SAS info.", lsilogicInfo);
+ : "LsiLogic SAS info.", lsilogicR3Info);
/* Perform hard reset. */
- rc = lsilogicHardReset(pThis);
+ rc = lsilogicR3HardReset(pThis);
AssertRC(rc);
return rc;
@@ -5297,31 +5619,31 @@ const PDMDEVREG g_DeviceLsiLogicSCSI =
/* cbInstance */
sizeof(LSILOGICSCSI),
/* pfnConstruct */
- lsilogicConstruct,
+ lsilogicR3Construct,
/* pfnDestruct */
- lsilogicDestruct,
+ lsilogicR3Destruct,
/* pfnRelocate */
- lsilogicRelocate,
- /* pfnIOCtl */
+ lsilogicR3Relocate,
+ /* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
- lsilogicReset,
+ lsilogicR3Reset,
/* pfnSuspend */
- lsilogicSuspend,
+ lsilogicR3Suspend,
/* pfnResume */
- lsilogicResume,
+ lsilogicR3Resume,
/* pfnAttach */
- lsilogicAttach,
+ lsilogicR3Attach,
/* pfnDetach */
- lsilogicDetach,
+ lsilogicR3Detach,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
- lsilogicPowerOff,
+ lsilogicR3PowerOff,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
@@ -5354,31 +5676,31 @@ const PDMDEVREG g_DeviceLsiLogicSAS =
/* cbInstance */
sizeof(LSILOGICSCSI),
/* pfnConstruct */
- lsilogicConstruct,
+ lsilogicR3Construct,
/* pfnDestruct */
- lsilogicDestruct,
+ lsilogicR3Destruct,
/* pfnRelocate */
- lsilogicRelocate,
- /* pfnIOCtl */
+ lsilogicR3Relocate,
+ /* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
- lsilogicReset,
+ lsilogicR3Reset,
/* pfnSuspend */
- lsilogicSuspend,
+ lsilogicR3Suspend,
/* pfnResume */
- lsilogicResume,
+ lsilogicR3Resume,
/* pfnAttach */
- lsilogicAttach,
+ lsilogicR3Attach,
/* pfnDetach */
- lsilogicDetach,
+ lsilogicR3Detach,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
- lsilogicPowerOff,
+ lsilogicR3PowerOff,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
diff --git a/src/VBox/Devices/Storage/DevLsiLogicSCSI.h b/src/VBox/Devices/Storage/DevLsiLogicSCSI.h
index d04a4e83..207d8e85 100644
--- a/src/VBox/Devices/Storage/DevLsiLogicSCSI.h
+++ b/src/VBox/Devices/Storage/DevLsiLogicSCSI.h
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2009 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -24,8 +24,8 @@
* not be in the ISA range (below 400h) to avoid conflicts with ISA device
* probing. Addresses in the 300h-340h range should be especially avoided.
*/
-#define LSILOGIC_BIOS_IO_PORT 0x340
-#define LSILOGIC_SAS_BIOS_IO_PORT 0x350
+#define LSILOGIC_BIOS_IO_PORT 0x434
+#define LSILOGIC_SAS_BIOS_IO_PORT 0x438
#define LSILOGICSCSI_REQUEST_QUEUE_DEPTH_DEFAULT 256
#define LSILOGICSCSI_REPLY_QUEUE_DEPTH_DEFAULT 256
@@ -1150,7 +1150,71 @@ typedef union MptReplyUnion
MptFWDownloadReply FWDownload;
MptFWUploadReply FWUpload;
} MptReplyUnion, *PMptReplyUnion;
+AssertCompileSize(MptReplyUnion, 60);
+/**
+ * Firmware image header.
+ */
+#pragma pack(1)
+typedef struct FwImageHdr
+{
+ /** ARM branch instruction. */
+ uint32_t u32ArmBrInsn;
+ /** Signature part 1. */
+ uint32_t u32Signature1;
+ /** Signature part 2. */
+ uint32_t u32Signature2;
+ /** Signature part 3. */
+ uint32_t u32Signature3;
+ /** Another ARM branch instruction. */
+ uint32_t u32ArmBrInsn2;
+ /** Yet another ARM branch instruction. */
+ uint32_t u32ArmBrInsn3;
+ /** Reserved. */
+ uint32_t u32Reserved;
+ /** Checksum of the image. */
+ uint32_t u32Checksum;
+ /** Vendor ID. */
+ uint16_t u16VendorId;
+ /** Product ID. */
+ uint16_t u16ProductId;
+ /** Firmware version. */
+ uint32_t u32FwVersion;
+ /** Firmware sequencer Code version. */
+ uint32_t u32SeqCodeVersion;
+ /** Image size in bytes including the header. */
+ uint32_t u32ImageSize;
+ /** Offset of the first extended image header. */
+ uint32_t u32NextImageHeaderOffset;
+ /** Start address of the image in IOC memory. */
+ uint32_t u32LoadStartAddress;
+ /** Absolute start address of the Iop ARM. */
+ uint32_t u32IopResetVectorValue;
+ /** Address of the IopResetVector register. */
+ uint32_t u32IopResetVectorRegAddr;
+ /** Marker value for what utility. */
+ uint32_t u32VersionNameWhat;
+ /** ASCII string of version. */
+ uint8_t aszVersionName[256];
+ /** Marker value for what utility. */
+ uint32_t u32VendorNameWhat;
+ /** ASCII string of vendor name. */
+ uint8_t aszVendorName[256];
+} FwImageHdr, *PFwImageHdr;
+#pragma pack()
+AssertCompileSize(FwImageHdr, 584);
+
+/** First part of the signature. */
+#define LSILOGIC_FWIMGHDR_SIGNATURE1 UINT32_C(0x5aeaa55a)
+/** Second part of the signature. */
+#define LSILOGIC_FWIMGHDR_SIGNATURE2 UINT32_C(0xa55aeaa5)
+/** Third part of the signature. */
+#define LSILOGIC_FWIMGHDR_SIGNATURE3 UINT32_C(0x5aa55aea)
+/** Load address of the firmware image to watch for,
+ * seen used by Solaris 9. When this value is written to the
+ * diagnostic address register we know a firmware image is downloaded.
+ */
+#define LSILOGIC_FWIMGHDR_LOAD_ADDRESS UINT32_C(0x21ff5e00)
/**
* Configuration Page attributes.
@@ -3465,6 +3529,32 @@ typedef enum LSILOGICWHOINIT
/**
+ * Doorbell state.
+ */
+typedef enum LSILOGICDOORBELLSTATE
+{
+ /** Invalid value. */
+ LSILOGICDOORBELLSTATE_INVALID = 0,
+ /** Doorbell not in use. */
+ LSILOGICDOORBELLSTATE_NOT_IN_USE,
+ /** Reply frame removal, transfer number of entries, low 16bits. */
+ LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_LOW,
+ /** Reply frame removal, transfer number of entries, high 16bits. */
+ LSILOGICDOORBELLSTATE_RFR_FRAME_COUNT_HIGH,
+ /** Reply frame removal, remove next free frame, low part. */
+ LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_LOW,
+ /** Reply frame removal, remove next free frame, high part. */
+ LSILOGICDOORBELLSTATE_RFR_NEXT_FRAME_HIGH,
+ /** Function handshake. */
+ LSILOGICDOORBELLSTATE_FN_HANDSHAKE,
+ /** 32bit hack. */
+ LSILOGICDOORBELLSTATE_32BIT_HACK = 0x7fffffff
+} LSILOGICDOORBELLSTATE;
+/** Pointer to a doorbell state. */
+typedef LSILOGICDOORBELLSTATE *PLSILOGICDOORBELLSTATE;
+
+
+/**
* IOC status codes.
*/
#define LSILOGIC_IOCSTATUS_SUCCESS 0x0000
@@ -3490,7 +3580,7 @@ typedef enum LSILOGICWHOINIT
*/
#define LSILOGIC_REG_DOORBELL 0x00
# define LSILOGIC_REG_DOORBELL_SET_STATE(enmState) (((enmState) & 0x0f) << 28)
-# define LSILOGIC_REG_DOORBELL_SET_USED(fUsed) (((fUsed) ? 1 : 0) << 27)
+# define LSILOGIC_REG_DOORBELL_SET_USED(enmDoorbell) (((enmDoorbell != LSILOGICDOORBELLSTATE_NOT_IN_USE) ? 1 : 0) << 27)
# define LSILOGIC_REG_DOORBELL_SET_WHOINIT(enmWhoInit) (((enmWhoInit) & 0x07) << 24)
# define LSILOGIC_REG_DOORBELL_SET_FAULT_CODE(u16Code) (u16Code)
# define LSILOGIC_REG_DOORBELL_GET_FUNCTION(x) (((x) & 0xff000000) >> 24)
diff --git a/src/VBox/Devices/Storage/DrvBlock.cpp b/src/VBox/Devices/Storage/DrvBlock.cpp
index 8353d805..30dfb9ee 100644
--- a/src/VBox/Devices/Storage/DrvBlock.cpp
+++ b/src/VBox/Devices/Storage/DrvBlock.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -264,6 +264,23 @@ static DECLCALLBACK(uint64_t) drvblockGetSize(PPDMIBLOCK pInterface)
}
+/** @copydoc PDMIBLOCK::pfnGetSize */
+static DECLCALLBACK(uint32_t) drvblockGetSectorSize(PPDMIBLOCK pInterface)
+{
+ PDRVBLOCK pThis = PDMIBLOCK_2_DRVBLOCK(pInterface);
+
+ /*
+ * Check the state.
+ */
+ if (!pThis->pDrvMedia)
+ return 0;
+
+ uint32_t cb = pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia);
+ LogFlowFunc(("returns %u\n", cb));
+ return cb;
+}
+
+
/** @copydoc PDMIBLOCK::pfnGetType */
static DECLCALLBACK(PDMBLOCKTYPE) drvblockGetType(PPDMIBLOCK pInterface)
{
@@ -774,6 +791,7 @@ static DECLCALLBACK(void) drvblockDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
NOREF(fFlags);
}
+
/**
* Reset notification.
*
@@ -787,6 +805,34 @@ static DECLCALLBACK(void) drvblockReset(PPDMDRVINS pDrvIns)
pThis->fLocked = false;
}
+
+/**
+ * Translates a PDMBLOCKTYPE value into a string.
+ *
+ * @returns Read only string.
+ * @param enmType The type value.
+ */
+static const char *drvblockGetTypeName(PDMBLOCKTYPE enmType)
+{
+ switch (enmType)
+ {
+ case PDMBLOCKTYPE_ERROR: return "ERROR";
+ case PDMBLOCKTYPE_FLOPPY_360: return "FLOPPY_360";
+ case PDMBLOCKTYPE_FLOPPY_720: return "FLOPPY_720";
+ case PDMBLOCKTYPE_FLOPPY_1_20: return "FLOPPY_1_20";
+ case PDMBLOCKTYPE_FLOPPY_1_44: return "FLOPPY_1_44";
+ case PDMBLOCKTYPE_FLOPPY_2_88: return "FLOPPY_2_88";
+ case PDMBLOCKTYPE_FLOPPY_FAKE_15_6: return "FLOPPY_FAKE_15_6";
+ case PDMBLOCKTYPE_FLOPPY_FAKE_63_5: return "FLOPPY_FAKE_63_5";
+ case PDMBLOCKTYPE_CDROM: return "CDROM";
+ case PDMBLOCKTYPE_DVD: return "DVD";
+ case PDMBLOCKTYPE_HARD_DISK: return "HARD_DISK";
+ default: return "Unknown";
+
+ }
+}
+
+
/**
* Construct a block driver instance.
*
@@ -823,6 +869,7 @@ static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, u
pThis->IBlock.pfnMerge = drvblockMerge;
pThis->IBlock.pfnIsReadOnly = drvblockIsReadOnly;
pThis->IBlock.pfnGetSize = drvblockGetSize;
+ pThis->IBlock.pfnGetSectorSize = drvblockGetSectorSize;
pThis->IBlock.pfnGetType = drvblockGetType;
pThis->IBlock.pfnGetUuid = drvblockGetUuid;
@@ -889,6 +936,10 @@ static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, u
pThis->enmType = PDMBLOCKTYPE_FLOPPY_720;
else if (!strcmp(psz, "Floppy 360"))
pThis->enmType = PDMBLOCKTYPE_FLOPPY_360;
+ else if (!strcmp(psz, "Floppy 15.6"))
+ pThis->enmType = PDMBLOCKTYPE_FLOPPY_FAKE_15_6;
+ else if (!strcmp(psz, "Floppy 63.5"))
+ pThis->enmType = PDMBLOCKTYPE_FLOPPY_FAKE_63_5;
else
{
PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_BLOCK_UNKNOWN_TYPE, RT_SRC_POS,
@@ -1009,6 +1060,56 @@ static DECLCALLBACK(int) drvblockConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, u
pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, &pThis->Uuid);
}
+ /*
+ * Automatically upgrade the floppy drive if the specified one is too
+ * small to represent the whole boot time image. (We cannot do this later
+ * since the BIOS (and others) gets the info via CMOS.)
+ *
+ * This trick should make 2.88 images as well as the fake 15.6 and 63.5 MB
+ * images despite the hardcoded default 1.44 drive.
+ */
+ if ( PDMBLOCKTYPE_IS_FLOPPY(pThis->enmType)
+ && pThis->pDrvMedia)
+ {
+ uint64_t const cbFloppyImg = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
+ PDMBLOCKTYPE const enmCfgType = pThis->enmType;
+ switch (enmCfgType)
+ {
+ default:
+ AssertFailed();
+ case PDMBLOCKTYPE_FLOPPY_360:
+ if (cbFloppyImg > 40 * 2 * 9 * 512)
+ pThis->enmType = PDMBLOCKTYPE_FLOPPY_360;
+ /* fall thru */
+ case PDMBLOCKTYPE_FLOPPY_720:
+ if (cbFloppyImg > 80 * 2 * 14 * 512)
+ pThis->enmType = PDMBLOCKTYPE_FLOPPY_1_20;
+ /* fall thru */
+ case PDMBLOCKTYPE_FLOPPY_1_20:
+ if (cbFloppyImg > 80 * 2 * 20 * 512)
+ pThis->enmType = PDMBLOCKTYPE_FLOPPY_1_44;
+ /* fall thru */
+ case PDMBLOCKTYPE_FLOPPY_1_44:
+ if (cbFloppyImg > 80 * 2 * 24 * 512)
+ pThis->enmType = PDMBLOCKTYPE_FLOPPY_2_88;
+ /* fall thru */
+ case PDMBLOCKTYPE_FLOPPY_2_88:
+ if (cbFloppyImg > 80 * 2 * 48 * 512)
+ pThis->enmType = PDMBLOCKTYPE_FLOPPY_FAKE_15_6;
+ /* fall thru */
+ case PDMBLOCKTYPE_FLOPPY_FAKE_15_6:
+ if (cbFloppyImg > 255 * 2 * 63 * 512)
+ pThis->enmType = PDMBLOCKTYPE_FLOPPY_FAKE_63_5;
+ case PDMBLOCKTYPE_FLOPPY_FAKE_63_5:
+ if (cbFloppyImg > 255 * 2 * 255 * 512)
+ LogRel(("Warning: Floppy image is larger that 63.5 MB! (%llu bytes)\n", cbFloppyImg));
+ break;
+ }
+ if (pThis->enmType != enmCfgType)
+ LogRel(("Automatically upgraded floppy drive from %s to %s to better support the %u byte image\n",
+ drvblockGetTypeName(enmCfgType), drvblockGetTypeName(pThis->enmType), cbFloppyImg));
+ }
+
return VINF_SUCCESS;
}
diff --git a/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp b/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp
index fa63d050..76419351 100644
--- a/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp
+++ b/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -160,6 +160,8 @@ typedef struct DRVDISKINTEGRITY
/** Flag whether consistency checks are enabled. */
bool fCheckConsistency;
+ /** Flag whether the RAM disk was prepopulated. */
+ bool fPrepopulateRamDisk;
/** AVL tree containing the disk blocks to check. */
PAVLRFOFFTREE pTreeSegments;
@@ -437,6 +439,17 @@ static int drvdiskintReadVerify(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsign
cbRange = cbLeft;
else
cbRange = pSeg->Core.Key - offCurr;
+
+ if (pThis->fPrepopulateRamDisk)
+ {
+ /* No segment means everything should be 0 for this part. */
+ if (!RTSgBufIsZero(&SgBuf, cbRange))
+ {
+ RTMsgError("Corrupted disk at offset %llu (expected everything to be 0)!\n",
+ offCurr);
+ RTAssertDebugBreak();
+ }
+ }
}
else
{
@@ -522,7 +535,7 @@ static int drvdiskintDiscardRecords(PDRVDISKINTEGRITY pThis, PCRTRANGE paRanges,
{
size_t cbPreLeft, cbPostLeft;
- cbRange = RT_MIN(cbRange, pSeg->Core.KeyLast - offStart + 1);
+ cbRange = RT_MIN(cbLeft, pSeg->Core.KeyLast - offStart + 1);
cbPreLeft = offStart - pSeg->Core.Key;
cbPostLeft = pSeg->cbSeg - cbRange - cbPreLeft;
@@ -1241,7 +1254,8 @@ static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg,
"ExpireIntervalMs\0"
"CheckDoubleCompletions\0"
"HistorySize\0"
- "IoLog\0"))
+ "IoLog\0"
+ "PrepopulateRamDisk\0"))
return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
rc = CFGMR3QueryBoolDef(pCfg, "CheckConsistency", &pThis->fCheckConsistency, false);
@@ -1256,6 +1270,8 @@ static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg,
AssertRC(rc);
rc = CFGMR3QueryU32Def(pCfg, "HistorySize", &pThis->cEntries, 512);
AssertRC(rc);
+ rc = CFGMR3QueryBoolDef(pCfg, "PrepopulateRamDisk", &pThis->fPrepopulateRamDisk, false);
+ AssertRC(rc);
char *pszIoLogFilename = NULL;
rc = CFGMR3QueryStringAlloc(pCfg, "IoLog", &pszIoLogFilename);
@@ -1363,6 +1379,50 @@ static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg,
MMR3HeapFree(pszIoLogFilename);
}
+ /* Read in all data before the start if requested. */
+ if (pThis->fPrepopulateRamDisk)
+ {
+ uint64_t cbDisk = 0;
+
+ LogRel(("DiskIntegrity: Prepopulating RAM disk, this will take some time...\n"));
+
+ cbDisk = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
+ if (cbDisk)
+ {
+ uint64_t off = 0;
+ uint8_t abBuffer[_64K];
+ RTSGSEG Seg;
+
+ Seg.pvSeg = abBuffer;
+
+ while (cbDisk)
+ {
+ size_t cbThisRead = RT_MIN(cbDisk, sizeof(abBuffer));
+
+ rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, abBuffer, cbThisRead);
+ if (RT_FAILURE(rc))
+ break;
+
+ if (ASMBitFirstSet(abBuffer, sizeof(abBuffer) * 8) != -1)
+ {
+ Seg.cbSeg = cbThisRead;
+ rc = drvdiskintWriteRecord(pThis, &Seg, 1,
+ off, cbThisRead);
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ cbDisk -= cbThisRead;
+ off += cbThisRead;
+ }
+
+ LogRel(("DiskIntegrity: Prepopulating RAM disk finished with %Rrc\n", rc));
+ }
+ else
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_INTERNAL_ERROR,
+ N_("DiskIntegrity: Error querying the media size below"));
+ }
+
return rc;
}
diff --git a/src/VBox/Devices/Storage/DrvHostBase.cpp b/src/VBox/Devices/Storage/DrvHostBase.cpp
index cf2cbdd1..5c588f20 100644
--- a/src/VBox/Devices/Storage/DrvHostBase.cpp
+++ b/src/VBox/Devices/Storage/DrvHostBase.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -872,7 +872,7 @@ static int drvHostBaseOpen(PDRVHOSTBASE pThis, PRTFILE pFileDevice, bool fReadOn
else
{
strcpy(szName1, *pszVendor ? pszVendor : pszProduct);
- RTStrPrintf(szName2, sizeof(szName2), "%s %s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
+ RTStrPrintf(szName2, sizeof(szName2), "%s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
}
}
else
@@ -1486,7 +1486,7 @@ static LRESULT CALLBACK DeviceChangeWindowProc(HWND hwnd, UINT uMsg, WPARAM wPar
Log2(("DeviceChangeWindowProc: hwnd=%08x uMsg=%08x\n", hwnd, uMsg));
if (uMsg == WM_DESTROY)
{
- PDRVHOSTBASE pThis = (PDRVHOSTBASE)GetWindowLong(hwnd, GWLP_USERDATA);
+ PDRVHOSTBASE pThis = (PDRVHOSTBASE)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (pThis)
ASMAtomicXchgSize(&pThis->hwndDeviceChange, NULL);
PostQuitMessage(0);
@@ -1560,7 +1560,7 @@ static DECLCALLBACK(int) drvHostBaseMediaThread(RTTHREAD ThreadSelf, void *pvUse
memset(&s_classDeviceChange, 0, sizeof(s_classDeviceChange));
s_classDeviceChange.lpfnWndProc = DeviceChangeWindowProc;
s_classDeviceChange.lpszClassName = "VBOX_DeviceChangeClass";
- s_classDeviceChange.hInstance = GetModuleHandle("VBOXDD.DLL");
+ s_classDeviceChange.hInstance = GetModuleHandle("VBoxDD.dll");
Assert(s_classDeviceChange.hInstance);
s_hAtomDeviceChange = RegisterClassA(&s_classDeviceChange);
Assert(s_hAtomDeviceChange);
@@ -1877,6 +1877,7 @@ int DRVHostBaseInitData(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, PDMBLOCKTYPE enmType
#endif
pThis->enmType = enmType;
//pThis->cErrors = 0;
+ pThis->fAttachFailError = true; /* It's an error until we've read the config. */
pThis->pfnGetMediaSize = drvHostBaseGetMediaSize;
@@ -2078,6 +2079,8 @@ int DRVHostBaseInitFinish(PDRVHOSTBASE pThis)
case PDMBLOCKTYPE_FLOPPY_1_20:
case PDMBLOCKTYPE_FLOPPY_1_44:
case PDMBLOCKTYPE_FLOPPY_2_88:
+ case PDMBLOCKTYPE_FLOPPY_FAKE_15_6:
+ case PDMBLOCKTYPE_FLOPPY_FAKE_63_5:
if (uDriveType != DRIVE_REMOVABLE)
{
AssertMsgFailed(("Configuration error: '%s' is not a floppy (type=%d)\n",
diff --git a/src/VBox/Devices/Storage/DrvHostBase.h b/src/VBox/Devices/Storage/DrvHostBase.h
index c999fc05..04a560b5 100644
--- a/src/VBox/Devices/Storage/DrvHostBase.h
+++ b/src/VBox/Devices/Storage/DrvHostBase.h
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Devices/Storage/DrvHostDVD.cpp b/src/VBox/Devices/Storage/DrvHostDVD.cpp
index 3d5858ad..8ed2c1b1 100644
--- a/src/VBox/Devices/Storage/DrvHostDVD.cpp
+++ b/src/VBox/Devices/Storage/DrvHostDVD.cpp
@@ -34,9 +34,6 @@
# include <mach/mach_error.h>
# define USE_MEDIA_POLLING
-#elif defined(RT_OS_L4)
-/* nothing (yet). */
-
#elif defined RT_OS_LINUX
# include <sys/ioctl.h>
# include <linux/version.h>
@@ -437,10 +434,6 @@ static int drvHostDvdSendCmd(PPDMIBLOCK pInterface, const uint8_t *pbCmd,
/* sense information set */
rc = VERR_DEV_IO_ERROR;
-#elif defined(RT_OS_L4)
- /* Not really ported to L4 yet. */
- rc = VERR_INTERNAL_ERROR;
-
#elif defined(RT_OS_LINUX)
int direction;
struct cdrom_generic_command cgc;
@@ -758,62 +751,64 @@ static DECLCALLBACK(int) drvHostDvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg,
LogFlow(("drvHostDvdConstruct: iInstance=%d\n", pDrvIns->iInstance));
/*
- * Validate configuration.
- */
- if (!CFGMR3AreValuesValid(pCfg, "Path\0Interval\0Locked\0BIOSVisible\0AttachFailError\0Passthrough\0"))
- return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
-
-
- /*
* Init instance data.
*/
int rc = DRVHostBaseInitData(pDrvIns, pCfg, PDMBLOCKTYPE_DVD);
if (RT_SUCCESS(rc))
{
/*
- * Override stuff.
+ * Validate configuration.
*/
+ if (CFGMR3AreValuesValid(pCfg, "Path\0Interval\0Locked\0BIOSVisible\0AttachFailError\0Passthrough\0"))
+ {
+ /*
+ * Override stuff.
+ */
#ifdef RT_OS_LINUX
- pThis->pbDoubleBuffer = (uint8_t *)RTMemAlloc(SCSI_MAX_BUFFER_SIZE);
- if (!pThis->pbDoubleBuffer)
- return VERR_NO_MEMORY;
+ pThis->pbDoubleBuffer = (uint8_t *)RTMemAlloc(SCSI_MAX_BUFFER_SIZE);
+ if (!pThis->pbDoubleBuffer)
+ return VERR_NO_MEMORY;
#endif
-#ifndef RT_OS_L4 /* Passthrough is not supported on L4 yet */
- bool fPassthrough;
- rc = CFGMR3QueryBool(pCfg, "Passthrough", &fPassthrough);
- if (RT_SUCCESS(rc) && fPassthrough)
- {
- pThis->IBlock.pfnSendCmd = drvHostDvdSendCmd;
- /* Passthrough requires opening the device in R/W mode. */
- pThis->fReadOnlyConfig = false;
-# ifdef VBOX_WITH_SUID_WRAPPER /* Solaris setuid for Passthrough mode. */
- rc = solarisCheckUserAuth();
- if (RT_FAILURE(rc))
+ bool fPassthrough;
+ rc = CFGMR3QueryBool(pCfg, "Passthrough", &fPassthrough);
+ if (RT_SUCCESS(rc) && fPassthrough)
{
- Log(("DVD: solarisCheckUserAuth failed. Permission denied!\n"));
- return rc;
+ pThis->IBlock.pfnSendCmd = drvHostDvdSendCmd;
+ /* Passthrough requires opening the device in R/W mode. */
+ pThis->fReadOnlyConfig = false;
+#ifdef VBOX_WITH_SUID_WRAPPER /* Solaris setuid for Passthrough mode. */
+ rc = solarisCheckUserAuth();
+ if (RT_FAILURE(rc))
+ {
+ Log(("DVD: solarisCheckUserAuth failed. Permission denied!\n"));
+ return rc;
+ }
+#endif /* VBOX_WITH_SUID_WRAPPER */
}
-# endif /* VBOX_WITH_SUID_WRAPPER */
- }
-#endif /* !RT_OS_L4 */
- pThis->IMount.pfnUnmount = drvHostDvdUnmount;
- pThis->pfnDoLock = drvHostDvdDoLock;
+ pThis->IMount.pfnUnmount = drvHostDvdUnmount;
+ pThis->pfnDoLock = drvHostDvdDoLock;
#ifdef USE_MEDIA_POLLING
- if (!fPassthrough)
- pThis->pfnPoll = drvHostDvdPoll;
- else
- pThis->pfnPoll = NULL;
+ if (!fPassthrough)
+ pThis->pfnPoll = drvHostDvdPoll;
+ else
+ pThis->pfnPoll = NULL;
#endif
#ifdef RT_OS_LINUX
- pThis->pfnGetMediaSize = drvHostDvdGetMediaSize;
+ pThis->pfnGetMediaSize = drvHostDvdGetMediaSize;
#endif
- /*
- * 2nd init part.
- */
- rc = DRVHostBaseInitFinish(pThis);
+ /*
+ * 2nd init part.
+ */
+ rc = DRVHostBaseInitFinish(pThis);
+ }
+ else
+ {
+ pThis->fAttachFailError = true;
+ rc = VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
+ }
}
if (RT_FAILURE(rc))
{
diff --git a/src/VBox/Devices/Storage/DrvHostFloppy.cpp b/src/VBox/Devices/Storage/DrvHostFloppy.cpp
index a65185b6..ba53e807 100644
--- a/src/VBox/Devices/Storage/DrvHostFloppy.cpp
+++ b/src/VBox/Devices/Storage/DrvHostFloppy.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -91,7 +91,7 @@ static DECLCALLBACK(int) drvHostFloppyGetMediaSize(PDRVHOSTBASE pThis, uint64_t
dwLastError = GetLastError();
rc = RTErrConvertFromWin32(dwLastError);
- Log(("DrvHostFloppy: IOCTL_DISK_GET_DRIVE_GEOMETRY(%s) failed, LastError=%d rc=%Rrc\n",
+ Log(("DrvHostFloppy: IOCTL_DISK_GET_DRIVE_GEOMETRY(%s) failed, LastError=%d rc=%Rrc\n",
pThis->pszDevice, dwLastError, rc));
return rc;
}
@@ -184,32 +184,37 @@ static DECLCALLBACK(int) drvHostFloppyConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pC
LogFlow(("drvHostFloppyConstruct: iInstance=%d\n", pDrvIns->iInstance));
/*
- * Validate configuration.
- */
- if (!CFGMR3AreValuesValid(pCfg, "Path\0ReadOnly\0Interval\0Locked\0BIOSVisible\0"))
- return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
-
- /*
* Init instance data.
*/
int rc = DRVHostBaseInitData(pDrvIns, pCfg, PDMBLOCKTYPE_FLOPPY_1_44);
if (RT_SUCCESS(rc))
{
/*
- * Override stuff.
+ * Validate configuration.
*/
+ if (CFGMR3AreValuesValid(pCfg, "Path\0ReadOnly\0Interval\0Locked\0BIOSVisible\0"))
+ {
+ /*
+ * Override stuff.
+ */
#ifdef RT_OS_WINDOWS
- pThis->Base.pfnGetMediaSize = drvHostFloppyGetMediaSize;
+ pThis->Base.pfnGetMediaSize = drvHostFloppyGetMediaSize;
#endif
#ifdef RT_OS_LINUX
- pThis->Base.pfnPoll = drvHostFloppyPoll;
- pThis->Base.pfnGetMediaSize = drvHostFloppyGetMediaSize;
+ pThis->Base.pfnPoll = drvHostFloppyPoll;
+ pThis->Base.pfnGetMediaSize = drvHostFloppyGetMediaSize;
#endif
- /*
- * 2nd init part.
- */
- rc = DRVHostBaseInitFinish(&pThis->Base);
+ /*
+ * 2nd init part.
+ */
+ rc = DRVHostBaseInitFinish(&pThis->Base);
+ }
+ else
+ {
+ pThis->Base.fAttachFailError = true;
+ rc = VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
+ }
}
if (RT_FAILURE(rc))
{
diff --git a/src/VBox/Devices/Storage/DrvMediaISO.cpp b/src/VBox/Devices/Storage/DrvMediaISO.cpp
index f5fc8d24..c18a9dd2 100644
--- a/src/VBox/Devices/Storage/DrvMediaISO.cpp
+++ b/src/VBox/Devices/Storage/DrvMediaISO.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -196,8 +196,11 @@ static DECLCALLBACK(void) drvMediaISODestruct(PPDMDRVINS pDrvIns)
LogFlow(("drvMediaISODestruct: '%s'\n", pThis->pszFilename));
PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
- RTFileClose(pThis->hFile);
- pThis->hFile = NIL_RTFILE;
+ if (pThis->hFile != NIL_RTFILE)
+ {
+ RTFileClose(pThis->hFile);
+ pThis->hFile = NIL_RTFILE;
+ }
if (pThis->pszFilename)
{
diff --git a/src/VBox/Devices/Storage/DrvRawImage.cpp b/src/VBox/Devices/Storage/DrvRawImage.cpp
index 72d96bb9..75a2ffc8 100644
--- a/src/VBox/Devices/Storage/DrvRawImage.cpp
+++ b/src/VBox/Devices/Storage/DrvRawImage.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -234,8 +234,11 @@ static DECLCALLBACK(void) drvRawImageDestruct(PPDMDRVINS pDrvIns)
LogFlow(("drvRawImageDestruct: '%s'\n", pThis->pszFilename));
PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
- RTFileClose(pThis->hFile);
- pThis->hFile = NIL_RTFILE;
+ if (pThis->hFile != NIL_RTFILE)
+ {
+ RTFileClose(pThis->hFile);
+ pThis->hFile = NIL_RTFILE;
+ }
if (pThis->pszFilename)
{
diff --git a/src/VBox/Devices/Storage/DrvSCSI.cpp b/src/VBox/Devices/Storage/DrvSCSI.cpp
index e7ff7068..1b0ce172 100644
--- a/src/VBox/Devices/Storage/DrvSCSI.cpp
+++ b/src/VBox/Devices/Storage/DrvSCSI.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -70,10 +70,8 @@ typedef struct DRVSCSI
PDMIBLOCKPORT IPort;
/** The optional block async port interface. */
PDMIBLOCKASYNCPORT IPortAsync;
-#if 0 /* these interfaces aren't implemented */
/** The mount notify interface. */
PDMIMOUNTNOTIFY IMountNotify;
-#endif
/** Fallback status LED state for this drive.
* This is used in case the device doesn't has a LED interface. */
PDMLED Led;
@@ -114,6 +112,8 @@ typedef struct DRVSCSI
#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
/** Converts a pointer to DRVSCSI::IPortAsync to a PDRVSCSI. */
#define PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPortAsync)) )
+/** Converts a pointer to DRVSCSI::IMountNotify to PDRVSCSI. */
+#define PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IMountNotify)) )
/** Converts a pointer to DRVSCSI::IPort to a PDRVSCSI. */
#define PDMIBLOCKPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPort)) )
@@ -245,6 +245,27 @@ static DECLCALLBACK(int) drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser,
return VINF_SUCCESS;
}
+
+static DECLCALLBACK(int) drvscsiGetSectorSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint32_t *pcbSectorSize)
+{
+ PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
+
+ *pcbSectorSize = pThis->pDrvBlock->pfnGetSectorSize(pThis->pDrvBlock);
+
+ return VINF_SUCCESS;
+}
+static DECLCALLBACK(int) drvscsiSetLock(VSCSILUN hVScsiLun, void *pvScsiLunUser, bool fLocked)
+{
+ PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
+
+ if (fLocked)
+ pThis->pDrvMount->pfnLock(pThis->pDrvMount);
+ else
+ pThis->pDrvMount->pfnUnlock(pThis->pDrvMount);
+
+ return VINF_SUCCESS;
+}
+
static int drvscsiTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rc)
{
PDRVSCSI pThis = PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface);
@@ -635,9 +656,12 @@ static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const c
PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKBIOS, pThis->pDrvBlockBios);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync);
return NULL;
}
@@ -652,6 +676,38 @@ static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIBLOCKPORT pInterface, c
}
/**
+ * Called when media is mounted.
+ *
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ */
+static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface)
+{
+ PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface);
+ LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun));
+
+ /* Ignore the call if we're called while being attached. */
+ if (!pThis->pDrvBlock)
+ return;
+
+ /* Let the LUN know that a medium was mounted. */
+ VSCSILunMountNotify(pThis->hVScsiLun);
+}
+
+/**
+ * Called when media is unmounted
+ *
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ */
+static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
+{
+ PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface);
+ LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun));
+
+ /* Let the LUN know that the medium was unmounted. */
+ VSCSILunUnmountNotify(pThis->hVScsiLun);
+}
+
+/**
* Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
*
* @param pDrvIns The driver instance.
@@ -800,15 +856,21 @@ static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
}
/* Free the VSCSI device and LUN handle. */
- VSCSILUN hVScsiLun;
- int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
- AssertRC(rc);
-
- Assert(hVScsiLun == pThis->hVScsiLun);
- rc = VSCSILunDestroy(hVScsiLun);
- AssertRC(rc);
- rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
- AssertRC(rc);
+ if (pThis->hVScsiDevice)
+ {
+ VSCSILUN hVScsiLun;
+ int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
+ AssertRC(rc);
+
+ Assert(hVScsiLun == pThis->hVScsiLun);
+ rc = VSCSILunDestroy(hVScsiLun);
+ AssertRC(rc);
+ rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
+ AssertRC(rc);
+
+ pThis->hVScsiDevice = NULL;
+ pThis->hVScsiLun = NULL;
+ }
}
/**
@@ -831,6 +893,8 @@ static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, ui
pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
+ pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify;
+ pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify;
pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
pThis->hQueueRequests = NIL_RTREQQUEUE;
@@ -896,24 +960,60 @@ static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, ui
pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC);
PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
- if (enmType != PDMBLOCKTYPE_HARD_DISK)
+ VSCSILUNTYPE enmLunType;
+ switch (enmType)
+ {
+ case PDMBLOCKTYPE_HARD_DISK:
+ enmLunType = VSCSILUNTYPE_SBC;
+ break;
+ case PDMBLOCKTYPE_CDROM:
+ case PDMBLOCKTYPE_DVD:
+ enmLunType = VSCSILUNTYPE_MMC;
+ break;
+ default:
return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
- N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"),
+ N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
enmType);
+ }
+ if ( ( enmType == PDMBLOCKTYPE_DVD
+ || enmType == PDMBLOCKTYPE_CDROM)
+ && !pThis->pDrvMount)
+ {
+ AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
+ return VERR_INTERNAL_ERROR;
+ }
/* Create VSCSI device and LUN. */
- pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
- pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
- pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
+ pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
+ pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSectorSize = drvscsiGetSectorSize;
+ pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
+ pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
+ pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc);
- rc = VSCSILunCreate(&pThis->hVScsiLun, VSCSILUNTYPE_SBC, &pThis->VScsiIoCallbacks,
+ rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
pThis);
AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc);
rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
+ //@todo: This is a very hacky way of telling the LUN whether a medium was mounted.
+ // The mount/unmount interface doesn't work in a very sensible manner!
+ if (pThis->pDrvMount)
+ {
+ if (pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock))
+ {
+ rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
+ AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
+ }
+ else
+ {
+ rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
+ AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
+ }
+ }
+
/* Register statistics counter. */
/** @todo aeichner: Find a way to put the instance number of the attached
* controller device when we support more than one controller of the same type.
@@ -1001,4 +1101,3 @@ const PDMDRVREG g_DrvSCSI =
/* u32EndVersion */
PDM_DRVREG_VERSION
};
-
diff --git a/src/VBox/Devices/Storage/DrvSCSIHost.cpp b/src/VBox/Devices/Storage/DrvSCSIHost.cpp
index ac4b32c5..f2ddd25d 100644
--- a/src/VBox/Devices/Storage/DrvSCSIHost.cpp
+++ b/src/VBox/Devices/Storage/DrvSCSIHost.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Devices/Storage/DrvVD.cpp b/src/VBox/Devices/Storage/DrvVD.cpp
index d1decd9f..f4ba4967 100644
--- a/src/VBox/Devices/Storage/DrvVD.cpp
+++ b/src/VBox/Devices/Storage/DrvVD.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -40,9 +40,13 @@
#ifdef VBOX_WITH_INIP
/* All lwip header files are not C++ safe. So hack around this. */
RT_C_DECLS_BEGIN
+#include <lwip/opt.h>
#include <lwip/inet.h>
#include <lwip/tcp.h>
#include <lwip/sockets.h>
+# ifdef VBOX_WITH_NEW_LWIP
+# include <lwip/inet6.h>
+# endif
RT_C_DECLS_END
#endif /* VBOX_WITH_INIP */
@@ -308,7 +312,7 @@ static DECLCALLBACK(void) drvvdAsyncTaskCompleted(PPDMDRVINS pDrvIns, void *pvTe
{
Assert(!pvUser);
pStorageBackend->rcReqLast = rcReq;
- pStorageBackend->fSyncIoPending = false;
+ ASMAtomicWriteBool(&pStorageBackend->fSyncIoPending, false);
RTSemEventSignal(pStorageBackend->EventSem);
}
else
@@ -345,9 +349,9 @@ static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation,
drvvdAsyncTaskCompleted, pStorageBackend, "AsyncTaskCompleted");
if (RT_SUCCESS(rc))
{
- uint32_t fFlags = (fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ
- ? PDMACEP_FILE_FLAGS_READ_ONLY
- : 0;
+ uint32_t fFlags = (fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ
+ ? PDMACEP_FILE_FLAGS_READ_ONLY
+ : 0;
if (pThis->fShareable)
{
Assert((fOpen & RTFILE_O_DENY_MASK) == RTFILE_O_DENY_NONE);
@@ -368,6 +372,8 @@ static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation,
if (RT_SUCCESS(rc))
{
+ LogFlow(("drvvdAsyncIOOpen: Successfully opened '%s'; fOpen=%#x pStorage=%p\n",
+ pszLocation, fOpen, pStorageBackend));
*ppStorage = pStorageBackend;
return VINF_SUCCESS;
}
@@ -408,8 +414,8 @@ static DECLCALLBACK(int) drvvdAsyncIOReadSync(void *pvUser, void *pStorage, uint
RTSGSEG DataSeg;
PPDMASYNCCOMPLETIONTASK pTask;
- Assert(!pStorageBackend->fSyncIoPending);
- ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
+ bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
+ Assert(!fOld);
DataSeg.cbSeg = cbRead;
DataSeg.pvSeg = pvBuf;
@@ -440,8 +446,8 @@ static DECLCALLBACK(int) drvvdAsyncIOWriteSync(void *pvUser, void *pStorage, uin
RTSGSEG DataSeg;
PPDMASYNCCOMPLETIONTASK pTask;
- Assert(!pStorageBackend->fSyncIoPending);
- ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
+ bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
+ Assert(!fOld);
DataSeg.cbSeg = cbWrite;
DataSeg.pvSeg = (void *)pvBuf;
@@ -472,8 +478,8 @@ static DECLCALLBACK(int) drvvdAsyncIOFlushSync(void *pvUser, void *pStorage)
LogFlowFunc(("pvUser=%#p pStorage=%#p\n", pvUser, pStorage));
- Assert(!pStorageBackend->fSyncIoPending);
- ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
+ bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
+ Assert(!fOld);
int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, NULL, &pTask);
if (RT_FAILURE(rc))
@@ -500,7 +506,7 @@ static DECLCALLBACK(int) drvvdAsyncIOReadAsync(void *pvUser, void *pStorage, uin
PVBOXDISK pThis = (PVBOXDISK)pvUser;
PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
- int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbRead,
+ int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, paSegments, (unsigned)cSegments, cbRead,
pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
if (rc == VINF_AIO_TASK_PENDING)
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
@@ -516,7 +522,7 @@ static DECLCALLBACK(int) drvvdAsyncIOWriteAsync(void *pvUser, void *pStorage, ui
PVBOXDISK pThis = (PVBOXDISK)pvUser;
PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
- int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbWrite,
+ int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, paSegments, (unsigned)cSegments, cbWrite,
pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
if (rc == VINF_AIO_TASK_PENDING)
rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
@@ -615,10 +621,17 @@ static int drvvdCfgQuery(void *pvUser, const char *pszName, char *pszString, siz
* VD TCP network stack interface implementation - INIP case *
*******************************************************************************/
+/**
+ * vvl: this structure duplicate meaning of sockaddr,
+ * perhaps it'd be better to get rid of it.
+ */
typedef union INIPSOCKADDRUNION
{
struct sockaddr Addr;
struct sockaddr_in Ipv4;
+#ifdef VBOX_WITH_NEW_LWIP
+ struct sockaddr_in6 Ipv6;
+#endif
} INIPSOCKADDRUNION;
typedef struct INIPSOCKET
@@ -665,6 +678,11 @@ static DECLCALLBACK(int) drvvdINIPClientConnect(VDSOCKET Sock, const char *pszAd
{
int rc = VINF_SUCCESS;
PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
+ int iInetFamily = PF_INET;
+ struct in_addr ip;
+#ifdef VBOX_WITH_NEW_LWIP
+ ip6_addr_t ip6;
+#endif
/* Check whether lwIP is set up in this VM instance. */
if (!DevINIPConfigured())
@@ -674,21 +692,43 @@ static DECLCALLBACK(int) drvvdINIPClientConnect(VDSOCKET Sock, const char *pszAd
}
/* Resolve hostname. As there is no standard resolver for lwIP yet,
* just accept numeric IP addresses for now. */
- struct in_addr ip;
- if (!lwip_inet_aton(pszAddress, &ip))
+#ifdef VBOX_WITH_NEW_LWIP
+ if (inet6_aton(pszAddress, &ip6))
+ iInetFamily = PF_INET6;
+ else /* concatination with if */
+#endif
+ if (!lwip_inet_aton(pszAddress, &ip))
{
LogRelFunc(("cannot resolve IP %s\n", pszAddress));
return VERR_NET_HOST_UNREACHABLE;
}
/* Create socket and connect. */
- int iSock = lwip_socket(PF_INET, SOCK_STREAM, 0);
+ int iSock = lwip_socket(iInetFamily, SOCK_STREAM, 0);
if (iSock != -1)
{
- struct sockaddr_in InAddr = {0};
- InAddr.sin_family = AF_INET;
- InAddr.sin_port = htons(uPort);
- InAddr.sin_addr = ip;
- if (!lwip_connect(iSock, (struct sockaddr *)&InAddr, sizeof(InAddr)))
+ struct sockaddr *pSockAddr = NULL;
+ if (iInetFamily == PF_INET)
+ {
+ struct sockaddr_in InAddr = {0};
+ InAddr.sin_family = AF_INET;
+ InAddr.sin_port = htons(uPort);
+ InAddr.sin_addr = ip;
+ InAddr.sin_len = sizeof(InAddr);
+ pSockAddr = (struct sockaddr *)&InAddr;
+ }
+#ifdef VBOX_WITH_NEW_LWIP
+ else
+ {
+ struct sockaddr_in6 In6Addr = {0};
+ In6Addr.sin6_family = AF_INET6;
+ In6Addr.sin6_port = htons(uPort);
+ memcpy(&In6Addr.sin6_addr, &ip6, sizeof(ip6));
+ In6Addr.sin6_len = sizeof(In6Addr);
+ pSockAddr = (struct sockaddr *)&In6Addr;
+ }
+#endif
+ if ( pSockAddr
+ && !lwip_connect(iSock, pSockAddr, pSockAddr->sa_len))
{
pSocketInt->hSock = iSock;
return VINF_SUCCESS;
@@ -882,6 +922,16 @@ static DECLCALLBACK(int) drvvdINIPGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAdd
pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
}
+#ifdef VBOX_WITH_NEW_LWIP
+ else if ( cbAddr == sizeof(struct sockaddr_in6)
+ && u.Addr.sa_family == AF_INET6)
+ {
+ RT_ZERO(*pAddr);
+ pAddr->enmType = RTNETADDRTYPE_IPV6;
+ pAddr->uPort = RT_N2H_U16(u.Ipv6.sin6_port);
+ memcpy(&pAddr->uAddr.IPv6, &u.Ipv6.sin6_addr, sizeof(RTNETADDRIPV6));
+ }
+#endif
else
return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
return VINF_SUCCESS;
@@ -909,6 +959,16 @@ static DECLCALLBACK(int) drvvdINIPGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr
pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
}
+#ifdef VBOX_WITH_NEW_LWIP
+ else if ( cbAddr == sizeof(struct sockaddr_in6)
+ && u.Addr.sa_family == AF_INET6)
+ {
+ RT_ZERO(*pAddr);
+ pAddr->enmType = RTNETADDRTYPE_IPV6;
+ pAddr->uPort = RT_N2H_U16(u.Ipv6.sin6_port);
+ memcpy(&pAddr->uAddr.IPv6, &u.Ipv6.sin6_addr, sizeof(RTNETADDRIPV6));
+ }
+#endif
else
return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
return VINF_SUCCESS;
@@ -1474,7 +1534,7 @@ static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
}
if (RT_SUCCESS(rc))
- Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Rhxd\n", __FUNCTION__,
+ Log2(("%s: off=%#llx pvBuf=%p cbRead=%d\n%.*Rhxd\n", __FUNCTION__,
off, pvBuf, cbRead, cbRead, pvBuf));
LogFlowFunc(("returns %Rrc\n", rc));
return rc;
@@ -1487,7 +1547,7 @@ static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
{
LogFlowFunc(("off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
- Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Rhxd\n", __FUNCTION__,
+ Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d\n%.*Rhxd\n", __FUNCTION__,
off, pvBuf, cbWrite, cbWrite, pvBuf));
/* Invalidate any buffer if boot acceleration is enabled. */
@@ -1557,6 +1617,16 @@ static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
return cb;
}
+/** @copydoc PDMIMEDIA::pfnGetSectorSize */
+static DECLCALLBACK(uint32_t) drvvdGetSectorSize(PPDMIMEDIA pInterface)
+{
+ LogFlowFunc(("\n"));
+ PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
+ uint32_t cb = VDGetSectorSize(pThis->pDisk, VD_LAST_IMAGE);
+ LogFlowFunc(("returns %u\n", cb));
+ return cb;
+}
+
/** @copydoc PDMIMEDIA::pfnIsReadOnly */
static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
{
@@ -2010,9 +2080,9 @@ static DECLCALLBACK(void) drvvdReset(PPDMDRVINS pDrvIns)
*/
static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
LogFlowFunc(("\n"));
- PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
RTSEMFASTMUTEX mutex;
ASMAtomicXchgHandle(&pThis->MergeCompleteMutex, NIL_RTSEMFASTMUTEX, &mutex);
@@ -2029,13 +2099,13 @@ static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
AssertRC(rc);
}
- if (VALID_PTR(pThis->pBlkCache))
+ if (RT_VALID_PTR(pThis->pBlkCache))
{
PDMR3BlkCacheRelease(pThis->pBlkCache);
pThis->pBlkCache = NULL;
}
- if (VALID_PTR(pThis->pDisk))
+ if (RT_VALID_PTR(pThis->pDisk))
{
VDDestroy(pThis->pDisk);
pThis->pDisk = NULL;
@@ -2049,7 +2119,10 @@ static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
pThis->MergeLock = NIL_RTSEMRW;
}
if (pThis->pbData)
+ {
RTMemFree(pThis->pbData);
+ pThis->pbData = NULL;
+ }
if (pThis->pszBwGroup)
{
MMR3HeapFree(pThis->pszBwGroup);
@@ -2062,11 +2135,10 @@ static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
*
* @copydoc FNPDMDRVCONSTRUCT
*/
-static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
- PCFGMNODE pCfg,
- uint32_t fFlags)
+static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
LogFlowFunc(("\n"));
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
int rc = VINF_SUCCESS;
char *pszName = NULL; /**< The path of the disk image file. */
@@ -2076,7 +2148,6 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
bool fReadOnly; /**< True if the media is read-only. */
bool fMaybeReadOnly; /**< True if the media may or may not be read-only. */
bool fHonorZeroWrites; /**< True if zero blocks should be written. */
- PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
/*
* Init the static parts.
@@ -2089,6 +2160,7 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
pThis->fShareable = false;
pThis->fMergePending = false;
pThis->MergeCompleteMutex = NIL_RTSEMFASTMUTEX;
+ pThis->MergeLock = NIL_RTSEMRW;
pThis->uMergeSource = VD_LAST_IMAGE;
pThis->uMergeTarget = VD_LAST_IMAGE;
@@ -2098,6 +2170,7 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
pThis->IMedia.pfnFlush = drvvdFlush;
pThis->IMedia.pfnMerge = drvvdMerge;
pThis->IMedia.pfnGetSize = drvvdGetSize;
+ pThis->IMedia.pfnGetSectorSize = drvvdGetSectorSize;
pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
@@ -2141,6 +2214,7 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
bool fUseBlockCache = false;
bool fDiscard = false;
bool fInformAboutZeroBlocks = false;
+ bool fSkipConsistencyChecks = false;
unsigned iLevel = 0;
PCFGMNODE pCurNode = pCfg;
VDTYPE enmType = VDTYPE_HDD;
@@ -2158,7 +2232,8 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
"ReadOnly\0MaybeReadOnly\0TempReadOnly\0Shareable\0HonorZeroWrites\0"
"HostIPStack\0UseNewIo\0BootAcceleration\0BootAccelerationBuffer\0"
"SetupMerge\0MergeSource\0MergeTarget\0BwGroup\0Type\0BlockCache\0"
- "CachePath\0CacheFormat\0Discard\0InformAboutZeroBlocks\0");
+ "CachePath\0CacheFormat\0Discard\0InformAboutZeroBlocks\0"
+ "SkipConsistencyChecks\0");
}
else
{
@@ -2300,6 +2375,13 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
N_("DrvVD: Configuration error: Querying \"InformAboutZeroBlocks\" as boolean failed"));
break;
}
+ rc = CFGMR3QueryBoolDef(pCurNode, "SkipConsistencyChecks", &fSkipConsistencyChecks, true);
+ if (RT_FAILURE(rc))
+ {
+ rc = PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvVD: Configuration error: Querying \"SKipConsistencyChecks\" as boolean failed"));
+ break;
+ }
char *psz;
rc = CFGMR3QueryStringAlloc(pCfg, "Type", &psz);
@@ -2405,6 +2487,8 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
if (pThis->pDrvMediaAsyncPort && fUseNewIo)
pThis->fAsyncIOSupported = true;
+ uint64_t tsStart = RTTimeNanoTS();
+
unsigned iImageIdx = 0;
while (pCurNode && RT_SUCCESS(rc))
{
@@ -2602,6 +2686,9 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
uOpenFlags |= VD_OPEN_FLAGS_DISCARD;
if (fInformAboutZeroBlocks)
uOpenFlags |= VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS;
+ if ( (uOpenFlags & VD_OPEN_FLAGS_READONLY)
+ && fSkipConsistencyChecks)
+ uOpenFlags |= VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS;
/* Try to open backend in async I/O mode first. */
rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
@@ -2662,9 +2749,11 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
pCurNode = CFGMR3GetParent(pCurNode);
}
+ LogRel(("VD: Opening the disk took %lld ns\n", RTTimeNanoTS() - tsStart));
+
/* Open the cache image if set. */
if ( RT_SUCCESS(rc)
- && VALID_PTR(pszCachePath))
+ && RT_VALID_PTR(pszCachePath))
{
/* Insert the custom I/O interface only if we're told to use new IO.
* Since the I/O interface is per image we could make this more
@@ -2697,9 +2786,9 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
rc = PDMDRV_SET_ERROR(pDrvIns, rc, N_("DrvVD: Could not open cache image"));
}
- if (VALID_PTR(pszCachePath))
+ if (RT_VALID_PTR(pszCachePath))
MMR3HeapFree(pszCachePath);
- if (VALID_PTR(pszCacheFormat))
+ if (RT_VALID_PTR(pszCacheFormat))
MMR3HeapFree(pszCacheFormat);
if ( RT_SUCCESS(rc)
@@ -2791,9 +2880,9 @@ static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
if (RT_FAILURE(rc))
{
- if (VALID_PTR(pszName))
+ if (RT_VALID_PTR(pszName))
MMR3HeapFree(pszName);
- if (VALID_PTR(pszFormat))
+ if (RT_VALID_PTR(pszFormat))
MMR3HeapFree(pszFormat);
/* drvvdDestruct does the rest. */
}
diff --git a/src/VBox/Devices/Storage/PIIX3ATABmDma.h b/src/VBox/Devices/Storage/PIIX3ATABmDma.h
index 652a2d1f..af7059a4 100644
--- a/src/VBox/Devices/Storage/PIIX3ATABmDma.h
+++ b/src/VBox/Devices/Storage/PIIX3ATABmDma.h
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
diff --git a/src/VBox/Devices/Storage/UsbMsd.cpp b/src/VBox/Devices/Storage/UsbMsd.cpp
index d41b929a..52e9de8f 100644
--- a/src/VBox/Devices/Storage/UsbMsd.cpp
+++ b/src/VBox/Devices/Storage/UsbMsd.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2007-2010 Oracle Corporation
+ * Copyright (C) 2007-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -38,15 +38,24 @@
/** @name USB MSD string IDs
* @{ */
#define USBMSD_STR_ID_MANUFACTURER 1
-#define USBMSD_STR_ID_PRODUCT 2
+#define USBMSD_STR_ID_PRODUCT_HD 2
+#define USBMSD_STR_ID_PRODUCT_CDROM 3
/** @} */
+/** @name USB MSD vendor and product IDs
+ * @{ */
+#define VBOX_USB_VENDOR 0x80EE
+#define USBMSD_PID_HD 0x0030
+#define USBMSD_PID_CD 0x0031
+/** @} */
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
+
/**
- * USB Command block wrapper (MSD/SCSI).
+ * USB MSD Command Block Wrapper or CBW. The command block
+ * itself (CBWCB) contains protocol-specific data (here SCSI).
*/
#pragma pack(1)
typedef struct USBCBW
@@ -71,7 +80,7 @@ typedef USBCBW *PUSBCBW;
typedef const USBCBW *PCUSBCBW;
/**
- * USB Command Status Wrapper (MSD/SCSI).
+ * USB MSD Command Status Wrapper or CSW.
*/
#pragma pack(1)
typedef struct USBCSW
@@ -254,8 +263,9 @@ typedef USBMSD *PUSBMSD;
*******************************************************************************/
static const PDMUSBDESCCACHESTRING g_aUsbMsdStrings_en_US[] =
{
- { USBMSD_STR_ID_MANUFACTURER, "VirtualBox" },
- { USBMSD_STR_ID_PRODUCT, "VirtualBox MSD" },
+ { USBMSD_STR_ID_MANUFACTURER, "VirtualBox" },
+ { USBMSD_STR_ID_PRODUCT_HD, "USB Harddisk" },
+ { USBMSD_STR_ID_PRODUCT_CDROM, "USB CD-ROM" }
};
static const PDMUSBDESCCACHELANG g_aUsbMsdLanguages[] =
@@ -263,7 +273,7 @@ static const PDMUSBDESCCACHELANG g_aUsbMsdLanguages[] =
{ 0x0409, RT_ELEMENTS(g_aUsbMsdStrings_en_US), g_aUsbMsdStrings_en_US }
};
-static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescs[2] =
+static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescsFS[2] =
{
{
{
@@ -271,8 +281,8 @@ static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescs[2] =
/* .bDescriptorType = */ VUSB_DT_ENDPOINT,
/* .bEndpointAddress = */ 0x81 /* ep=1, in */,
/* .bmAttributes = */ 2 /* bulk */,
- /* .wMaxPacketSize = */ 0x200 /* or 64? */,
- /* .bInterval = */ 0xff,
+ /* .wMaxPacketSize = */ 64 /* maximum possible */,
+ /* .bInterval = */ 0 /* not applicable for bulk EP */
},
/* .pvMore = */ NULL,
/* .pvClass = */ NULL,
@@ -284,8 +294,8 @@ static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescs[2] =
/* .bDescriptorType = */ VUSB_DT_ENDPOINT,
/* .bEndpointAddress = */ 0x02 /* ep=2, out */,
/* .bmAttributes = */ 2 /* bulk */,
- /* .wMaxPacketSize = */ 0x200 /* or 64? */,
- /* .bInterval = */ 0xff,
+ /* .wMaxPacketSize = */ 64 /* maximum possible */,
+ /* .bInterval = */ 0 /* not applicable for bulk EP */
},
/* .pvMore = */ NULL,
/* .pvClass = */ NULL,
@@ -293,7 +303,58 @@ static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescs[2] =
}
};
-static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDesc =
+static const VUSBDESCENDPOINTEX g_aUsbMsdEndpointDescsHS[2] =
+{
+ {
+ {
+ /* .bLength = */ sizeof(VUSBDESCENDPOINT),
+ /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
+ /* .bEndpointAddress = */ 0x81 /* ep=1, in */,
+ /* .bmAttributes = */ 2 /* bulk */,
+ /* .wMaxPacketSize = */ 512 /* HS bulk packet size */,
+ /* .bInterval = */ 0 /* no NAKs */
+ },
+ /* .pvMore = */ NULL,
+ /* .pvClass = */ NULL,
+ /* .cbClass = */ 0
+ },
+ {
+ {
+ /* .bLength = */ sizeof(VUSBDESCENDPOINT),
+ /* .bDescriptorType = */ VUSB_DT_ENDPOINT,
+ /* .bEndpointAddress = */ 0x02 /* ep=2, out */,
+ /* .bmAttributes = */ 2 /* bulk */,
+ /* .wMaxPacketSize = */ 512 /* HS bulk packet size */,
+ /* .bInterval = */ 0 /* no NAKs */
+ },
+ /* .pvMore = */ NULL,
+ /* .pvClass = */ NULL,
+ /* .cbClass = */ 0
+ }
+};
+
+static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDescFS =
+{
+ {
+ /* .bLength = */ sizeof(VUSBDESCINTERFACE),
+ /* .bDescriptorType = */ VUSB_DT_INTERFACE,
+ /* .bInterfaceNumber = */ 0,
+ /* .bAlternateSetting = */ 0,
+ /* .bNumEndpoints = */ 2,
+ /* .bInterfaceClass = */ 8 /* Mass Storage */,
+ /* .bInterfaceSubClass = */ 6 /* SCSI transparent command set */,
+ /* .bInterfaceProtocol = */ 0x50 /* Bulk-Only Transport */,
+ /* .iInterface = */ 0
+ },
+ /* .pvMore = */ NULL,
+ /* .pvClass = */ NULL,
+ /* .cbClass = */ 0,
+ &g_aUsbMsdEndpointDescsFS[0],
+ /* .pIAD = */ NULL,
+ /* .cbIAD = */ 0
+};
+
+static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDescHS =
{
{
/* .bLength = */ sizeof(VUSBDESCINTERFACE),
@@ -309,52 +370,100 @@ static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDesc =
/* .pvMore = */ NULL,
/* .pvClass = */ NULL,
/* .cbClass = */ 0,
- &g_aUsbMsdEndpointDescs[0]
+ &g_aUsbMsdEndpointDescsHS[0],
+ /* .pIAD = */ NULL,
+ /* .cbIAD = */ 0
};
-static const VUSBINTERFACE g_aUsbMsdInterfaces[2] =
+static const VUSBINTERFACE g_aUsbMsdInterfacesFS[] =
{
- { &g_UsbMsdInterfaceDesc, /* .cSettings = */ 1 },
+ { &g_UsbMsdInterfaceDescFS, /* .cSettings = */ 1 },
};
-static const VUSBDESCCONFIGEX g_UsbMsdConfigDesc =
+static const VUSBINTERFACE g_aUsbMsdInterfacesHS[] =
+{
+ { &g_UsbMsdInterfaceDescHS, /* .cSettings = */ 1 },
+};
+
+static const VUSBDESCCONFIGEX g_UsbMsdConfigDescFS =
{
{
/* .bLength = */ sizeof(VUSBDESCCONFIG),
/* .bDescriptorType = */ VUSB_DT_CONFIG,
/* .wTotalLength = */ 0 /* recalculated on read */,
- /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbMsdInterfaces),
+ /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbMsdInterfacesFS),
/* .bConfigurationValue =*/ 1,
/* .iConfiguration = */ 0,
/* .bmAttributes = */ RT_BIT(7),
/* .MaxPower = */ 50 /* 100mA */
},
- NULL,
- &g_aUsbMsdInterfaces[0]
+ NULL, /* pvMore */
+ &g_aUsbMsdInterfacesFS[0],
+ NULL /* pvOriginal */
+};
+
+static const VUSBDESCCONFIGEX g_UsbMsdConfigDescHS =
+{
+ {
+ /* .bLength = */ sizeof(VUSBDESCCONFIG),
+ /* .bDescriptorType = */ VUSB_DT_CONFIG,
+ /* .wTotalLength = */ 0 /* recalculated on read */,
+ /* .bNumInterfaces = */ RT_ELEMENTS(g_aUsbMsdInterfacesHS),
+ /* .bConfigurationValue =*/ 1,
+ /* .iConfiguration = */ 0,
+ /* .bmAttributes = */ RT_BIT(7),
+ /* .MaxPower = */ 50 /* 100mA */
+ },
+ NULL, /* pvMore */
+ &g_aUsbMsdInterfacesHS[0],
+ NULL /* pvOriginal */
};
static const VUSBDESCDEVICE g_UsbMsdDeviceDesc =
{
/* .bLength = */ sizeof(g_UsbMsdDeviceDesc),
/* .bDescriptorType = */ VUSB_DT_DEVICE,
- /* .bcdUsb = */ 0x200,
+ /* .bcdUsb = */ 0x200, /* USB 2.0 */
/* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
/* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
- /* .bDeviceProtocol = */ 0x50 /* Protocol specified in the interface desc. */,
+ /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
/* .bMaxPacketSize0 = */ 64,
- /* .idVendor = */ 0x4200,
- /* .idProduct = */ 0x0042,
- /* .bcdDevice = */ 0x0100,
+ /* .idVendor = */ VBOX_USB_VENDOR,
+ /* .idProduct = */ USBMSD_PID_HD,
+ /* .bcdDevice = */ 0x0100, /* 1.0 */
/* .iManufacturer = */ USBMSD_STR_ID_MANUFACTURER,
- /* .iProduct = */ USBMSD_STR_ID_PRODUCT,
+ /* .iProduct = */ USBMSD_STR_ID_PRODUCT_HD,
/* .iSerialNumber = */ 0,
/* .bNumConfigurations = */ 1
};
-static const PDMUSBDESCCACHE g_UsbMsdDescCache =
+static const VUSBDEVICEQUALIFIER g_UsbMsdDeviceQualifier =
+{
+ /* .bLength = */ sizeof(g_UsbMsdDeviceQualifier),
+ /* .bDescriptorType = */ VUSB_DT_DEVICE_QUALIFIER,
+ /* .bcdUsb = */ 0x200, /* USB 2.0 */
+ /* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
+ /* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
+ /* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
+ /* .bMaxPacketSize0 = */ 64,
+ /* .bNumConfigurations = */ 1,
+ /* .bReserved = */ 0
+};
+
+static const PDMUSBDESCCACHE g_UsbMsdDescCacheFS =
{
/* .pDevice = */ &g_UsbMsdDeviceDesc,
- /* .paConfigs = */ &g_UsbMsdConfigDesc,
+ /* .paConfigs = */ &g_UsbMsdConfigDescFS,
+ /* .paLanguages = */ g_aUsbMsdLanguages,
+ /* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages),
+ /* .fUseCachedDescriptors = */ true,
+ /* .fUseCachedStringsDescriptors = */ true
+};
+
+static const PDMUSBDESCCACHE g_UsbMsdDescCacheHS =
+{
+ /* .pDevice = */ &g_UsbMsdDeviceDesc,
+ /* .paConfigs = */ &g_UsbMsdConfigDescHS,
/* .paLanguages = */ g_aUsbMsdLanguages,
/* .cLanguages = */ RT_ELEMENTS(g_aUsbMsdLanguages),
/* .fUseCachedDescriptors = */ true,
@@ -635,7 +744,7 @@ static int usbMsdCompleteOk(PUSBMSD pThis, PVUSBURB pUrb, size_t cbData)
Log(("usbMsdCompleteOk/#%u: pUrb=%p:%s cbData=%#zx\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, cbData));
pUrb->enmStatus = VUSBSTATUS_OK;
- pUrb->cbData = cbData;
+ pUrb->cbData = (uint32_t)cbData;
usbMsdLinkDone(pThis, pUrb);
return VINF_SUCCESS;
@@ -664,7 +773,7 @@ static int usbMsdResetWorker(PUSBMSD pThis, PVUSBURB pUrb, bool fSetConfig)
if ( pReq
&& pReq->enmState == USBMSDREQSTATE_EXECUTING)
{
- /* Don't try deal with the set config variant nor multiple build-only
+ /* Don't try to deal with the set config variant nor multiple build-only
mass storage resets. */
if (pThis->pResetUrb && (pUrb || fSetConfig))
{
@@ -784,7 +893,7 @@ static DECLCALLBACK(int) usbMsdLun0ScsiRequestCompleted(PPDMISCSIPORT pInterface
if (!pUrb)
break;
- /* Process it as the normal way. */
+ /* Process it the normal way. */
usbMsdHandleBulkDevToHost(pThis, &pThis->aEps[1], pUrb);
}
}
@@ -913,13 +1022,13 @@ static int usbMsdHandleScsiReqestSense(PUSBMSD pThis, PUSBMSDREQ pReq, PCUSBCBW
/* validation */
if ((pCbw->bmCBWFlags & USBCBW_DIR_MASK) != USBCBW_DIR_IN)
return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INVALID_MESSAGE, 0, "direction");
- if (pCbw->bCBWCBLength != 6)
+ if (pCbw->bCBWCBLength < 6)
return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INVALID_MESSAGE, 0, "length");
if ((pCbw->CBWCB[1] >> 5) != pCbw->bCBWLun)
return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0, "lun");
if (pCbw->bCBWLun != 0)
return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INVALID_MESSAGE, 0, "lun0");
- if ((pCbw->CBWCB[4] < 6) != pCbw->bCBWLun)
+ if (pCbw->CBWCB[4] < 6)
return usbMsdScsiIllegalRequest(pThis, pReq, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0, "out length");
/* If the previous command succeeded successfully, whip up some sense data. */
@@ -1000,7 +1109,7 @@ static bool usbMsdIsValidCommand(PUSBMSD pThis, PCUSBCBW pCbw, PVUSBURB pUrb)
/**
- * Handles request sent to the out-bound (to device) bulk pipe.
+ * Handle requests sent to the outbound (to device) bulk pipe.
*/
static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb)
{
@@ -1034,13 +1143,13 @@ static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb
Log(("usbMsd: Bad CBW: cbData=%#x < min=%#x\n", pUrb->cbData, RT_UOFFSETOF(USBCBW, CBWCB[1]) ));
return usbMsdCompleteStall(pThis, NULL, pUrb, "BAD CBW");
}
- Log(("usbMsd: CBW: dCBWSignature=%#x dCBWTag=%#x dCBWDataTransferLength=%#x bmCBWFlags=%#x bCBWLun=%#x bCBWCBLength=%#x cbData=%#x fShortNotOk=%RTbool\n",
- pCbw->dCBWSignature, pCbw->dCBWTag, pCbw->dCBWDataTransferLength, pCbw->bmCBWFlags, pCbw->bCBWLun, pCbw->bCBWCBLength, pUrb->cbData, pUrb->fShortNotOk));
if (pCbw->dCBWSignature != USBCBW_SIGNATURE)
{
Log(("usbMsd: CBW: Invalid dCBWSignature value: %#x\n", pCbw->dCBWSignature));
return usbMsdCompleteStall(pThis, NULL, pUrb, "Bad CBW");
}
+ Log(("usbMsd: CBW: dCBWTag=%#x dCBWDataTransferLength=%#x bmCBWFlags=%#x bCBWLun=%#x bCBWCBLength=%#x cbData=%#x fShortNotOk=%RTbool\n",
+ pCbw->dCBWTag, pCbw->dCBWDataTransferLength, pCbw->bmCBWFlags, pCbw->bCBWLun, pCbw->bCBWCBLength, pUrb->cbData, pUrb->fShortNotOk));
if (pCbw->bmCBWFlags & ~USBCBW_DIR_MASK)
{
Log(("usbMsd: CBW: Bad bmCBWFlags value: %#x\n", pCbw->bmCBWFlags));
@@ -1120,7 +1229,7 @@ static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb
}
}
- return usbMsdCompleteOk(pThis, pUrb, 0);
+ return usbMsdCompleteOk(pThis, pUrb, pUrb->cbData);
}
/*
@@ -1148,7 +1257,8 @@ static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb
return usbMsdCompleteStall(pThis, NULL, pUrb, "SCSI Submit #2");
}
}
- return usbMsdCompleteOk(pThis, pUrb, 0);
+LogRel(("DATA_FROM_HOST: %d bytes\n", cbData));
+ return usbMsdCompleteOk(pThis, pUrb, cbData);
}
/*
@@ -1168,7 +1278,7 @@ static int usbMsdHandleBulkHostToDev(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb
/**
- * Handles request sent to the in-bound (to host) bulk pipe.
+ * Handle requests sent to the inbound (to host) bulk pipe.
*/
static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb)
{
@@ -1208,6 +1318,7 @@ static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb
Log(("usbMsdHandleBulkDevToHost: Entering STATUS\n"));
pReq->enmState = USBMSDREQSTATE_STATUS;
}
+LogRel(("DATA_TO_HOST: %d bytes\n", cbCopy));
return usbMsdCompleteOk(pThis, pUrb, cbCopy);
}
@@ -1216,10 +1327,9 @@ static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb
*/
case USBMSDREQSTATE_STATUS:
{
- /** @todo !fShortNotOk and CSW request? */
- if (pUrb->cbData != sizeof(USBCSW))
+ if ((pUrb->cbData < sizeof(USBCSW)) || (pUrb->cbData > sizeof(USBCSW) && pUrb->fShortNotOk))
{
- Log(("usbMsd: Unexpected status request size: %#x (expected %#x)\n", pUrb->cbData, sizeof(USBCSW)));
+ Log(("usbMsd: Unexpected status request size: %#x (expected %#x), fShortNotOK=%RTbool\n", pUrb->cbData, sizeof(USBCSW), pUrb->fShortNotOk));
return usbMsdCompleteStall(pThis, NULL, pUrb, "Invalid CSW size");
}
@@ -1232,15 +1342,17 @@ static int usbMsdHandleBulkDevToHost(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb
: pReq->iScsiReqStatus >= 0
? USBCSW_STATUS_FAILED
: USBCSW_STATUS_PHASE_ERROR;
+ /** @todo the following is not always accurate; VSCSI needs
+ * to implement residual counts properly! */
if ((pReq->Cbw.bmCBWFlags & USBCBW_DIR_MASK) == USBCBW_DIR_OUT)
pCsw->dCSWDataResidue = pCsw->bCSWStatus == USBCSW_STATUS_OK
? pReq->Cbw.dCBWDataTransferLength - pReq->ScsiReq.cbScatterGather
: pReq->Cbw.dCBWDataTransferLength;
else
pCsw->dCSWDataResidue = pCsw->bCSWStatus == USBCSW_STATUS_OK
- ? pReq->ScsiReq.cbScatterGather
- : 0;
- Log(("usbMsdHandleBulkDevToHost: CSW: dCSWTag=%#x bCSWStatus=%d dCSWDataResidue=%#x\n",
+ ? 0
+ : pReq->ScsiReq.cbScatterGather;
+ Log(("usbMsd: CSW: dCSWTag=%#x bCSWStatus=%d dCSWDataResidue=%#x\n",
pCsw->dCSWTag, pCsw->bCSWStatus, pCsw->dCSWDataResidue));
Log(("usbMsdHandleBulkDevToHost: Entering READY\n"));
@@ -1333,9 +1445,18 @@ static int usbMsdHandleDefaultPipe(PUSBMSD pThis, PUSBMSDEP pEp, PVUSBURB pUrb)
switch (pSetup->wValue >> 8)
{
+ uint32_t cbCopy;
+
case VUSB_DT_STRING:
Log(("usbMsd: GET_DESCRIPTOR DT_STRING wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
break;
+ case VUSB_DT_DEVICE_QUALIFIER:
+ Log(("usbMsd: GET_DESCRIPTOR DT_DEVICE_QUALIFIER wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
+ /* Returned data is written after the setup message. */
+ cbCopy = pUrb->cbData - sizeof(*pSetup);
+ cbCopy = RT_MIN(cbCopy, sizeof(g_UsbMsdDeviceQualifier));
+ memcpy(&pUrb->abData[sizeof(*pSetup)], &g_UsbMsdDeviceQualifier, cbCopy);
+ return usbMsdCompleteOk(pThis, pUrb, cbCopy + sizeof(*pSetup));
default:
Log(("usbMsd: GET_DESCRIPTOR, huh? wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
break;
@@ -1484,7 +1605,10 @@ static DECLCALLBACK(PCPDMUSBDESCCACHE) usbMsdUsbGetDescriptorCache(PPDMUSBINS pU
{
PUSBMSD pThis = PDMINS_2_DATA(pUsbIns, PUSBMSD);
LogFlow(("usbMsdUsbGetDescriptorCache/#%u:\n", pUsbIns->iInstance));
- return &g_UsbMsdDescCache;
+ if (pThis->pUsbIns->iUsbHubVersion & VUSB_STDVER_20)
+ return &g_UsbMsdDescCacheHS;
+ else
+ return &g_UsbMsdDescCacheFS;
}
@@ -1602,7 +1726,7 @@ const PDMUSBREG g_UsbMsd =
/* pszDescription */
"USB Mass Storage Device, one LUN.",
/* fFlags */
- 0,
+ PDM_USBREG_HIGHSPEED_CAPABLE,
/* cMaxInstances */
~0U,
/* cbInstance */
diff --git a/src/VBox/Devices/Storage/VBoxSCSI.cpp b/src/VBox/Devices/Storage/VBoxSCSI.cpp
index 6351a8d3..e077cf58 100644
--- a/src/VBox/Devices/Storage/VBoxSCSI.cpp
+++ b/src/VBox/Devices/Storage/VBoxSCSI.cpp
@@ -1,12 +1,10 @@
/* $Id: VBoxSCSI.cpp $ */
/** @file
- *
- * VBox storage devices:
- * Simple SCSI interface for BIOS access
+ * VBox storage devices - Simple SCSI interface for BIOS access.
*/
/*
- * Copyright (C) 2006-2009 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -21,10 +19,10 @@
* Header Files *
*******************************************************************************/
//#define DEBUG
-#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC /* @todo: Create extra group. */
+#define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC /** @todo Create extra group. */
#if defined(IN_R0) || defined(IN_RC)
-# error This device has no R0 or GC components
+# error This device has no R0 or RC components
#endif
#include <VBox/vmm/pdmdev.h>
@@ -36,21 +34,26 @@
#include "VBoxSCSI.h"
+
+/**
+ * Resets the state.
+ */
static void vboxscsiReset(PVBOXSCSI pVBoxSCSI)
{
- pVBoxSCSI->regIdentify = 0;
- pVBoxSCSI->cbCDB = 0;
- memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
- pVBoxSCSI->iCDB = 0;
- pVBoxSCSI->fBusy = false;
- pVBoxSCSI->cbBuf = 0;
- pVBoxSCSI->iBuf = 0;
- if (pVBoxSCSI->pBuf)
- RTMemFree(pVBoxSCSI->pBuf);
-
- pVBoxSCSI->pBuf = NULL;
- pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
+ pVBoxSCSI->regIdentify = 0;
+ pVBoxSCSI->cbCDB = 0;
+ memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
+ pVBoxSCSI->iCDB = 0;
+ pVBoxSCSI->fBusy = false;
+ pVBoxSCSI->rcCompletion = 0;
+ pVBoxSCSI->uTargetDevice = 0;
+ pVBoxSCSI->cbBuf = 0;
+ pVBoxSCSI->iBuf = 0;
+ if (pVBoxSCSI->pbBuf)
+ RTMemFree(pVBoxSCSI->pbBuf);
+ pVBoxSCSI->pbBuf = NULL;
+ pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
}
/**
@@ -61,7 +64,7 @@ static void vboxscsiReset(PVBOXSCSI pVBoxSCSI)
*/
int vboxscsiInitialize(PVBOXSCSI pVBoxSCSI)
{
- pVBoxSCSI->pBuf = NULL;
+ pVBoxSCSI->pbBuf = NULL;
vboxscsiReset(pVBoxSCSI);
return VINF_SUCCESS;
@@ -91,16 +94,19 @@ int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32V
*/
RTThreadYield();
}
- else
- uVal &= ~VBOX_SCSI_BUSY;
+ if (pVBoxSCSI->rcCompletion)
+ uVal |= VBOX_SCSI_ERROR;
break;
}
case 1:
{
- if (pVBoxSCSI->cbBuf > 0)
+ /* If we're not in the 'command ready' state, there may not even be a buffer yet. */
+ if ((pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY) && pVBoxSCSI->cbBuf > 0)
{
- AssertMsg(pVBoxSCSI->pBuf, ("pBuf is NULL\n"));
- uVal = pVBoxSCSI->pBuf[pVBoxSCSI->iBuf];
+ AssertMsg(pVBoxSCSI->pbBuf, ("pBuf is NULL\n"));
+ Assert(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY);
+ Assert(!pVBoxSCSI->fBusy);
+ uVal = pVBoxSCSI->pbBuf[pVBoxSCSI->iBuf];
pVBoxSCSI->iBuf++;
pVBoxSCSI->cbBuf--;
if (pVBoxSCSI->cbBuf == 0)
@@ -108,14 +114,15 @@ int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32V
/** The guest read the last byte from the data in buffer.
* Clear everything and reset command buffer.
*/
- RTMemFree(pVBoxSCSI->pBuf);
- pVBoxSCSI->pBuf = NULL;
+ RTMemFree(pVBoxSCSI->pbBuf);
+ pVBoxSCSI->pbBuf = NULL;
pVBoxSCSI->cbCDB = 0;
pVBoxSCSI->iCDB = 0;
pVBoxSCSI->iBuf = 0;
+ pVBoxSCSI->rcCompletion = 0;
pVBoxSCSI->uTargetDevice = 0;
pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
- memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
+ memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
}
}
break;
@@ -125,6 +132,11 @@ int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32V
uVal = pVBoxSCSI->regIdentify;
break;
}
+ case 3:
+ {
+ uVal = pVBoxSCSI->rcCompletion;
+ break;
+ }
default:
AssertMsgFailed(("Invalid register to read from %u\n", iRegister));
}
@@ -162,33 +174,36 @@ int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
vboxscsiReset(pVBoxSCSI);
else
{
- pVBoxSCSI->enmState = VBOXSCSISTATE_READ_CDB_SIZE;
+ pVBoxSCSI->enmState = VBOXSCSISTATE_READ_CDB_SIZE_BUFHI;
pVBoxSCSI->uTxDir = uVal;
}
}
- else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_CDB_SIZE)
+ else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_CDB_SIZE_BUFHI)
{
- if (uVal > VBOXSCSI_CDB_SIZE_MAX)
+ uint8_t cbCDB = uVal & 0x0F;
+
+ if (cbCDB > VBOXSCSI_CDB_SIZE_MAX)
vboxscsiReset(pVBoxSCSI);
else
{
- pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_LOW;
- pVBoxSCSI->cbCDB = uVal;
+ pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_LSB;
+ pVBoxSCSI->cbCDB = cbCDB;
+ pVBoxSCSI->cbBuf = (uVal & 0xF0) << 12; /* Bits 16-19 of buffer size. */
}
}
- else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_LOW)
+ else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_LSB)
{
- pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_HIGH;
- pVBoxSCSI->cbBuf = uVal;
+ pVBoxSCSI->enmState = VBOXSCSISTATE_READ_BUFFER_SIZE_MID;
+ pVBoxSCSI->cbBuf |= uVal; /* Bits 0-7 of buffer size. */
}
- else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_HIGH)
+ else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_BUFFER_SIZE_MID)
{
pVBoxSCSI->enmState = VBOXSCSISTATE_READ_COMMAND;
- pVBoxSCSI->cbBuf |= (((uint16_t)uVal) << 8);
+ pVBoxSCSI->cbBuf |= (((uint16_t)uVal) << 8); /* Bits 8-15 of buffer size. */
}
else if (pVBoxSCSI->enmState == VBOXSCSISTATE_READ_COMMAND)
{
- pVBoxSCSI->aCDB[pVBoxSCSI->iCDB] = uVal;
+ pVBoxSCSI->abCDB[pVBoxSCSI->iCDB] = uVal;
pVBoxSCSI->iCDB++;
/* Check if we have all necessary command data. */
@@ -199,8 +214,8 @@ int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
{
/* This is a write allocate buffer. */
- pVBoxSCSI->pBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
- if (!pVBoxSCSI->pBuf)
+ pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
+ if (!pVBoxSCSI->pbBuf)
return VERR_NO_MEMORY;
}
else
@@ -225,7 +240,7 @@ int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal)
}
else
{
- pVBoxSCSI->pBuf[pVBoxSCSI->iBuf++] = uVal;
+ pVBoxSCSI->pbBuf[pVBoxSCSI->iBuf++] = uVal;
if (pVBoxSCSI->iBuf == pVBoxSCSI->cbBuf)
{
rc = VERR_MORE_DATA;
@@ -268,13 +283,16 @@ int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint
AssertMsg(pVBoxSCSI->enmState == VBOXSCSISTATE_COMMAND_READY, ("Invalid state %u\n", pVBoxSCSI->enmState));
+ /* Clear any errors from a previous request. */
+ pVBoxSCSI->rcCompletion = 0;
+
if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
{
- if (pVBoxSCSI->pBuf)
- RTMemFree(pVBoxSCSI->pBuf);
+ if (pVBoxSCSI->pbBuf)
+ RTMemFree(pVBoxSCSI->pbBuf);
- pVBoxSCSI->pBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
- if (!pVBoxSCSI->pBuf)
+ pVBoxSCSI->pbBuf = (uint8_t *)RTMemAllocZ(pVBoxSCSI->cbBuf);
+ if (!pVBoxSCSI->pbBuf)
return VERR_NO_MEMORY;
}
@@ -282,8 +300,8 @@ int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint
pScsiRequest->paScatterGatherHead = (PRTSGSEG)RTMemAllocZ(sizeof(RTSGSEG) * 1); /* Only one element. */
if (!pScsiRequest->paScatterGatherHead)
{
- RTMemFree(pVBoxSCSI->pBuf);
- pVBoxSCSI->pBuf = NULL;
+ RTMemFree(pVBoxSCSI->pbBuf);
+ pVBoxSCSI->pbBuf = NULL;
return VERR_NO_MEMORY;
}
@@ -292,13 +310,13 @@ int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint
pScsiRequest->pbSenseBuffer = (uint8_t *)RTMemAllocZ(pScsiRequest->cbSenseBuffer);
pScsiRequest->cbCDB = pVBoxSCSI->cbCDB;
- pScsiRequest->pbCDB = pVBoxSCSI->aCDB;
+ pScsiRequest->pbCDB = pVBoxSCSI->abCDB;
pScsiRequest->uLogicalUnit = 0;
pScsiRequest->cbScatterGather = pVBoxSCSI->cbBuf;
pScsiRequest->cScatterGatherEntries = 1;
pScsiRequest->paScatterGatherHead[0].cbSeg = pVBoxSCSI->cbBuf;
- pScsiRequest->paScatterGatherHead[0].pvSeg = pVBoxSCSI->pBuf;
+ pScsiRequest->paScatterGatherHead[0].pvSeg = pVBoxSCSI->pbBuf;
*puTargetDevice = pVBoxSCSI->uTargetDevice;
@@ -309,7 +327,7 @@ int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint
* Notifies the device that a request finished and the incoming data
* is ready at the incoming data port.
*/
-int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
+int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, int rcCompletion)
{
LogFlowFunc(("pVBoxSCSI=%#p pScsiRequest=%#p\n", pVBoxSCSI, pScsiRequest));
RTMemFree(pScsiRequest->paScatterGatherHead);
@@ -317,18 +335,20 @@ int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_TO_DEVICE)
{
- if (pVBoxSCSI->pBuf)
- RTMemFree(pVBoxSCSI->pBuf);
- pVBoxSCSI->pBuf = NULL;
+ if (pVBoxSCSI->pbBuf)
+ RTMemFree(pVBoxSCSI->pbBuf);
+ pVBoxSCSI->pbBuf = NULL;
pVBoxSCSI->cbBuf = 0;
pVBoxSCSI->cbCDB = 0;
pVBoxSCSI->iCDB = 0;
pVBoxSCSI->iBuf = 0;
pVBoxSCSI->uTargetDevice = 0;
pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
- memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
+ memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
}
+ pVBoxSCSI->rcCompletion = rcCompletion;
+
ASMAtomicXchgBool(&pVBoxSCSI->fBusy, false);
return VINF_SUCCESS;
@@ -347,24 +367,38 @@ int vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegiste
AssertMsg(iRegister == 1, ("Hey only register 1 can be read from with string\n"));
/* Accesses without a valid buffer will be ignored. */
- if (!pVBoxSCSI->pBuf)
+ if (!pVBoxSCSI->pbBuf)
return VINF_SUCCESS;
- int rc = PGMPhysSimpleDirtyWriteGCPtr(PDMDevHlpGetVMCPU(pDevIns), GCDst, pVBoxSCSI->pBuf, cbTransfer);
+ /* Also ignore attempts to read more data than is available. */
+ Assert(cbTransfer <= pVBoxSCSI->cbBuf);
+ if (cbTransfer > pVBoxSCSI->cbBuf)
+ cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
+
+ int rc = PGMPhysSimpleDirtyWriteGCPtr(PDMDevHlpGetVMCPU(pDevIns), GCDst, pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, cbTransfer);
AssertRC(rc);
*pGCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCDst + cbTransfer);
*pcTransfer = 0;
- RTMemFree(pVBoxSCSI->pBuf);
- pVBoxSCSI->pBuf = NULL;
- pVBoxSCSI->cbBuf = 0;
- pVBoxSCSI->cbCDB = 0;
- pVBoxSCSI->iCDB = 0;
- pVBoxSCSI->iBuf = 0;
- pVBoxSCSI->uTargetDevice = 0;
- pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
- memset(pVBoxSCSI->aCDB, 0, sizeof(pVBoxSCSI->aCDB));
+ /* Advance current buffer position. */
+ pVBoxSCSI->iBuf += cbTransfer;
+ pVBoxSCSI->cbBuf -= cbTransfer;
+
+ if (pVBoxSCSI->cbBuf == 0)
+ {
+ /** The guest read the last byte from the data in buffer.
+ * Clear everything and reset command buffer.
+ */
+ RTMemFree(pVBoxSCSI->pbBuf);
+ pVBoxSCSI->pbBuf = NULL;
+ pVBoxSCSI->cbCDB = 0;
+ pVBoxSCSI->iCDB = 0;
+ pVBoxSCSI->iBuf = 0;
+ pVBoxSCSI->uTargetDevice = 0;
+ pVBoxSCSI->enmState = VBOXSCSISTATE_NO_COMMAND;
+ memset(pVBoxSCSI->abCDB, 0, sizeof(pVBoxSCSI->abCDB));
+ }
return rc;
}
@@ -379,16 +413,20 @@ int vboxscsiWriteString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegist
AssertMsg(iRegister == 1, ("Hey only register 1 can be written to with string\n"));
/* Accesses without a valid buffer will be ignored. */
- if (!pVBoxSCSI->pBuf)
+ if (!pVBoxSCSI->pbBuf)
return VINF_SUCCESS;
- Assert(cbTransfer == pVBoxSCSI->cbBuf);
+ Assert(cbTransfer <= pVBoxSCSI->cbBuf);
if (cbTransfer > pVBoxSCSI->cbBuf)
cbTransfer = pVBoxSCSI->cbBuf; /* Ignore excess data (not supposed to happen). */
- int rc = PDMDevHlpPhysReadGCVirt(pDevIns, pVBoxSCSI->pBuf, GCSrc, cbTransfer);
+ int rc = PDMDevHlpPhysReadGCVirt(pDevIns, pVBoxSCSI->pbBuf + pVBoxSCSI->iBuf, GCSrc, cbTransfer);
AssertRC(rc);
+ /* Advance current buffer position. */
+ pVBoxSCSI->iBuf += cbTransfer;
+ pVBoxSCSI->cbBuf -= cbTransfer;
+
*pGCPtrSrc = (RTGCPTR)((RTGCUINTPTR)GCSrc + cbTransfer);
*pcTransfer = 0;
@@ -405,7 +443,6 @@ void vboxscsiSetRequestRedo(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest)
if (pVBoxSCSI->uTxDir == VBOXSCSI_TXDIR_FROM_DEVICE)
{
- AssertPtr(pVBoxSCSI->pBuf);
+ AssertPtr(pVBoxSCSI->pbBuf);
}
}
-
diff --git a/src/VBox/Devices/Storage/VBoxSCSI.h b/src/VBox/Devices/Storage/VBoxSCSI.h
index c30749a5..5d055698 100644
--- a/src/VBox/Devices/Storage/VBoxSCSI.h
+++ b/src/VBox/Devices/Storage/VBoxSCSI.h
@@ -1,12 +1,10 @@
/* $Id: VBoxSCSI.h $ */
/** @file
- *
- * VBox storage devices:
- * Simple SCSI interface for BIOS access
+ * VBox storage devices - Simple SCSI interface for BIOS access.
*/
/*
- * Copyright (C) 2006-2009 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -17,14 +15,14 @@
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
-/**
- * This is a simple interface to access SCSI devices from the BIOS
- * which is shared between the BusLogic and the LsiLogic
- * SCSI host adapters to simplify the BIOS part.
+/** @page pg_drv_scsi Simple SCSI interface for BIOS access.
+ *
+ * This is a simple interface to access SCSI devices from the BIOS which is
+ * shared between the BusLogic and the LsiLogic SCSI host adapters to simplify
+ * the BIOS part.
*
- * The BusLogic interface if available will be starting at port 0x330
- * and the LsiLogic starts at 0x340 and each will have a size of 3 ports.
- * The ports are used as described below:
+ * The first interface (if available) will be starting at port 0x430 and
+ * each will occupy 4 ports. The ports are used as described below:
*
* +--------+--------+----------+
* | Offset | Access | Purpose |
@@ -39,22 +37,29 @@
* +--------+--------+----------+
* | 2 | R/W | Detect |
* +--------+--------+----------+
+ * | 3 | Read | SCSI rc |
+ * +--------+--------+----------+
* | 3 | Write | Reset |
* +--------+--------+----------+
*
- * The register at port 0 receives the SCSI CDB issued from the driver when writing to it but
- * before writing the actual CDB the first write gives the size of the CDB in bytes.
+ * The register at port 0 receives the SCSI CDB issued from the driver when
+ * writing to it but before writing the actual CDB the first write gives the
+ * size of the CDB in bytes.
*
- * Reading the port at offset 0 gives status information about the adapter.
- * If the busy bit is set the adapter is processing a previous issued request if it is
+ * Reading the port at offset 0 gives status information about the adapter. If
+ * the busy bit is set the adapter is processing a previous issued request if it is
* cleared the command finished and the adapter can process another request.
- * The driver has to poll this bit because the adapter will not assert an IRQ for simplicity reasons.
+ * The driver has to poll this bit because the adapter will not assert an IRQ
+ * for simplicity reasons.
*
- * The register at offset 2 is to detect if a host adapter is available
- * If the driver writes a value to this port and gets the same value after reading it
+ * The register at offset 2 is to detect if a host adapter is available. If the
+ * driver writes a value to this port and gets the same value after reading it
* again the adapter is available.
*
- * This part has no R0 or GC components.
+ * Any write to the register at offset 3 causes the interface to be reset. A
+ * read returns the SCSI status code of the last operation.
+ *
+ * This part has no R0 or RC components.
*/
#ifndef ___Storage_VBoxSCSI_h
@@ -65,14 +70,15 @@
*******************************************************************************/
//#define DEBUG
#include <VBox/vmm/pdmdev.h>
+#include <VBox/scsi.h>
typedef enum VBOXSCSISTATE
{
VBOXSCSISTATE_NO_COMMAND = 0x00,
VBOXSCSISTATE_READ_TXDIR = 0x01,
- VBOXSCSISTATE_READ_CDB_SIZE = 0x02,
- VBOXSCSISTATE_READ_BUFFER_SIZE_LOW = 0x03,
- VBOXSCSISTATE_READ_BUFFER_SIZE_HIGH = 0x04,
+ VBOXSCSISTATE_READ_CDB_SIZE_BUFHI = 0x02,
+ VBOXSCSISTATE_READ_BUFFER_SIZE_LSB = 0x03,
+ VBOXSCSISTATE_READ_BUFFER_SIZE_MID = 0x04,
VBOXSCSISTATE_READ_COMMAND = 0x05,
VBOXSCSISTATE_COMMAND_READY = 0x06
} VBOXSCSISTATE;
@@ -94,7 +100,7 @@ typedef struct VBOXSCSI
/** The size of the CDB we are issuing. */
uint8_t cbCDB;
/** The command to issue. */
- uint8_t aCDB[12];
+ uint8_t abCDB[12];
/** Current position in the array. */
uint8_t iCDB;
@@ -103,18 +109,24 @@ typedef struct VBOXSCSI
#endif
/** Pointer to the buffer holding the data. */
- R3PTRTYPE(uint8_t *) pBuf;
+ R3PTRTYPE(uint8_t *) pbBuf;
/** Size of the buffer in bytes. */
uint32_t cbBuf;
- /** Current position in the buffer. */
+ /** Current position in the buffer (offBuf if you like). */
uint32_t iBuf;
+ /** The result code of last operation. */
+ int32_t rcCompletion;
+#if HC_ARCH_BITS == 64
+ uint32_t Alignment1;
+#endif
/** Flag whether a request is pending. */
volatile bool fBusy;
/** The state we are in when fetching a command from the BIOS. */
VBOXSCSISTATE enmState;
} VBOXSCSI, *PVBOXSCSI;
-#define VBOX_SCSI_BUSY RT_BIT(0)
+#define VBOX_SCSI_BUSY RT_BIT(0)
+#define VBOX_SCSI_ERROR RT_BIT(1)
#ifdef IN_RING3
RT_C_DECLS_BEGIN
@@ -122,13 +134,14 @@ int vboxscsiInitialize(PVBOXSCSI pVBoxSCSI);
int vboxscsiReadRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint32_t *pu32Value);
int vboxscsiWriteRegister(PVBOXSCSI pVBoxSCSI, uint8_t iRegister, uint8_t uVal);
int vboxscsiSetupRequest(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, uint32_t *puTargetDevice);
-int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest);
+int vboxscsiRequestFinished(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest, int rcCompletion);
void vboxscsiSetRequestRedo(PVBOXSCSI pVBoxSCSI, PPDMSCSIREQUEST pScsiRequest);
int vboxscsiWriteString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb);
int vboxscsiReadString(PPDMDEVINS pDevIns, PVBOXSCSI pVBoxSCSI, uint8_t iRegister,
RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb);
RT_C_DECLS_END
-#endif
+#endif /* IN_RING3 */
+
+#endif /* !___Storage_VBoxSCSI_h */
-#endif /* ___Storage_VBoxSCSI_h */
diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp
index 4f300716..75f1b491 100644
--- a/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp
+++ b/src/VBox/Devices/Storage/VSCSI/VSCSIDevice.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -101,7 +101,11 @@ static bool vscsiDeviceReqProcess(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVS
}
case SCSI_TEST_UNIT_READY:
{
- *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
+ if ( vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun)
+ && pVScsiDevice->papVScsiLun[pVScsiReq->iLun]->fReady)
+ *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
+ else
+ fProcessed = false; /* The LUN (if present) will provide details. */
break;
}
case SCSI_REQUEST_SENSE:
@@ -111,6 +115,7 @@ static bool vscsiDeviceReqProcess(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVS
*prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
else
*prcReq = vscsiReqSenseCmd(&pVScsiDevice->VScsiSense, pVScsiReq);
+ break;
}
default:
fProcessed = false;
@@ -343,4 +348,3 @@ VBOXDDU_DECL(int) VSCSIDeviceReqCreate(VSCSIDEVICE hVScsiDevice, PVSCSIREQ phVSc
return VINF_SUCCESS;
}
-
diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSIInternal.h b/src/VBox/Devices/Storage/VSCSI/VSCSIInternal.h
index 4fd696b0..8e086573 100644
--- a/src/VBox/Devices/Storage/VSCSI/VSCSIInternal.h
+++ b/src/VBox/Devices/Storage/VSCSI/VSCSIInternal.h
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -85,6 +85,10 @@ typedef struct VSCSILUNINT
PVSCSILUNIOCALLBACKS pVScsiLunIoCallbacks;
/** Pointer to the LUN type descriptor. */
PVSCSILUNDESC pVScsiLunDesc;
+ /** Flag indicating whether LUN is ready. */
+ bool fReady;
+ /** Flag indicating media presence in LUN. */
+ bool fMediaPresent;
/** Flags of supported features. */
uint64_t fFeatures;
/** I/O request processing data */
@@ -359,6 +363,34 @@ DECLINLINE(int) vscsiLunMediumGetSize(PVSCSILUNINT pVScsiLun, uint64_t *pcbSize)
}
/**
+ * Wrapper for the get medium sector size I/O callback.
+ *
+ * @returns VBox status code.
+ * @param pVScsiLun The LUN.
+ * @param pcbSectorSize Where to store the sector size on success.
+ */
+DECLINLINE(int) vscsiLunMediumGetSectorSize(PVSCSILUNINT pVScsiLun, uint32_t *pcbSectorSize)
+{
+ return pVScsiLun->pVScsiLunIoCallbacks->pfnVScsiLunMediumGetSectorSize(pVScsiLun,
+ pVScsiLun->pvVScsiLunUser,
+ pcbSectorSize);
+}
+
+/**
+ * Wrapper for the get medium lock/unlock I/O callback.
+ *
+ * @returns VBox status code.
+ * @param pVScsiLun The LUN.
+ * @param bool The new medium lock state.
+ */
+DECLINLINE(int) vscsiLunMediumSetLock(PVSCSILUNINT pVScsiLun, bool fLocked)
+{
+ return pVScsiLun->pVScsiLunIoCallbacks->pfnVScsiLunMediumSetLock(pVScsiLun,
+ pVScsiLun->pvVScsiLunUser,
+ fLocked);
+}
+
+/**
* Wrapper for the I/O request enqueue I/O callback.
*
* @returns VBox status code.
diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSILun.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSILun.cpp
index 9a2f1f7c..24462db0 100644
--- a/src/VBox/Devices/Storage/VSCSI/VSCSILun.cpp
+++ b/src/VBox/Devices/Storage/VSCSI/VSCSILun.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2010 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -115,3 +115,45 @@ VBOXDDU_DECL(int) VSCSILunDestroy(VSCSILUN hVScsiLun)
return VINF_SUCCESS;
}
+/**
+ * Notify virtual SCSI LUN of media being mounted.
+ *
+ * @returns VBox status code.
+ * @param hVScsiLun The virtual SCSI LUN
+ * mounting the medium.
+ */
+VBOXDDU_DECL(int) VSCSILunMountNotify(VSCSILUN hVScsiLun)
+{
+ PVSCSILUNINT pVScsiLun = (PVSCSILUNINT)hVScsiLun;
+
+ LogFlowFunc(("hVScsiLun=%p\n", hVScsiLun));
+ AssertPtrReturn(pVScsiLun, VERR_INVALID_HANDLE);
+ AssertReturn(vscsiIoReqOutstandingCountGet(pVScsiLun) == 0, VERR_VSCSI_LUN_BUSY);
+
+ /* Mark the LUN as not ready so that LUN specific code can do its job. */
+ pVScsiLun->fReady = false;
+ pVScsiLun->fMediaPresent = true;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Notify virtual SCSI LUN of media being unmounted.
+ *
+ * @returns VBox status code.
+ * @param hVScsiLun The virtual SCSI LUN
+ * mounting the medium.
+ */
+VBOXDDU_DECL(int) VSCSILunUnmountNotify(VSCSILUN hVScsiLun)
+{
+ PVSCSILUNINT pVScsiLun = (PVSCSILUNINT)hVScsiLun;
+
+ LogFlowFunc(("hVScsiLun=%p\n", hVScsiLun));
+ AssertPtrReturn(pVScsiLun, VERR_INVALID_HANDLE);
+ AssertReturn(vscsiIoReqOutstandingCountGet(pVScsiLun) == 0, VERR_VSCSI_LUN_BUSY);
+
+ pVScsiLun->fReady = false;
+ pVScsiLun->fMediaPresent = false;
+
+ return VINF_SUCCESS;
+}
diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp
index 6da3833b..10330cdf 100644
--- a/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp
+++ b/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -44,6 +44,116 @@ typedef struct VSCSILUNMMC
bool fLocked;
} VSCSILUNMMC, *PVSCSILUNMMC;
+
+DECLINLINE(void) mmcLBA2MSF(uint8_t *pbBuf, uint32_t iLBA)
+{
+ iLBA += 150;
+ pbBuf[0] = (iLBA / 75) / 60;
+ pbBuf[1] = (iLBA / 75) % 60;
+ pbBuf[2] = iLBA % 75;
+}
+
+DECLINLINE(uint32_t) mmcMSF2LBA(const uint8_t *pbBuf)
+{
+ return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
+}
+
+
+/* Fabricate normal TOC information. */
+static int mmcReadTOCNormal(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
+{
+ PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
+ uint8_t aReply[32];
+ uint8_t *pbBuf = aReply;
+ uint8_t *q;
+ uint8_t iStartTrack;
+ uint32_t cbSize;
+
+ iStartTrack = pVScsiReq->pbCDB[6];
+ if (iStartTrack > 1 && iStartTrack != 0xaa)
+ {
+ return vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
+ }
+ q = pbBuf + 2;
+ *q++ = 1; /* first session */
+ *q++ = 1; /* last session */
+ if (iStartTrack <= 1)
+ {
+ *q++ = 0; /* reserved */
+ *q++ = 0x14; /* ADR, CONTROL */
+ *q++ = 1; /* track number */
+ *q++ = 0; /* reserved */
+ if (fMSF)
+ {
+ *q++ = 0; /* reserved */
+ mmcLBA2MSF(q, 0);
+ q += 3;
+ }
+ else
+ {
+ /* sector 0 */
+ vscsiH2BEU32(q, 0);
+ q += 4;
+ }
+ }
+ /* lead out track */
+ *q++ = 0; /* reserved */
+ *q++ = 0x14; /* ADR, CONTROL */
+ *q++ = 0xaa; /* track number */
+ *q++ = 0; /* reserved */
+ if (fMSF)
+ {
+ *q++ = 0; /* reserved */
+ mmcLBA2MSF(q, pVScsiLunMmc->cSectors);
+ q += 3;
+ }
+ else
+ {
+ vscsiH2BEU32(q, pVScsiLunMmc->cSectors);
+ q += 4;
+ }
+ cbSize = q - pbBuf;
+ Assert(cbSize <= sizeof(aReply));
+ vscsiH2BEU16(pbBuf, cbSize - 2);
+ if (cbSize < cbMaxTransfer)
+ cbMaxTransfer = cbSize;
+
+ RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, cbMaxTransfer);
+
+ return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+}
+
+/* Fabricate session information. */
+static int mmcReadTOCMulti(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
+{
+ PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
+ uint8_t aReply[32];
+ uint8_t *pbBuf = aReply;
+
+ /* multi session: only a single session defined */
+ memset(pbBuf, 0, 12);
+ pbBuf[1] = 0x0a;
+ pbBuf[2] = 0x01; /* first complete session number */
+ pbBuf[3] = 0x01; /* last complete session number */
+ pbBuf[5] = 0x14; /* ADR, CONTROL */
+ pbBuf[6] = 1; /* first track in last complete session */
+
+ if (fMSF)
+ {
+ pbBuf[8] = 0; /* reserved */
+ mmcLBA2MSF(pbBuf + 8, 0);
+ }
+ else
+ {
+ /* sector 0 */
+ vscsiH2BEU32(pbBuf + 8, 0);
+ }
+
+ RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, 12);
+
+ return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+}
+
static int vscsiLunMmcInit(PVSCSILUNINT pVScsiLun)
{
PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
@@ -73,9 +183,40 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
uint32_t cSectorTransfer = 0;
int rc = VINF_SUCCESS;
int rcReq = SCSI_STATUS_OK;
+ unsigned uCmd = pVScsiReq->pbCDB[0];
- switch(pVScsiReq->pbCDB[0])
+ /*
+ * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands
+ * operate even when a unit attention condition exists for initiator; every other command
+ * needs to report CHECK CONDITION in that case.
+ */
+ if (!pVScsiLunMmc->Core.fReady && uCmd != SCSI_INQUIRY)
{
+ /*
+ * A note on media changes: As long as a medium is not present, the unit remains in
+ * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium
+ * is inserted; however, we internally keep the 'not ready' state until we've had
+ * a chance to report the UNIT ATTENTION status indicating a media change.
+ */
+ if (pVScsiLunMmc->Core.fMediaPresent)
+ {
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION,
+ SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00);
+ pVScsiLunMmc->Core.fReady = true;
+ }
+ else
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
+ SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
+ }
+ else
+ {
+ switch (uCmd)
+ {
+ case SCSI_TEST_UNIT_READY:
+ Assert(!pVScsiLunMmc->Core.fReady); /* Only should get here if LUN isn't ready. */
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
+ break;
+
case SCSI_INQUIRY:
{
SCSIINQUIRYDATA ScsiInquiryReply;
@@ -120,6 +261,7 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
uint8_t aReply[24];
uint8_t *pu8ReplyPos;
+ bool fValid = false;
memset(aReply, 0, sizeof(aReply));
aReply[0] = 4; /* Reply length 4. */
@@ -135,10 +277,18 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
*pu8ReplyPos++ = 0x08; /* Page code. */
*pu8ReplyPos++ = 0x12; /* Size of the page. */
*pu8ReplyPos++ = 0x4; /* Write cache enabled. */
+ fValid = true;
+ } else if (uModePage == 0) {
+ fValid = true;
}
- RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
- rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+ /* Querying unknown pages must fail. */
+ if (fValid) {
+ RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
+ rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+ } else {
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
+ }
break;
}
case SCSI_MODE_SELECT_6:
@@ -267,13 +417,37 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
{
pVScsiLunMmc->fLocked = pVScsiReq->pbCDB[4] & 1;
+ vscsiLunMediumSetLock(pVScsiLun, pVScsiLunMmc->fLocked);
rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
break;
}
+ case SCSI_READ_TOC_PMA_ATIP:
+ {
+ uint8_t format;
+ uint16_t cbMax;
+ bool fMSF;
+
+ format = pVScsiReq->pbCDB[2] & 0x0f;
+ cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
+ fMSF = (pVScsiReq->pbCDB[1] >> 1) & 1;
+ switch (format)
+ {
+ case 0x00:
+ mmcReadTOCNormal(pVScsiLun, pVScsiReq, cbMax, fMSF);
+ break;
+ case 0x01:
+ mmcReadTOCMulti(pVScsiLun, pVScsiReq, cbMax, fMSF);
+ break;
+ default:
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
+ }
+ break;
+ }
default:
- //AssertMsgFailed(("Command %#x [%s] not implemented\n", pRequest->pbCDB[0], SCSICmdText(pRequest->pbCDB[0])));
+ //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0])));
rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
+ }
}
if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
@@ -286,7 +460,7 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
}
- else if (!cSectorTransfer)
+ else if (!cSectorTransfer)
{
/* A 0 transfer length is not an error. */
rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSILunSbc.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSILunSbc.cpp
index 81b8ce25..d5a3e68f 100644
--- a/src/VBox/Devices/Storage/VSCSI/VSCSILunSbc.cpp
+++ b/src/VBox/Devices/Storage/VSCSI/VSCSILunSbc.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -32,7 +32,7 @@
#include "VSCSIInternal.h"
/** Maximum of amount of LBAs to unmap with one command. */
-#define VSCSI_UNMAP_LBAS_MAX ((10*_1M) / 512)
+#define VSCSI_UNMAP_LBAS_MAX(a_cbSector) ((10*_1M) / a_cbSector)
/**
* SBC LUN instance
@@ -41,6 +41,8 @@ typedef struct VSCSILUNSBC
{
/** Core LUN structure */
VSCSILUNINT Core;
+ /** Sector size of the medium. */
+ uint32_t cbSector;
/** Size of the virtual disk. */
uint64_t cSectors;
/** VPD page pool. */
@@ -56,9 +58,13 @@ static int vscsiLunSbcInit(PVSCSILUNINT pVScsiLun)
int rc = VINF_SUCCESS;
int cVpdPages = 0;
- rc = vscsiLunMediumGetSize(pVScsiLun, &cbDisk);
+ rc = vscsiLunMediumGetSectorSize(pVScsiLun, &pVScsiLunSbc->cbSector);
if (RT_SUCCESS(rc))
- pVScsiLunSbc->cSectors = cbDisk / 512; /* Fixed sector size */
+ {
+ rc = vscsiLunMediumGetSize(pVScsiLun, &cbDisk);
+ if (RT_SUCCESS(rc))
+ pVScsiLunSbc->cSectors = cbDisk / pVScsiLunSbc->cbSector;
+ }
if (RT_SUCCESS(rc))
rc = vscsiVpdPagePoolInit(&pVScsiLunSbc->VpdPagePool);
@@ -99,7 +105,7 @@ static int vscsiLunSbcInit(PVSCSILUNINT pVScsiLun)
pBlkPage->u32MaxTrfLength = 0;
pBlkPage->u32OptTrfLength = 0;
pBlkPage->u32MaxPreXdTrfLength = 0;
- pBlkPage->u32MaxUnmapLbaCount = RT_H2BE_U32(VSCSI_UNMAP_LBAS_MAX);
+ pBlkPage->u32MaxUnmapLbaCount = RT_H2BE_U32(VSCSI_UNMAP_LBAS_MAX(pVScsiLunSbc->cbSector));
pBlkPage->u32MaxUnmapBlkDescCount = UINT32_C(0xffffffff);
pBlkPage->u32OptUnmapGranularity = 0;
pBlkPage->u32UnmapGranularityAlignment = 0;
@@ -167,6 +173,9 @@ static int vscsiLunSbcInit(PVSCSILUNINT pVScsiLun)
}
}
+ /* For SBC LUNs, there will be no ready state transitions. */
+ pVScsiLunSbc->Core.fReady = true;
+
return rc;
}
@@ -242,7 +251,7 @@ static int vscsiLunSbcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
vscsiH2BEU32(aReply, UINT32_C(0xffffffff));
else
vscsiH2BEU32(aReply, pVScsiLunSbc->cSectors - 1);
- vscsiH2BEU32(&aReply[4], 512);
+ vscsiH2BEU32(&aReply[4], pVScsiLunSbc->cbSector);
RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
break;
@@ -252,6 +261,7 @@ static int vscsiLunSbcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
uint8_t aReply[24];
uint8_t *pu8ReplyPos;
+ bool fValid = false;
memset(aReply, 0, sizeof(aReply));
aReply[0] = 4; /* Reply length 4. */
@@ -270,10 +280,47 @@ static int vscsiLunSbcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
*pu8ReplyPos++ = 0x08; /* Page code. */
*pu8ReplyPos++ = 0x12; /* Size of the page. */
*pu8ReplyPos++ = 0x4; /* Write cache enabled. */
+ fValid = true;
+ } else if (uModePage == 0) {
+ fValid = true;
}
- RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
- rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+ /* Querying unknown pages must fail. */
+ if (fValid) {
+ RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
+ rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+ } else {
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
+ }
+ break;
+ }
+ case SCSI_MODE_SELECT_6:
+ {
+ uint8_t abParms[12];
+ size_t cbCopied;
+ size_t cbList = pVScsiReq->pbCDB[4];
+
+ /* Copy the parameters. */
+ cbCopied = RTSgBufCopyToBuf(&pVScsiReq->SgBuf, &abParms[0], sizeof(abParms));
+
+ /* Handle short LOGICAL BLOCK LENGTH parameter. */
+ if ( !(pVScsiReq->pbCDB[1] & 0x01)
+ && cbCopied == sizeof(abParms)
+ && cbList >= 12
+ && abParms[3] == 8)
+ {
+ uint32_t cbBlock;
+
+ cbBlock = vscsiBE2HU24(&abParms[4 + 5]);
+ Log2(("SBC: set LOGICAL BLOCK LENGTH to %u\n", cbBlock));
+ if (cbBlock == 512) /* Fixed block size. */
+ {
+ rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+ break;
+ }
+ }
+ /* Fail any other requests. */
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
break;
}
case SCSI_READ_6:
@@ -445,7 +492,7 @@ static int vscsiLunSbcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
&& cbCopied == sizeof(abHdr)
&& cbList >= 8)
{
- size_t cBlkDesc = vscsiBE2HU16(&abHdr[2]) / 16;
+ uint32_t cBlkDesc = vscsiBE2HU16(&abHdr[2]) / 16;
if (cBlkDesc)
{
diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSISense.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSISense.cpp
index c90cd673..673ee0b8 100644
--- a/src/VBox/Devices/Storage/VSCSI/VSCSISense.cpp
+++ b/src/VBox/Devices/Storage/VSCSI/VSCSISense.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -24,6 +24,12 @@
void vscsiSenseInit(PVSCSISENSE pVScsiSense)
{
memset(pVScsiSense->abSenseBuf, 0, sizeof(pVScsiSense->abSenseBuf));
+
+ /* Fill in valid sense information (can't be just zeros). */
+ pVScsiSense->abSenseBuf[0] = (1 << 7) | SCSI_SENSE_RESPONSE_CODE_CURR_FIXED; /* Fixed format */
+ pVScsiSense->abSenseBuf[2] = SCSI_SENSE_NONE;
+ pVScsiSense->abSenseBuf[7] = 10;
+ pVScsiSense->abSenseBuf[12] = SCSI_ASC_NONE;
}
int vscsiReqSenseOkSet(PVSCSISENSE pVScsiSense, PVSCSIREQINT pVScsiReq)
diff --git a/src/VBox/Devices/Storage/swab.h b/src/VBox/Devices/Storage/swab.h
index 66086fd4..32c80a2e 100644
--- a/src/VBox/Devices/Storage/swab.h
+++ b/src/VBox/Devices/Storage/swab.h
@@ -14,7 +14,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;