diff options
Diffstat (limited to 'src/VBox/Devices/Storage/DevAHCI.cpp')
| -rw-r--r-- | src/VBox/Devices/Storage/DevAHCI.cpp | 1893 |
1 files changed, 1041 insertions, 852 deletions
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, |
