DevATA.cpp revision d4bc81a2be43a3c11f479f600c48581f6d496ac8
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync/* $Id$ */
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync/** @file
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync * VBox storage devices: ATA/ATAPI controller device (disk and cdrom).
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync */
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync/*
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * Copyright (C) 2006-2008 Sun Microsystems, Inc.
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync *
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * available from http://www.virtualbox.org. This file is free software;
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * you can redistribute it and/or modify it under the terms of the GNU
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * General Public License (GPL) as published by the Free Software
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync *
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * additional information or have any questions.
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync */
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync/*******************************************************************************
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync* Defined Constants And Macros *
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync*******************************************************************************/
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync/** Temporary instrumentation for tracking down potential virtual disk
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * write performance issues. */
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync#undef VBOX_INSTRUMENT_DMA_WRITES
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync/**
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * The SSM saved state versions.
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync#define ATA_SAVED_STATE_VERSION 17
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync#define ATA_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE 16
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync/*******************************************************************************
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync* Header Files *
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync*******************************************************************************/
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync#define LOG_GROUP LOG_GROUP_DEV_IDE
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync#include <VBox/pdmdev.h>
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync#include <iprt/assert.h>
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync#include <iprt/string.h>
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync#ifdef IN_RING3
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync# include <iprt/uuid.h>
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync# include <iprt/semaphore.h>
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync# include <iprt/thread.h>
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync# include <iprt/time.h>
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync# include <iprt/alloc.h>
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync#endif /* IN_RING3 */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync#include <iprt/critsect.h>
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync#include <iprt/asm.h>
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync#include <VBox/stam.h>
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync#include <VBox/mm.h>
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync#include <VBox/pgm.h>
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync#include <VBox/scsi.h>
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync#include "PIIX3ATABmDma.h"
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync#include "ide.h"
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync#include "ATAController.h"
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync#include "../Builtins.h"
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsynctypedef struct PCIATAState {
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync PCIDEVICE dev;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync /** The controllers. */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ATACONTROLLER aCts[2];
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync /** Pointer to device instance. */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync PPDMDEVINSR3 pDevIns;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync /** Status Port - Base interface. */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync PDMIBASE IBase;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /** Status Port - Leds interface. */
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync PDMILEDPORTS ILeds;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /** Partner of ILeds. */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /** Flag whether GC is enabled. */
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync bool fGCEnabled;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync /** Flag whether R0 is enabled. */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync bool fR0Enabled;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /** Flag indicating whether PIIX4 or PIIX3 is being emulated. */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync bool fPIIX4;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync bool Alignment0[HC_ARCH_BITS == 64 ? 5 : 1]; /**< Align the struct size. */
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync} PCIATAState;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync#define PDMIBASE_2_PCIATASTATE(pInterface) ( (PCIATAState *)((uintptr_t)(pInterface) - RT_OFFSETOF(PCIATAState, IBase)) )
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync#define PDMILEDPORTS_2_PCIATASTATE(pInterface) ( (PCIATAState *)((uintptr_t)(pInterface) - RT_OFFSETOF(PCIATAState, ILeds)) )
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync#define PDMIBLOCKPORT_2_ATASTATE(pInterface) ( (ATADevState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ATADevState, IPort)) )
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync#define PDMIMOUNT_2_ATASTATE(pInterface) ( (ATADevState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ATADevState, IMount)) )
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync#define PDMIMOUNTNOTIFY_2_ATASTATE(pInterface) ( (ATADevState *)((uintptr_t)(pInterface) - RT_OFFSETOF(ATADevState, IMountNotify)) )
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync#define PCIDEV_2_PCIATASTATE(pPciDev) ( (PCIATAState *)(pPciDev) )
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync#define ATACONTROLLER_IDX(pController) ( (pController) - PDMINS_2_DATA(CONTROLLER_2_DEVINS(pController), PCIATAState *)->aCts )
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync#ifndef VBOX_DEVICE_STRUCT_TESTCASE
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync/*******************************************************************************
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * Internal Functions *
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ******************************************************************************/
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync__BEGIN_DECLS
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsyncPDMBOTHCBDECL(int) ataIOPortWrite1(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncPDMBOTHCBDECL(int) ataIOPortRead1(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *u32, unsigned cb);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncPDMBOTHCBDECL(int) ataIOPortWriteStr1(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncPDMBOTHCBDECL(int) ataIOPortReadStr1(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncPDMBOTHCBDECL(int) ataIOPortWrite2(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
0c94a8282c9042b02f022302a3d987746140eab9vboxsyncPDMBOTHCBDECL(int) ataIOPortRead2(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *u32, unsigned cb);
0c94a8282c9042b02f022302a3d987746140eab9vboxsyncPDMBOTHCBDECL(int) ataBMDMAIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
0c94a8282c9042b02f022302a3d987746140eab9vboxsyncPDMBOTHCBDECL(int) ataBMDMAIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
0c94a8282c9042b02f022302a3d987746140eab9vboxsync__END_DECLS
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsyncDECLINLINE(void) ataSetStatusValue(ATADevState *s, uint8_t stat)
0c94a8282c9042b02f022302a3d987746140eab9vboxsync{
0c94a8282c9042b02f022302a3d987746140eab9vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync /* Freeze status register contents while processing RESET. */
0c94a8282c9042b02f022302a3d987746140eab9vboxsync if (!pCtl->fReset)
0c94a8282c9042b02f022302a3d987746140eab9vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->uATARegStatus = stat;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, s->iLUN, s->uATARegStatus));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync}
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsyncDECLINLINE(void) ataSetStatus(ATADevState *s, uint8_t stat)
0c94a8282c9042b02f022302a3d987746140eab9vboxsync{
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync /* Freeze status register contents while processing RESET. */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync if (!pCtl->fReset)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync {
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync s->uATARegStatus |= stat;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, s->iLUN, s->uATARegStatus));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync}
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncDECLINLINE(void) ataUnsetStatus(ATADevState *s, uint8_t stat)
56970d3a1944c7c073d66266cd52449835221badvboxsync{
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync /* Freeze status register contents while processing RESET. */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync if (!pCtl->fReset)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync s->uATARegStatus &= ~stat;
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, s->iLUN, s->uATARegStatus));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync}
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync#ifdef IN_RING3
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsynctypedef void (*PBeginTransferFunc)(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsynctypedef bool (*PSourceSinkFunc)(ATADevState *);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic void ataReadWriteSectorsBT(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic void ataPacketBT(ATADevState *);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsyncstatic void atapiCmdBT(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic void atapiPassthroughCmdBT(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool ataIdentifySS(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool ataFlushSS(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool ataReadSectorsSS(ATADevState *);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsyncstatic bool ataWriteSectorsSS(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool ataExecuteDeviceDiagnosticSS(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool ataPacketSS(ATADevState *);
f9cdd92d151d9c28eb0f1aed25863fc04f85691dvboxsyncstatic bool atapiGetConfigurationSS(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool atapiIdentifySS(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool atapiInquirySS(ATADevState *);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsyncstatic bool atapiMechanismStatusSS(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool atapiModeSenseErrorRecoverySS(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool atapiModeSenseCDStatusSS(ATADevState *);
f9cdd92d151d9c28eb0f1aed25863fc04f85691dvboxsyncstatic bool atapiReadSS(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool atapiReadCapacitySS(ATADevState *);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool atapiReadDiscInformationSS(ATADevState *);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsyncstatic bool atapiReadTOCNormalSS(ATADevState *);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsyncstatic bool atapiReadTOCMultiSS(ATADevState *);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsyncstatic bool atapiReadTOCRawSS(ATADevState *);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsyncstatic bool atapiReadTrackInformationSS(ATADevState *);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsyncstatic bool atapiRequestSenseSS(ATADevState *);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsyncstatic bool atapiPassthroughSS(ATADevState *);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync/**
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync * Begin of transfer function indexes for g_apfnBeginTransFuncs.
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync */
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsynctypedef enum ATAFNBT
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync{
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync ATAFN_BT_NULL = 0,
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync ATAFN_BT_READ_WRITE_SECTORS,
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ATAFN_BT_PACKET,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ATAFN_BT_ATAPI_CMD,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ATAFN_BT_ATAPI_PASSTHROUGH_CMD,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ATAFN_BT_MAX
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync} ATAFNBT;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync/**
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * Array of end transfer functions, the index is ATAFNET.
1969e98a26e5b56b67fbe3b6bfa007f8f09e86ebvboxsync * Make sure ATAFNET and this array match!
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync */
81614fc60e096e714022d10d38b70a36b9b21d48vboxsyncstatic const PBeginTransferFunc g_apfnBeginTransFuncs[ATAFN_BT_MAX] =
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync{
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync NULL,
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync ataReadWriteSectorsBT,
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataPacketBT,
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync atapiCmdBT,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync atapiPassthroughCmdBT,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync};
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync/**
0c94a8282c9042b02f022302a3d987746140eab9vboxsync * Source/sink function indexes for g_apfnSourceSinkFuncs.
0c94a8282c9042b02f022302a3d987746140eab9vboxsync */
0c94a8282c9042b02f022302a3d987746140eab9vboxsynctypedef enum ATAFNSS
0c94a8282c9042b02f022302a3d987746140eab9vboxsync{
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_NULL = 0,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_IDENTIFY,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_FLUSH,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_READ_SECTORS,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_WRITE_SECTORS,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_EXECUTE_DEVICE_DIAGNOSTIC,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_PACKET,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_GET_CONFIGURATION,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_IDENTIFY,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_INQUIRY,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_MECHANISM_STATUS,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_READ,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_READ_CAPACITY,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_READ_DISC_INFORMATION,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_READ_TOC_NORMAL,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_READ_TOC_MULTI,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_READ_TOC_RAW,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_READ_TRACK_INFORMATION,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_REQUEST_SENSE,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_ATAPI_PASSTHROUGH,
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ATAFN_SS_MAX
0c94a8282c9042b02f022302a3d987746140eab9vboxsync} ATAFNSS;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync/**
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync * Array of source/sink functions, the index is ATAFNSS.
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync * Make sure ATAFNSS and this array match!
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync */
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic const PSourceSinkFunc g_apfnSourceSinkFuncs[ATAFN_SS_MAX] =
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync NULL,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ataIdentifySS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ataFlushSS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ataReadSectorsSS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ataWriteSectorsSS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ataExecuteDeviceDiagnosticSS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ataPacketSS,
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync atapiGetConfigurationSS,
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync atapiIdentifySS,
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync atapiInquirySS,
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync atapiMechanismStatusSS,
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync atapiModeSenseErrorRecoverySS,
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync atapiModeSenseCDStatusSS,
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync atapiReadSS,
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiReadCapacitySS,
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiReadDiscInformationSS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync atapiReadTOCNormalSS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync atapiReadTOCMultiSS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync atapiReadTOCRawSS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync atapiReadTrackInformationSS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync atapiRequestSenseSS,
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync atapiPassthroughSS
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync};
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic const ATARequest ataDMARequest = { ATA_AIO_DMA, };
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic const ATARequest ataPIORequest = { ATA_AIO_PIO, };
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic const ATARequest ataResetARequest = { ATA_AIO_RESET_ASSERTED, };
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic const ATARequest ataResetCRequest = { ATA_AIO_RESET_CLEARED, };
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic void ataAsyncIOClearRequests(PATACONTROLLER pCtl)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync int rc;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync AssertRC(rc);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pCtl->AsyncIOReqHead = 0;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pCtl->AsyncIOReqTail = 0;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync AssertRC(rc);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync}
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic void ataAsyncIOPutRequest(PATACONTROLLER pCtl, const ATARequest *pReq)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync{
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync int rc;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync AssertRC(rc);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync Assert((pCtl->AsyncIOReqHead + 1) % RT_ELEMENTS(pCtl->aAsyncIORequests) != pCtl->AsyncIOReqTail);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync memcpy(&pCtl->aAsyncIORequests[pCtl->AsyncIOReqHead], pReq, sizeof(*pReq));
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pCtl->AsyncIOReqHead++;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pCtl->AsyncIOReqHead %= RT_ELEMENTS(pCtl->aAsyncIORequests);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
56970d3a1944c7c073d66266cd52449835221badvboxsync AssertRC(rc);
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync LogBird(("ata: %x: signalling\n", pCtl->IOPortBase1));
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync rc = PDMR3CritSectScheduleExitEvent(&pCtl->lock, pCtl->AsyncIOSem);
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync if (RT_FAILURE(rc))
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync {
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync LogBird(("ata: %x: schedule failed, rc=%Rrc\n", pCtl->IOPortBase1, rc));
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync rc = RTSemEventSignal(pCtl->AsyncIOSem);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync AssertRC(rc);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync }
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync}
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic const ATARequest *ataAsyncIOGetCurrentRequest(PATACONTROLLER pCtl)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync int rc;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync const ATARequest *pReq;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync AssertRC(rc);
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync if (pCtl->AsyncIOReqHead != pCtl->AsyncIOReqTail)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pReq = &pCtl->aAsyncIORequests[pCtl->AsyncIOReqTail];
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync else
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pReq = NULL;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync AssertRC(rc);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync return pReq;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync}
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync/**
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync * Remove the request with the given type, as it's finished. The request
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync * is not removed blindly, as this could mean a RESET request that is not
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync * yet processed (but has cleared the request queue) is lost.
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync *
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync * @param pCtl Controller for which to remove the request.
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync * @param ReqType Type of the request to remove.
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync */
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic void ataAsyncIORemoveCurrentRequest(PATACONTROLLER pCtl, ATAAIO ReqType)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync int rc;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync AssertRC(rc);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (pCtl->AsyncIOReqHead != pCtl->AsyncIOReqTail && pCtl->aAsyncIORequests[pCtl->AsyncIOReqTail].ReqType == ReqType)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync {
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync pCtl->AsyncIOReqTail++;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pCtl->AsyncIOReqTail %= RT_ELEMENTS(pCtl->aAsyncIORequests);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync AssertRC(rc);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync}
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync/**
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync * Dump the request queue for a particular controller. First dump the queue
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync * contents, then the already processed entries, as long as they haven't been
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync * overwritten.
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync *
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync * @param pCtl Controller for which to dump the queue.
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic void ataAsyncIODumpRequests(PATACONTROLLER pCtl)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync{
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync int rc;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint8_t curr;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync AssertRC(rc);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("PIIX3 ATA: Ctl#%d: request queue dump (topmost is current):\n", ATACONTROLLER_IDX(pCtl)));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync curr = pCtl->AsyncIOReqTail;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync do
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (curr == pCtl->AsyncIOReqHead)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("PIIX3 ATA: Ctl#%d: processed requests (topmost is oldest):\n", ATACONTROLLER_IDX(pCtl)));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync switch (pCtl->aAsyncIORequests[curr].ReqType)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case ATA_AIO_NEW:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("new transfer request, iIf=%d iBeginTransfer=%d iSourceSink=%d cbTotalTransfer=%d uTxDir=%d\n", pCtl->aAsyncIORequests[curr].u.t.iIf, pCtl->aAsyncIORequests[curr].u.t.iBeginTransfer, pCtl->aAsyncIORequests[curr].u.t.iSourceSink, pCtl->aAsyncIORequests[curr].u.t.cbTotalTransfer, pCtl->aAsyncIORequests[curr].u.t.uTxDir));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case ATA_AIO_DMA:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("dma transfer finished\n"));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case ATA_AIO_PIO:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("pio transfer finished\n"));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case ATA_AIO_RESET_ASSERTED:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("reset asserted request\n"));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case ATA_AIO_RESET_CLEARED:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("reset cleared request\n"));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case ATA_AIO_ABORT:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("abort request, iIf=%d fResetDrive=%d\n", pCtl->aAsyncIORequests[curr].u.a.iIf, pCtl->aAsyncIORequests[curr].u.a.fResetDrive));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync default:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("unknown request %d\n", pCtl->aAsyncIORequests[curr].ReqType));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync curr = (curr + 1) % RT_ELEMENTS(pCtl->aAsyncIORequests);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync } while (curr != pCtl->AsyncIOReqTail);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync AssertRC(rc);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync}
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync/**
0c94a8282c9042b02f022302a3d987746140eab9vboxsync * Checks whether the request queue for a particular controller is empty
0c94a8282c9042b02f022302a3d987746140eab9vboxsync * or whether a particular controller is idle.
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *
0c94a8282c9042b02f022302a3d987746140eab9vboxsync * @param pCtl Controller for which to check the queue.
0c94a8282c9042b02f022302a3d987746140eab9vboxsync * @param fStrict If set then the controller is checked to be idle.
0c94a8282c9042b02f022302a3d987746140eab9vboxsync */
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsyncstatic bool ataAsyncIOIsIdle(PATACONTROLLER pCtl, bool fStrict)
0c94a8282c9042b02f022302a3d987746140eab9vboxsync{
0c94a8282c9042b02f022302a3d987746140eab9vboxsync int rc;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync bool fIdle;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync rc = RTSemMutexRequest(pCtl->AsyncIORequestMutex, RT_INDEFINITE_WAIT);
0c94a8282c9042b02f022302a3d987746140eab9vboxsync AssertRC(rc);
0c94a8282c9042b02f022302a3d987746140eab9vboxsync fIdle = pCtl->fRedoIdle;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync if (!fIdle)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync fIdle = (pCtl->AsyncIOReqHead == pCtl->AsyncIOReqTail);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync if (fStrict)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync fIdle &= (pCtl->uAsyncIOState == ATA_AIO_NEW);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync rc = RTSemMutexRelease(pCtl->AsyncIORequestMutex);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync AssertRC(rc);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync return fIdle;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync}
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync/**
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync * Send a transfer request to the async I/O thread.
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync *
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync * @param s Pointer to the ATA device state data.
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync * @param cbTotalTransfer Data transfer size.
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync * @param uTxDir Data transfer direction.
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * @param iBeginTransfer Index of BeginTransfer callback.
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * @param iSourceSink Index of SourceSink callback.
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * @param fChainedTransfer Whether this is a transfer that is part of the previous command/transfer.
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsyncstatic void ataStartTransfer(ATADevState *s, uint32_t cbTotalTransfer, uint8_t uTxDir, ATAFNBT iBeginTransfer, ATAFNSS iSourceSink, bool fChainedTransfer)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync{
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync ATARequest Req;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync Assert(PDMCritSectIsOwner(&pCtl->lock));
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync /* Do not issue new requests while the RESET line is asserted. */
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (pCtl->fReset)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync Log2(("%s: Ctl#%d: suppressed new request as RESET is active\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync return;
f9cdd92d151d9c28eb0f1aed25863fc04f85691dvboxsync }
f9cdd92d151d9c28eb0f1aed25863fc04f85691dvboxsync
f9cdd92d151d9c28eb0f1aed25863fc04f85691dvboxsync /* If the controller is already doing something else right now, ignore
f9cdd92d151d9c28eb0f1aed25863fc04f85691dvboxsync * the command that is being submitted. Some broken guests issue commands
f9cdd92d151d9c28eb0f1aed25863fc04f85691dvboxsync * twice (e.g. the Linux kernel that comes with Acronis True Image 8). */
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (!fChainedTransfer && !ataAsyncIOIsIdle(pCtl, true))
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync Log(("%s: Ctl#%d: ignored command %#04x, controller state %d\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl), s->uATARegCommand, pCtl->uAsyncIOState));
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync LogRel(("PIIX3 IDE: guest issued command %#04x while controller busy\n", s->uATARegCommand));
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync return;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync }
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync Req.ReqType = ATA_AIO_NEW;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (fChainedTransfer)
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync Req.u.t.iIf = pCtl->iAIOIf;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync else
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync Req.u.t.iIf = pCtl->iSelectedIf;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync Req.u.t.cbTotalTransfer = cbTotalTransfer;
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync Req.u.t.uTxDir = uTxDir;
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync Req.u.t.iBeginTransfer = iBeginTransfer;
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync Req.u.t.iSourceSink = iSourceSink;
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync ataSetStatusValue(s, ATA_STAT_BUSY);
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync pCtl->fChainedTransfer = fChainedTransfer;
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync /*
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync * Kick the worker thread into action.
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync */
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync Log2(("%s: Ctl#%d: message to async I/O thread, new request\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync ataAsyncIOPutRequest(pCtl, &Req);
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync}
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync/**
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync * Send an abort command request to the async I/O thread.
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync *
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync * @param s Pointer to the ATA device state data.
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync * @param fResetDrive Whether to reset the drive or just abort a command.
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync */
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsyncstatic void ataAbortCurrentCommand(ATADevState *s, bool fResetDrive)
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync{
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync ATARequest Req;
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync Assert(PDMCritSectIsOwner(&pCtl->lock));
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync /* Do not issue new requests while the RESET line is asserted. */
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync if (pCtl->fReset)
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync {
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync Log2(("%s: Ctl#%d: suppressed aborting command as RESET is active\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync return;
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync }
1379dfd407ada5fab15655776896f13b61a951fdvboxsync
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync Req.ReqType = ATA_AIO_ABORT;
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync Req.u.a.iIf = pCtl->iSelectedIf;
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync Req.u.a.fResetDrive = fResetDrive;
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync ataSetStatus(s, ATA_STAT_BUSY);
1379dfd407ada5fab15655776896f13b61a951fdvboxsync Log2(("%s: Ctl#%d: message to async I/O thread, abort command on LUN#%d\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl), s->iLUN));
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync ataAsyncIOPutRequest(pCtl, &Req);
f9cdd92d151d9c28eb0f1aed25863fc04f85691dvboxsync}
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsyncstatic void ataSetIRQ(ATADevState *s)
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync{
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync if (!(s->uATARegDevCtl & ATA_DEVCTL_DISABLE_IRQ))
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync {
1379dfd407ada5fab15655776896f13b61a951fdvboxsync Log2(("%s: LUN#%d asserting IRQ\n", __FUNCTION__, s->iLUN));
1379dfd407ada5fab15655776896f13b61a951fdvboxsync /* The BMDMA unit unconditionally sets BM_STATUS_INT if the interrupt
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync * line is asserted. It monitors the line for a rising edge. */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync if (!s->fIrqPending)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pCtl->BmDma.u8Status |= BM_STATUS_INT;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync /* Only actually set the IRQ line if updating the currently selected drive. */
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (s == &pCtl->aIfs[pCtl->iSelectedIf])
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync /** @todo experiment with adaptive IRQ delivery: for reads it is
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync * better to wait for IRQ delivery, as it reduces latency. */
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync if (pCtl->irq == 16)
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync PDMDevHlpPCISetIrqNoWait(pDevIns, 0, 1);
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync else
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync PDMDevHlpISASetIrqNoWait(pDevIns, pCtl->irq, 1);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync }
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync }
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync s->fIrqPending = true;
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync}
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync#endif /* IN_RING3 */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsyncstatic void ataUnsetIRQ(ATADevState *s)
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync{
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync if (!(s->uATARegDevCtl & ATA_DEVCTL_DISABLE_IRQ))
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync {
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync Log2(("%s: LUN#%d deasserting IRQ\n", __FUNCTION__, s->iLUN));
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync /* Only actually unset the IRQ line if updating the currently selected drive. */
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync if (s == &pCtl->aIfs[pCtl->iSelectedIf])
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync {
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync if (pCtl->irq == 16)
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync PDMDevHlpPCISetIrqNoWait(pDevIns, 0, 0);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync else
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync PDMDevHlpISASetIrqNoWait(pDevIns, pCtl->irq, 0);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync }
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync }
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync s->fIrqPending = false;
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync}
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync#ifdef IN_RING3
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsyncstatic void ataPIOTransferStart(ATADevState *s, uint32_t start, uint32_t size)
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync{
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync Log2(("%s: LUN#%d start %d size %d\n", __FUNCTION__, s->iLUN, start, size));
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync s->iIOBufferPIODataStart = start;
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync s->iIOBufferPIODataEnd = start + size;
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync ataSetStatus(s, ATA_STAT_DRQ);
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync}
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsyncstatic void ataPIOTransferStop(ATADevState *s)
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync{
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync Log2(("%s: LUN#%d\n", __FUNCTION__, s->iLUN));
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync if (s->fATAPITransfer)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector));
a66ec044d2a64d926996cd24da5faadccb070be3vboxsync ataSetIRQ(s);
a66ec044d2a64d926996cd24da5faadccb070be3vboxsync s->fATAPITransfer = false;
a66ec044d2a64d926996cd24da5faadccb070be3vboxsync }
a66ec044d2a64d926996cd24da5faadccb070be3vboxsync s->cbTotalTransfer = 0;
a66ec044d2a64d926996cd24da5faadccb070be3vboxsync s->cbElementaryTransfer = 0;
a66ec044d2a64d926996cd24da5faadccb070be3vboxsync s->iIOBufferPIODataStart = 0;
a66ec044d2a64d926996cd24da5faadccb070be3vboxsync s->iIOBufferPIODataEnd = 0;
a66ec044d2a64d926996cd24da5faadccb070be3vboxsync s->iBeginTransfer = ATAFN_BT_NULL;
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync s->iSourceSink = ATAFN_SS_NULL;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync}
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsyncstatic void ataPIOTransferLimitATAPI(ATADevState *s)
0c94a8282c9042b02f022302a3d987746140eab9vboxsync{
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync uint32_t cbLimit, cbTransfer;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync cbLimit = s->uATARegLCyl | (s->uATARegHCyl << 8);
0c94a8282c9042b02f022302a3d987746140eab9vboxsync /* Use maximum transfer size if the guest requested 0. Avoids a hang. */
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync if (cbLimit == 0)
0c94a8282c9042b02f022302a3d987746140eab9vboxsync cbLimit = 0xfffe;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync Log2(("%s: byte count limit=%d\n", __FUNCTION__, cbLimit));
0c94a8282c9042b02f022302a3d987746140eab9vboxsync if (cbLimit == 0xffff)
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync cbLimit--;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync cbTransfer = RT_MIN(s->cbTotalTransfer, s->iIOBufferEnd - s->iIOBufferCur);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync if (cbTransfer > cbLimit)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync /* Byte count limit for clipping must be even in this case */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync if (cbLimit & 1)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync cbLimit--;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync cbTransfer = cbLimit;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync }
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegLCyl = cbTransfer;
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync s->uATARegHCyl = cbTransfer >> 8;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync s->cbElementaryTransfer = cbTransfer;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync}
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsyncstatic uint32_t ataGetNSectors(ATADevState *s)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync /* 0 means either 256 (LBA28) or 65536 (LBA48) sectors. */
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (s->fLBA48)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (!s->uATARegNSector && !s->uATARegNSectorHOB)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync return 65536;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync else
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync return s->uATARegNSectorHOB << 8 | s->uATARegNSector;
20f21077abf35d7b7b618acb159267933907407fvboxsync }
20f21077abf35d7b7b618acb159267933907407fvboxsync else
20f21077abf35d7b7b618acb159267933907407fvboxsync {
0d8c2135d15345cc68111eea91052cdf5518d7e3vboxsync if (!s->uATARegNSector)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync return 256;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync else
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync return s->uATARegNSector;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync }
20f21077abf35d7b7b618acb159267933907407fvboxsync}
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsyncstatic void ataPadString(uint8_t *pbDst, const char *pbSrc, uint32_t cbSize)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync for (uint32_t i = 0; i < cbSize; i++)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (*pbSrc)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pbDst[i ^ 1] = *pbSrc++;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync else
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pbDst[i ^ 1] = ' ';
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync }
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync}
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic void ataSCSIPadStr(uint8_t *pbDst, const char *pbSrc, uint32_t cbSize)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync for (uint32_t i = 0; i < cbSize; i++)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (*pbSrc)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pbDst[i] = *pbSrc++;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync else
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pbDst[i] = ' ';
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync }
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync}
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncDECLINLINE(void) ataH2BE_U16(uint8_t *pbBuf, uint16_t val)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pbBuf[0] = val >> 8;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pbBuf[1] = val;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync}
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncDECLINLINE(void) ataH2BE_U24(uint8_t *pbBuf, uint32_t val)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync{
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync pbBuf[0] = val >> 16;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pbBuf[1] = val >> 8;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync pbBuf[2] = val;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync}
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsyncDECLINLINE(void) ataH2BE_U32(uint8_t *pbBuf, uint32_t val)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync{
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync pbBuf[0] = val >> 24;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync pbBuf[1] = val >> 16;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync pbBuf[2] = val >> 8;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pbBuf[3] = val;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync}
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsyncDECLINLINE(uint16_t) ataBE2H_U16(const uint8_t *pbBuf)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync return (pbBuf[0] << 8) | pbBuf[1];
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync}
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsyncDECLINLINE(uint32_t) ataBE2H_U24(const uint8_t *pbBuf)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2];
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync}
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsyncDECLINLINE(uint32_t) ataBE2H_U32(const uint8_t *pbBuf)
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync{
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3];
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync}
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncDECLINLINE(void) ataLBA2MSF(uint8_t *pbBuf, uint32_t iATAPILBA)
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync{
20f21077abf35d7b7b618acb159267933907407fvboxsync iATAPILBA += 150;
20f21077abf35d7b7b618acb159267933907407fvboxsync pbBuf[0] = (iATAPILBA / 75) / 60;
20f21077abf35d7b7b618acb159267933907407fvboxsync pbBuf[1] = (iATAPILBA / 75) % 60;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync pbBuf[2] = iATAPILBA % 75;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync}
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsyncDECLINLINE(uint32_t) ataMSF2LBA(const uint8_t *pbBuf)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync{
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync}
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic void ataCmdOK(ATADevState *s, uint8_t status)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync{
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync s->uATARegError = 0; /* Not needed by ATA spec, but cannot hurt. */
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ataSetStatusValue(s, ATA_STAT_READY | status);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync}
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsyncstatic void ataCmdError(ATADevState *s, uint8_t uErrorCode)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync{
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync Log(("%s: code=%#x\n", __FUNCTION__, uErrorCode));
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync s->uATARegError = uErrorCode;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync ataSetStatusValue(s, ATA_STAT_READY | ATA_STAT_ERR);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync s->cbTotalTransfer = 0;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync s->cbElementaryTransfer = 0;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync s->iIOBufferCur = 0;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync s->iIOBufferEnd = 0;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync s->uTxDir = PDMBLOCKTXDIR_NONE;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync s->iBeginTransfer = ATAFN_BT_NULL;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync s->iSourceSink = ATAFN_SS_NULL;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync}
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsyncstatic bool ataIdentifySS(ATADevState *s)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync{
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync uint16_t *p;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync char aSerial[20];
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync int rc;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync RTUUID Uuid;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync Assert(s->cbElementaryTransfer == 512);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync rc = s->pDrvBlock ? s->pDrvBlock->pfnGetUuid(s->pDrvBlock, &Uuid) : RTUuidClear(&Uuid);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (RT_FAILURE(rc) || RTUuidIsNull(&Uuid))
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync /* Generate a predictable serial for drives which don't have a UUID. */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync RTStrPrintf(aSerial, sizeof(aSerial), "VB%x-%04x%04x",
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync s->iLUN + ATADEVSTATE_2_DEVINS(s)->iInstance * 32,
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync pCtl->IOPortBase1, pCtl->IOPortBase2);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync }
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync else
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync RTStrPrintf(aSerial, sizeof(aSerial), "VB%08x-%08x", Uuid.au32[0], Uuid.au32[3]);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p = (uint16_t *)s->CTX_SUFF(pbIOBuffer);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync memset(p, 0, 512);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[0] = RT_H2LE_U16(0x0040);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[1] = RT_H2LE_U16(RT_MIN(s->PCHSGeometry.cCylinders, 16383));
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[3] = RT_H2LE_U16(s->PCHSGeometry.cHeads);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync /* Block size; obsolete, but required for the BIOS. */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[5] = RT_H2LE_U16(512);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[6] = RT_H2LE_U16(s->PCHSGeometry.cSectors);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync ataPadString((uint8_t *)(p + 10), aSerial, 20); /* serial number */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[21] = RT_H2LE_U16(512); /* XXX: retired, cache size in sectors */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[22] = RT_H2LE_U16(0); /* ECC bytes per sector */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync ataPadString((uint8_t *)(p + 23), "1.0", 8); /* firmware version */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync ataPadString((uint8_t *)(p + 27), "VBOX HARDDISK", 40); /* model */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync#if ATA_MAX_MULT_SECTORS > 1
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[47] = RT_H2LE_U16(0x8000 | ATA_MAX_MULT_SECTORS);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync#endif
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[48] = RT_H2LE_U16(1); /* dword I/O, used by the BIOS */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[49] = RT_H2LE_U16(1 << 11 | 1 << 9 | 1 << 8); /* DMA and LBA supported */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[50] = RT_H2LE_U16(1 << 14); /* No drive specific standby timer minimum */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[51] = RT_H2LE_U16(240); /* PIO transfer cycle */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[52] = RT_H2LE_U16(240); /* DMA transfer cycle */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[53] = RT_H2LE_U16(1 | 1 << 1 | 1 << 2); /* words 54-58,64-70,88 valid */
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync p[54] = RT_H2LE_U16(RT_MIN(s->PCHSGeometry.cCylinders, 16383));
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync p[55] = RT_H2LE_U16(s->PCHSGeometry.cHeads);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync p[56] = RT_H2LE_U16(s->PCHSGeometry.cSectors);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync p[57] = RT_H2LE_U16( RT_MIN(s->PCHSGeometry.cCylinders, 16383)
9b1d52365befbce1af8f32d53c2e563ee9169501vboxsync * s->PCHSGeometry.cHeads
76f3a3817b6b96f5beb30b76efebdf2d87090cf0vboxsync * s->PCHSGeometry.cSectors);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[58] = RT_H2LE_U16( RT_MIN(s->PCHSGeometry.cCylinders, 16383)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync * s->PCHSGeometry.cHeads
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync * s->PCHSGeometry.cSectors >> 16);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync if (s->cMultSectors)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[59] = RT_H2LE_U16(0x100 | s->cMultSectors);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync if (s->cTotalSectors <= (1 << 28) - 1)
76f3a3817b6b96f5beb30b76efebdf2d87090cf0vboxsync {
76f3a3817b6b96f5beb30b76efebdf2d87090cf0vboxsync p[60] = RT_H2LE_U16(s->cTotalSectors);
9b1d52365befbce1af8f32d53c2e563ee9169501vboxsync p[61] = RT_H2LE_U16(s->cTotalSectors >> 16);
76f3a3817b6b96f5beb30b76efebdf2d87090cf0vboxsync }
76f3a3817b6b96f5beb30b76efebdf2d87090cf0vboxsync else
9b1d52365befbce1af8f32d53c2e563ee9169501vboxsync {
76f3a3817b6b96f5beb30b76efebdf2d87090cf0vboxsync /* Report maximum number of sectors possible with LBA28 */
76f3a3817b6b96f5beb30b76efebdf2d87090cf0vboxsync p[60] = RT_H2LE_U16(((1 << 28) - 1) & 0xffff);
9b1d52365befbce1af8f32d53c2e563ee9169501vboxsync p[61] = RT_H2LE_U16(((1 << 28) - 1) >> 16);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync }
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[63] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_MDMA, ATA_MDMA_MODE_MAX, s->uATATransferMode)); /* MDMA modes supported / mode enabled */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[64] = RT_H2LE_U16(ATA_PIO_MODE_MAX > 2 ? (1 << (ATA_PIO_MODE_MAX - 2)) - 1 : 0); /* PIO modes beyond PIO2 supported */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[65] = RT_H2LE_U16(120); /* minimum DMA multiword tx cycle time */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[66] = RT_H2LE_U16(120); /* recommended DMA multiword tx cycle time */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[82] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* supports power management, write cache and look-ahead */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync if (s->cTotalSectors <= (1 << 28) - 1)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[83] = RT_H2LE_U16(1 << 14 | 1 << 12); /* supports FLUSH CACHE */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync else
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[83] = RT_H2LE_U16(1 << 14 | 1 << 10 | 1 << 12 | 1 << 13); /* supports LBA48, FLUSH CACHE and FLUSH CACHE EXT */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[84] = RT_H2LE_U16(1 << 14);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[85] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* enabled power management, write cache and look-ahead */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync if (s->cTotalSectors <= (1 << 28) - 1)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[86] = RT_H2LE_U16(1 << 12); /* enabled FLUSH CACHE */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync else
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[86] = RT_H2LE_U16(1 << 10 | 1 << 12 | 1 << 13); /* enabled LBA48, FLUSH CACHE and FLUSH CACHE EXT */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[87] = RT_H2LE_U16(1 << 14);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[88] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_UDMA, ATA_UDMA_MODE_MAX, s->uATATransferMode)); /* UDMA modes supported / mode enabled */
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[93] = RT_H2LE_U16((1 | 1 << 1) << ((s->iLUN & 1) == 0 ? 0 : 8) | 1 << 13 | 1 << 14);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync if (s->cTotalSectors > (1 << 28) - 1)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync {
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[100] = RT_H2LE_U16(s->cTotalSectors);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[101] = RT_H2LE_U16(s->cTotalSectors >> 16);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[102] = RT_H2LE_U16(s->cTotalSectors >> 32);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync p[103] = RT_H2LE_U16(s->cTotalSectors >> 48);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync }
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync s->iSourceSink = ATAFN_SS_NULL;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync ataCmdOK(s, ATA_STAT_SEEK);
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync return false;
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync}
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync
76f3a3817b6b96f5beb30b76efebdf2d87090cf0vboxsyncstatic bool ataFlushSS(ATADevState *s)
2346cab03e0c9fba1765c8e21ef98f03c8564cd8vboxsync{
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync int rc;
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_NONE);
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync Assert(!s->cbElementaryTransfer);
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync PDMCritSectLeave(&pCtl->lock);
76f3a3817b6b96f5beb30b76efebdf2d87090cf0vboxsync
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync STAM_PROFILE_START(&s->StatFlushes, f);
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync rc = s->pDrvBlock->pfnFlush(s->pDrvBlock);
a655881be920ead6948994168c5ee09e5798aa05vboxsync AssertRC(rc);
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync STAM_PROFILE_STOP(&s->StatFlushes, f);
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync
8865793e4f3435f5e2c728d9e6739cd24d08c0devboxsync STAM_PROFILE_START(&pCtl->StatLockWait, a);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync ataCmdOK(s, 0);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync return false;
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync}
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsyncstatic bool atapiIdentifySS(ATADevState *s)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync{
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint16_t *p;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync char aSerial[20];
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync RTUUID Uuid;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync int rc;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync Assert(s->cbElementaryTransfer == 512);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync rc = s->pDrvBlock ? s->pDrvBlock->pfnGetUuid(s->pDrvBlock, &Uuid) : RTUuidClear(&Uuid);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (RT_FAILURE(rc) || RTUuidIsNull(&Uuid))
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /* Generate a predictable serial for drives which don't have a UUID. */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync RTStrPrintf(aSerial, sizeof(aSerial), "VB%x-%04x%04x",
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->iLUN + ATADEVSTATE_2_DEVINS(s)->iInstance * 32,
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync pCtl->IOPortBase1, pCtl->IOPortBase2);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync else
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync RTStrPrintf(aSerial, sizeof(aSerial), "VB%08x-%08x", Uuid.au32[0], Uuid.au32[3]);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync p = (uint16_t *)s->CTX_SUFF(pbIOBuffer);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync memset(p, 0, 512);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /* Removable CDROM, 50us response, 12 byte packets */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[0] = RT_H2LE_U16(2 << 14 | 5 << 8 | 1 << 7 | 2 << 5 | 0 << 0);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync ataPadString((uint8_t *)(p + 10), aSerial, 20); /* serial number */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[21] = RT_H2LE_U16(512); /* XXX: retired, cache size in sectors */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync ataPadString((uint8_t *)(p + 23), "1.0", 8); /* firmware version */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync ataPadString((uint8_t *)(p + 27), "VBOX CD-ROM", 40); /* model */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[49] = RT_H2LE_U16(1 << 11 | 1 << 9 | 1 << 8); /* DMA and LBA supported */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[50] = RT_H2LE_U16(1 << 14); /* No drive specific standby timer minimum */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[51] = RT_H2LE_U16(240); /* PIO transfer cycle */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[52] = RT_H2LE_U16(240); /* DMA transfer cycle */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[53] = RT_H2LE_U16(1 << 1 | 1 << 2); /* words 64-70,88 are valid */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[63] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_MDMA, ATA_MDMA_MODE_MAX, s->uATATransferMode)); /* MDMA modes supported / mode enabled */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[64] = RT_H2LE_U16(ATA_PIO_MODE_MAX > 2 ? (1 << (ATA_PIO_MODE_MAX - 2)) - 1 : 0); /* PIO modes beyond PIO2 supported */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[65] = RT_H2LE_U16(120); /* minimum DMA multiword tx cycle time */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[66] = RT_H2LE_U16(120); /* recommended DMA multiword tx cycle time */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[73] = RT_H2LE_U16(0x003e); /* ATAPI CDROM major */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[74] = RT_H2LE_U16(9); /* ATAPI CDROM minor */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[75] = RT_H2LE_U16(1); /* queue depth 1 */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
56970d3a1944c7c073d66266cd52449835221badvboxsync p[82] = RT_H2LE_U16(1 << 4 | 1 << 9); /* supports packet command set and DEVICE RESET */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync p[83] = RT_H2LE_U16(1 << 14);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync p[84] = RT_H2LE_U16(1 << 14);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync p[85] = RT_H2LE_U16(1 << 4 | 1 << 9); /* enabled packet command set and DEVICE RESET */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync p[86] = RT_H2LE_U16(0);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync p[87] = RT_H2LE_U16(1 << 14);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync p[88] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_UDMA, ATA_UDMA_MODE_MAX, s->uATATransferMode)); /* UDMA modes supported / mode enabled */
56970d3a1944c7c073d66266cd52449835221badvboxsync p[93] = RT_H2LE_U16((1 | 1 << 1) << ((s->iLUN & 1) == 0 ? 0 : 8) | 1 << 13 | 1 << 14);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->iSourceSink = ATAFN_SS_NULL;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync ataCmdOK(s, ATA_STAT_SEEK);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync return false;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync}
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsyncstatic void ataSetSignature(ATADevState *s)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync{
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegSelect &= 0xf0; /* clear head */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync /* put signature */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegNSector = 1;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegSector = 1;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync if (s->fATAPI)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync {
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegLCyl = 0x14;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegHCyl = 0xeb;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync }
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync else if (s->pDrvBlock)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync {
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegLCyl = 0;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegHCyl = 0;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync }
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync else
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync {
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegLCyl = 0xff;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegHCyl = 0xff;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync }
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync}
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsyncstatic uint64_t ataGetSector(ATADevState *s)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync{
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync uint64_t iLBA;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync if (s->uATARegSelect & 0x40)
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync {
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync /* any LBA variant */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync if (s->fLBA48)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync {
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync /* LBA48 */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync iLBA = ((uint64_t)s->uATARegHCylHOB << 40) |
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync ((uint64_t)s->uATARegLCylHOB << 32) |
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync ((uint64_t)s->uATARegSectorHOB << 24) |
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync ((uint64_t)s->uATARegHCyl << 16) |
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync ((uint64_t)s->uATARegLCyl << 8) |
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync s->uATARegSector;
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync }
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync else
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync {
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync /* LBA */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync iLBA = ((s->uATARegSelect & 0x0f) << 24) | (s->uATARegHCyl << 16) |
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync (s->uATARegLCyl << 8) | s->uATARegSector;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync }
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync else
56970d3a1944c7c073d66266cd52449835221badvboxsync {
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync /* CHS */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync iLBA = ((s->uATARegHCyl << 8) | s->uATARegLCyl) * s->PCHSGeometry.cHeads * s->PCHSGeometry.cSectors +
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync (s->uATARegSelect & 0x0f) * s->PCHSGeometry.cSectors +
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync (s->uATARegSector - 1);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync }
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync return iLBA;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync}
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsyncstatic void ataSetSector(ATADevState *s, uint64_t iLBA)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync{
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync uint32_t cyl, r;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync if (s->uATARegSelect & 0x40)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync {
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync /* any LBA variant */
2d66abeefb9716ed570bb5714884d3fe08629452vboxsync if (s->fLBA48)
2d66abeefb9716ed570bb5714884d3fe08629452vboxsync {
2d66abeefb9716ed570bb5714884d3fe08629452vboxsync /* LBA48 */
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync s->uATARegHCylHOB = iLBA >> 40;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync s->uATARegLCylHOB = iLBA >> 32;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync s->uATARegSectorHOB = iLBA >> 24;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegHCyl = iLBA >> 16;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegLCyl = iLBA >> 8;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync s->uATARegSector = iLBA;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync }
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync else
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync {
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync /* LBA */
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync s->uATARegSelect = (s->uATARegSelect & 0xf0) | (iLBA >> 24);
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync s->uATARegHCyl = (iLBA >> 16);
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync s->uATARegLCyl = (iLBA >> 8);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegSector = (iLBA);
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync }
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync }
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync else
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync {
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync /* CHS */
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync cyl = iLBA / (s->PCHSGeometry.cHeads * s->PCHSGeometry.cSectors);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync r = iLBA % (s->PCHSGeometry.cHeads * s->PCHSGeometry.cSectors);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegHCyl = cyl >> 8;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegLCyl = cyl;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegSelect = (s->uATARegSelect & 0xf0) | ((r / s->PCHSGeometry.cSectors) & 0x0f);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->uATARegSector = (r % s->PCHSGeometry.cSectors) + 1;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync }
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsync}
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
23d8f7aff045c2bade1b168fee79a3e4749e2345vboxsyncstatic int ataReadSectors(ATADevState *s, uint64_t u64Sector, void *pvBuf, uint32_t cSectors)
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync{
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync int rc;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync PDMCritSectLeave(&pCtl->lock);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync STAM_PROFILE_ADV_START(&s->StatReads, r);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync s->Led.Asserted.s.fReading = s->Led.Actual.s.fReading = 1;
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync rc = s->pDrvBlock->pfnRead(s->pDrvBlock, u64Sector * 512, pvBuf, cSectors * 512);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync s->Led.Actual.s.fReading = 0;
e70bda5438c3582164d26f171a8bc8d3d7da1e12vboxsync STAM_PROFILE_ADV_STOP(&s->StatReads, r);
e70bda5438c3582164d26f171a8bc8d3d7da1e12vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync STAM_REL_COUNTER_ADD(&s->StatBytesRead, cSectors * 512);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync STAM_PROFILE_START(&pCtl->StatLockWait, a);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
20f21077abf35d7b7b618acb159267933907407fvboxsync STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
20f21077abf35d7b7b618acb159267933907407fvboxsync return rc;
20f21077abf35d7b7b618acb159267933907407fvboxsync}
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
20f21077abf35d7b7b618acb159267933907407fvboxsync
20f21077abf35d7b7b618acb159267933907407fvboxsyncstatic int ataWriteSectors(ATADevState *s, uint64_t u64Sector, const void *pvBuf, uint32_t cSectors)
20f21077abf35d7b7b618acb159267933907407fvboxsync{
20f21077abf35d7b7b618acb159267933907407fvboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
20f21077abf35d7b7b618acb159267933907407fvboxsync int rc;
20f21077abf35d7b7b618acb159267933907407fvboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync PDMCritSectLeave(&pCtl->lock);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync STAM_PROFILE_ADV_START(&s->StatWrites, w);
c66c4413faa5a72ce047742f9acfa85e94dec8afvboxsync s->Led.Asserted.s.fWriting = s->Led.Actual.s.fWriting = 1;
e70bda5438c3582164d26f171a8bc8d3d7da1e12vboxsync#ifdef VBOX_INSTRUMENT_DMA_WRITES
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync if (s->fDMA)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync STAM_PROFILE_ADV_START(&s->StatInstrVDWrites, vw);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync#endif
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync rc = s->pDrvBlock->pfnWrite(s->pDrvBlock, u64Sector * 512, pvBuf, cSectors * 512);
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync#ifdef VBOX_INSTRUMENT_DMA_WRITES
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync if (s->fDMA)
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync STAM_PROFILE_ADV_STOP(&s->StatInstrVDWrites, vw);
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync#endif
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync s->Led.Actual.s.fWriting = 0;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync STAM_PROFILE_ADV_STOP(&s->StatWrites, w);
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync STAM_REL_COUNTER_ADD(&s->StatBytesWritten, cSectors * 512);
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync STAM_PROFILE_START(&pCtl->StatLockWait, a);
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync return rc;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync}
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsyncstatic void ataReadWriteSectorsBT(ATADevState *s)
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync{
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync uint32_t cSectors;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync cSectors = s->cbTotalTransfer / 512;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync if (cSectors > s->cSectorsPerIRQ)
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync s->cbElementaryTransfer = s->cSectorsPerIRQ * 512;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync else
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync s->cbElementaryTransfer = cSectors * 512;
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync ataCmdOK(s, 0);
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync}
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsync
66b58af085e22ee26be57f98127fb49ee2e91790vboxsyncstatic void ataWarningDiskFull(PPDMDEVINS pDevIns)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync{
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync int rc;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("PIIX3 ATA: Host disk full\n"));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync rc = VMSetRuntimeError(PDMDevHlpGetVM(pDevIns),
56970d3a1944c7c073d66266cd52449835221badvboxsync false, "DevATA_DISKFULL",
56970d3a1944c7c073d66266cd52449835221badvboxsync N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
56970d3a1944c7c073d66266cd52449835221badvboxsync AssertRC(rc);
56970d3a1944c7c073d66266cd52449835221badvboxsync}
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsyncstatic void ataWarningFileTooBig(PPDMDEVINS pDevIns)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync{
56970d3a1944c7c073d66266cd52449835221badvboxsync int rc;
56970d3a1944c7c073d66266cd52449835221badvboxsync LogRel(("PIIX3 ATA: File too big\n"));
56970d3a1944c7c073d66266cd52449835221badvboxsync rc = VMSetRuntimeError(PDMDevHlpGetVM(pDevIns),
56970d3a1944c7c073d66266cd52449835221badvboxsync false, "DevATA_FILETOOBIG",
56970d3a1944c7c073d66266cd52449835221badvboxsync N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files"));
56970d3a1944c7c073d66266cd52449835221badvboxsync AssertRC(rc);
56970d3a1944c7c073d66266cd52449835221badvboxsync}
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsyncstatic void ataWarningISCSI(PPDMDEVINS pDevIns)
56970d3a1944c7c073d66266cd52449835221badvboxsync{
56970d3a1944c7c073d66266cd52449835221badvboxsync int rc;
56970d3a1944c7c073d66266cd52449835221badvboxsync LogRel(("PIIX3 ATA: iSCSI target unavailable\n"));
56970d3a1944c7c073d66266cd52449835221badvboxsync rc = VMSetRuntimeError(PDMDevHlpGetVM(pDevIns),
56970d3a1944c7c073d66266cd52449835221badvboxsync false, "DevATA_ISCSIDOWN",
56970d3a1944c7c073d66266cd52449835221badvboxsync N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
56970d3a1944c7c073d66266cd52449835221badvboxsync AssertRC(rc);
56970d3a1944c7c073d66266cd52449835221badvboxsync}
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsyncstatic bool ataReadSectorsSS(ATADevState *s)
56970d3a1944c7c073d66266cd52449835221badvboxsync{
56970d3a1944c7c073d66266cd52449835221badvboxsync int rc;
56970d3a1944c7c073d66266cd52449835221badvboxsync uint32_t cSectors;
56970d3a1944c7c073d66266cd52449835221badvboxsync uint64_t iLBA;
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsync cSectors = s->cbElementaryTransfer / 512;
56970d3a1944c7c073d66266cd52449835221badvboxsync Assert(cSectors);
56970d3a1944c7c073d66266cd52449835221badvboxsync iLBA = ataGetSector(s);
56970d3a1944c7c073d66266cd52449835221badvboxsync Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iLBA));
56970d3a1944c7c073d66266cd52449835221badvboxsync rc = ataReadSectors(s, iLBA, s->CTX_SUFF(pbIOBuffer), cSectors);
56970d3a1944c7c073d66266cd52449835221badvboxsync if (RT_SUCCESS(rc))
56970d3a1944c7c073d66266cd52449835221badvboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync ataSetSector(s, iLBA + cSectors);
56970d3a1944c7c073d66266cd52449835221badvboxsync if (s->cbElementaryTransfer == s->cbTotalTransfer)
56970d3a1944c7c073d66266cd52449835221badvboxsync s->iSourceSink = ATAFN_SS_NULL;
56970d3a1944c7c073d66266cd52449835221badvboxsync ataCmdOK(s, ATA_STAT_SEEK);
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync else
56970d3a1944c7c073d66266cd52449835221badvboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync if (rc == VERR_DISK_FULL)
56970d3a1944c7c073d66266cd52449835221badvboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync ataWarningDiskFull(ATADEVSTATE_2_DEVINS(s));
56970d3a1944c7c073d66266cd52449835221badvboxsync return true;
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync if (rc == VERR_FILE_TOO_BIG)
56970d3a1944c7c073d66266cd52449835221badvboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync ataWarningFileTooBig(ATADEVSTATE_2_DEVINS(s));
56970d3a1944c7c073d66266cd52449835221badvboxsync return true;
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
56970d3a1944c7c073d66266cd52449835221badvboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync /* iSCSI connection abort (first error) or failure to reestablish
56970d3a1944c7c073d66266cd52449835221badvboxsync * connection (second error). Pause VM. On resume we'll retry. */
56970d3a1944c7c073d66266cd52449835221badvboxsync ataWarningISCSI(ATADEVSTATE_2_DEVINS(s));
56970d3a1944c7c073d66266cd52449835221badvboxsync return true;
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync if (s->cErrors++ < MAX_LOG_REL_ERRORS)
56970d3a1944c7c073d66266cd52449835221badvboxsync LogRel(("PIIX3 ATA: LUN#%d: disk read error (rc=%Rrc iSector=%#RX64 cSectors=%#RX32)\n",
56970d3a1944c7c073d66266cd52449835221badvboxsync s->iLUN, rc, iLBA, cSectors));
56970d3a1944c7c073d66266cd52449835221badvboxsync ataCmdError(s, ID_ERR);
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync /** @todo implement redo for iSCSI */
56970d3a1944c7c073d66266cd52449835221badvboxsync return false;
56970d3a1944c7c073d66266cd52449835221badvboxsync}
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsyncstatic bool ataWriteSectorsSS(ATADevState *s)
56970d3a1944c7c073d66266cd52449835221badvboxsync{
56970d3a1944c7c073d66266cd52449835221badvboxsync int rc;
56970d3a1944c7c073d66266cd52449835221badvboxsync uint32_t cSectors;
56970d3a1944c7c073d66266cd52449835221badvboxsync uint64_t iLBA;
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsync cSectors = s->cbElementaryTransfer / 512;
56970d3a1944c7c073d66266cd52449835221badvboxsync Assert(cSectors);
56970d3a1944c7c073d66266cd52449835221badvboxsync iLBA = ataGetSector(s);
56970d3a1944c7c073d66266cd52449835221badvboxsync Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iLBA));
56970d3a1944c7c073d66266cd52449835221badvboxsync rc = ataWriteSectors(s, iLBA, s->CTX_SUFF(pbIOBuffer), cSectors);
56970d3a1944c7c073d66266cd52449835221badvboxsync if (RT_SUCCESS(rc))
56970d3a1944c7c073d66266cd52449835221badvboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync ataSetSector(s, iLBA + cSectors);
56970d3a1944c7c073d66266cd52449835221badvboxsync if (!s->cbTotalTransfer)
56970d3a1944c7c073d66266cd52449835221badvboxsync s->iSourceSink = ATAFN_SS_NULL;
56970d3a1944c7c073d66266cd52449835221badvboxsync ataCmdOK(s, ATA_STAT_SEEK);
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync else
56970d3a1944c7c073d66266cd52449835221badvboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync if (rc == VERR_DISK_FULL)
56970d3a1944c7c073d66266cd52449835221badvboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync ataWarningDiskFull(ATADEVSTATE_2_DEVINS(s));
56970d3a1944c7c073d66266cd52449835221badvboxsync return true;
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync if (rc == VERR_FILE_TOO_BIG)
56970d3a1944c7c073d66266cd52449835221badvboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync ataWarningFileTooBig(ATADEVSTATE_2_DEVINS(s));
56970d3a1944c7c073d66266cd52449835221badvboxsync return true;
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
56970d3a1944c7c073d66266cd52449835221badvboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync /* iSCSI connection abort (first error) or failure to reestablish
56970d3a1944c7c073d66266cd52449835221badvboxsync * connection (second error). Pause VM. On resume we'll retry. */
56970d3a1944c7c073d66266cd52449835221badvboxsync ataWarningISCSI(ATADEVSTATE_2_DEVINS(s));
56970d3a1944c7c073d66266cd52449835221badvboxsync return true;
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync if (s->cErrors++ < MAX_LOG_REL_ERRORS)
56970d3a1944c7c073d66266cd52449835221badvboxsync LogRel(("PIIX3 ATA: LUN#%d: disk write error (rc=%Rrc iSector=%#RX64 cSectors=%#RX32)\n",
56970d3a1944c7c073d66266cd52449835221badvboxsync s->iLUN, rc, iLBA, cSectors));
56970d3a1944c7c073d66266cd52449835221badvboxsync ataCmdError(s, ID_ERR);
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync /** @todo implement redo for iSCSI */
56970d3a1944c7c073d66266cd52449835221badvboxsync return false;
56970d3a1944c7c073d66266cd52449835221badvboxsync}
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsyncstatic void atapiCmdOK(ATADevState *s)
56970d3a1944c7c073d66266cd52449835221badvboxsync{
56970d3a1944c7c073d66266cd52449835221badvboxsync s->uATARegError = 0;
56970d3a1944c7c073d66266cd52449835221badvboxsync ataSetStatusValue(s, ATA_STAT_READY);
56970d3a1944c7c073d66266cd52449835221badvboxsync s->uATARegNSector = (s->uATARegNSector & ~7)
56970d3a1944c7c073d66266cd52449835221badvboxsync | ((s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE) ? ATAPI_INT_REASON_IO : 0)
56970d3a1944c7c073d66266cd52449835221badvboxsync | (!s->cbTotalTransfer ? ATAPI_INT_REASON_CD : 0);
56970d3a1944c7c073d66266cd52449835221badvboxsync Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector));
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsync memset(s->abATAPISense, '\0', sizeof(s->abATAPISense));
56970d3a1944c7c073d66266cd52449835221badvboxsync s->abATAPISense[0] = 0x70 | (1 << 7);
56970d3a1944c7c073d66266cd52449835221badvboxsync s->abATAPISense[7] = 10;
56970d3a1944c7c073d66266cd52449835221badvboxsync}
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsync
56970d3a1944c7c073d66266cd52449835221badvboxsyncstatic void atapiCmdError(ATADevState *s, const uint8_t *pabATAPISense, size_t cbATAPISense)
56970d3a1944c7c073d66266cd52449835221badvboxsync{
56970d3a1944c7c073d66266cd52449835221badvboxsync Log(("%s: sense=%#x (%s) asc=%#x ascq=%#x (%s)\n", __FUNCTION__, pabATAPISense[2] & 0x0f, SCSISenseText(pabATAPISense[2] & 0x0f),
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync pabATAPISense[12], pabATAPISense[13], SCSISenseExtText(pabATAPISense[12], pabATAPISense[13])));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->uATARegError = pabATAPISense[2] << 4;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync ataSetStatusValue(s, ATA_STAT_READY | ATA_STAT_ERR);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync memset(s->abATAPISense, '\0', sizeof(s->abATAPISense));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync memcpy(s->abATAPISense, pabATAPISense, RT_MIN(cbATAPISense, sizeof(s->abATAPISense)));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbTotalTransfer = 0;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbElementaryTransfer = 0;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->iIOBufferCur = 0;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->iIOBufferEnd = 0;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->uTxDir = PDMBLOCKTXDIR_NONE;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->iBeginTransfer = ATAFN_BT_NULL;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->iSourceSink = ATAFN_SS_NULL;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync}
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync/** @todo deprecated function - doesn't provide enough info. Replace by direct
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * calls to atapiCmdError() with full data. */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic void atapiCmdErrorSimple(ATADevState *s, uint8_t uATAPISenseKey, uint8_t uATAPIASC)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync{
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync uint8_t abATAPISense[ATAPI_SENSE_SIZE];
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync memset(abATAPISense, '\0', sizeof(abATAPISense));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync abATAPISense[0] = 0x70 | (1 << 7);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync abATAPISense[2] = uATAPISenseKey & 0x0f;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync abATAPISense[7] = 10;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync abATAPISense[12] = uATAPIASC;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdError(s, abATAPISense, sizeof(abATAPISense));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync}
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic void atapiCmdBT(ATADevState *s)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync{
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync s->fATAPITransfer = true;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync s->cbElementaryTransfer = s->cbTotalTransfer;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync atapiCmdOK(s);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync}
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsyncstatic void atapiPassthroughCmdBT(ATADevState *s)
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync{
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync /* @todo implement an algorithm for correctly determining the read and
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync * write sector size without sending additional commands to the drive.
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync * This should be doable by saving processing the configuration requests
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync * and replies. */
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync#if 0
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint8_t cmd = s->aATAPICmd[0];
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (cmd == SCSI_WRITE_10 || cmd == SCSI_WRITE_12 || cmd == SCSI_WRITE_AND_VERIFY_10)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint8_t aModeSenseCmd[10];
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint8_t aModeSenseResult[16];
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint8_t uDummySense;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint32_t cbTransfer;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync int rc;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync cbTransfer = sizeof(aModeSenseResult);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync aModeSenseCmd[0] = SCSI_MODE_SENSE_10;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync aModeSenseCmd[1] = 0x08; /* disable block descriptor = 1 */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync aModeSenseCmd[2] = (SCSI_PAGECONTROL_CURRENT << 6) | SCSI_MODEPAGE_WRITE_PARAMETER;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync aModeSenseCmd[3] = 0; /* subpage code */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync aModeSenseCmd[4] = 0; /* reserved */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync aModeSenseCmd[5] = 0; /* reserved */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync aModeSenseCmd[6] = 0; /* reserved */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync aModeSenseCmd[7] = cbTransfer >> 8;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync aModeSenseCmd[8] = cbTransfer & 0xff;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync aModeSenseCmd[9] = 0; /* control */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync rc = s->pDrvBlock->pfnSendCmd(s->pDrvBlock, aModeSenseCmd, PDMBLOCKTXDIR_FROM_DEVICE, aModeSenseResult, &cbTransfer, &uDummySense, 500);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (RT_FAILURE(rc))
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_NONE);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync return;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /* Select sector size based on the current data block type. */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync switch (aModeSenseResult[12] & 0x0f)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 0:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbATAPISector = 2352;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 1:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbATAPISector = 2368;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 2:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 3:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbATAPISector = 2448;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 8:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 10:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbATAPISector = 2048;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 9:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbATAPISector = 2336;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 11:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbATAPISector = 2056;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 12:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbATAPISector = 2324;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 13:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbATAPISector = 2332;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync default:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbATAPISector = 0;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync Log2(("%s: sector size %d\n", __FUNCTION__, s->cbATAPISector));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbTotalTransfer *= s->cbATAPISector;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (s->cbTotalTransfer == 0)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->uTxDir = PDMBLOCKTXDIR_NONE;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync#endif
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdBT(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync}
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic bool atapiReadSS(ATADevState *s)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync{
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync int rc = VINF_SUCCESS;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync uint32_t cbTransfer, cSectors;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync cbTransfer = RT_MIN(s->cbTotalTransfer, s->cbIOBuffer);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync cSectors = cbTransfer / s->cbATAPISector;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync Assert(cSectors * s->cbATAPISector <= cbTransfer);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, s->iATAPILBA));
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync PDMCritSectLeave(&pCtl->lock);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync STAM_PROFILE_ADV_START(&s->StatReads, r);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync s->Led.Asserted.s.fReading = s->Led.Actual.s.fReading = 1;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync switch (s->cbATAPISector)
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync {
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync case 2048:
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)s->iATAPILBA * s->cbATAPISector, s->CTX_SUFF(pbIOBuffer), s->cbATAPISector * cSectors);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync break;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync case 2352:
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync {
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync for (uint32_t i = s->iATAPILBA; i < s->iATAPILBA + cSectors; i++)
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync {
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync /* sync bytes */
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync *pbBuf++ = 0x00;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync memset(pbBuf, 0xff, 11);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync pbBuf += 11;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync /* MSF */
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync ataLBA2MSF(pbBuf, i);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync pbBuf += 3;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync *pbBuf++ = 0x01; /* mode 1 data */
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync /* data */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync rc = s->pDrvBlock->pfnRead(s->pDrvBlock, (uint64_t)i * 2048, pbBuf, 2048);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (RT_FAILURE(rc))
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync pbBuf += 2048;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync /* ECC */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync memset(pbBuf, 0, 288);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync pbBuf += 288;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync default:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync STAM_PROFILE_ADV_STOP(&s->StatReads, r);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync STAM_PROFILE_START(&pCtl->StatLockWait, a);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (RT_SUCCESS(rc))
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->Led.Actual.s.fReading = 0;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync STAM_REL_COUNTER_ADD(&s->StatBytesRead, s->cbATAPISector * cSectors);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync /* The initial buffer end value has been set up based on the total
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * transfer size. But the I/O buffer size limits what can actually be
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * done in one transfer, so set the actual value of the buffer end. */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->cbElementaryTransfer = cbTransfer;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (cbTransfer >= s->cbTotalTransfer)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->iSourceSink = ATAFN_SS_NULL;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync atapiCmdOK(s);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->iATAPILBA += cSectors;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync else
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (s->cErrors++ < MAX_LOG_REL_ERRORS)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync LogRel(("PIIX3 ATA: LUN#%d: CD-ROM read error, %d sectors at LBA %d\n", s->iLUN, cSectors, s->iATAPILBA));
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_MEDIUM_ERROR, SCSI_ASC_READ_ERROR);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync return false;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync}
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync
88e56f700a3b8dfdf1646f96320f335e22339caavboxsyncstatic bool atapiPassthroughSS(ATADevState *s)
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync{
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync int rc = VINF_SUCCESS;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync uint8_t abATAPISense[ATAPI_SENSE_SIZE];
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync size_t cbTransfer;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync PSTAMPROFILEADV pProf = NULL;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync cbTransfer = s->cbElementaryTransfer;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE)
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync Log3(("ATAPI PT data write (%d): %.*Vhxs\n", cbTransfer, cbTransfer, s->CTX_SUFF(pbIOBuffer)));
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync /* Simple heuristics: if there is at least one sector of data
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * to transfer, it's worth updating the LEDs. */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (cbTransfer >= 2048)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->Led.Asserted.s.fReading = s->Led.Actual.s.fReading = 1;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync pProf = &s->StatReads;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync else
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->Led.Asserted.s.fWriting = s->Led.Actual.s.fWriting = 1;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync pProf = &s->StatWrites;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync PDMCritSectLeave(&pCtl->lock);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (pProf) { STAM_PROFILE_ADV_START(pProf, b); }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (cbTransfer > 100 * _1K)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync /* Linux accepts commands with up to 100KB of data, but expects
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * us to handle commands with up to 128KB of data. The usual
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * imbalance of powers. */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync uint8_t aATAPICmd[ATAPI_PACKET_SIZE];
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync uint32_t iATAPILBA, cSectors, cReqSectors;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync size_t cbCurrTX;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync switch (s->aATAPICmd[0])
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_READ_10:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_WRITE_10:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_WRITE_AND_VERIFY_10:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync iATAPILBA = ataBE2H_U32(s->aATAPICmd + 2);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync cSectors = ataBE2H_U16(s->aATAPICmd + 7);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_READ_12:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_WRITE_12:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync iATAPILBA = ataBE2H_U32(s->aATAPICmd + 2);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync cSectors = ataBE2H_U32(s->aATAPICmd + 6);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_READ_CD:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync iATAPILBA = ataBE2H_U32(s->aATAPICmd + 2);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync cSectors = ataBE2H_U24(s->aATAPICmd + 6) / s->cbATAPISector;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_READ_CD_MSF:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync iATAPILBA = ataMSF2LBA(s->aATAPICmd + 3);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync cSectors = ataMSF2LBA(s->aATAPICmd + 6) - iATAPILBA;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync default:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync AssertMsgFailed(("Don't know how to split command %#04x\n", s->aATAPICmd[0]));
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (s->cErrors++ < MAX_LOG_REL_ERRORS)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync LogRel(("PIIX3 ATA: LUN#%d: CD-ROM passthrough split error\n", s->iLUN));
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync STAM_PROFILE_START(&pCtl->StatLockWait, a);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync return false;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync memcpy(aATAPICmd, s->aATAPICmd, ATAPI_PACKET_SIZE);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync cReqSectors = 0;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync for (uint32_t i = cSectors; i > 0; i -= cReqSectors)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (i * s->cbATAPISector > 100 * _1K)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync cReqSectors = (100 * _1K) / s->cbATAPISector;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync else
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync cReqSectors = i;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync cbCurrTX = s->cbATAPISector * cReqSectors;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync switch (s->aATAPICmd[0])
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_READ_10:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_WRITE_10:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_WRITE_AND_VERIFY_10:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataH2BE_U32(aATAPICmd + 2, iATAPILBA);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataH2BE_U16(aATAPICmd + 7, cReqSectors);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_READ_12:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_WRITE_12:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataH2BE_U32(aATAPICmd + 2, iATAPILBA);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataH2BE_U32(aATAPICmd + 6, cReqSectors);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_READ_CD:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataH2BE_U32(s->aATAPICmd + 2, iATAPILBA);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataH2BE_U24(s->aATAPICmd + 6, cbCurrTX);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync case SCSI_READ_CD_MSF:
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataLBA2MSF(aATAPICmd + 3, iATAPILBA);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataLBA2MSF(aATAPICmd + 6, iATAPILBA + cReqSectors);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync rc = s->pDrvBlock->pfnSendCmd(s->pDrvBlock, aATAPICmd, (PDMBLOCKTXDIR)s->uTxDir, pbBuf, &cbCurrTX, abATAPISense, sizeof(abATAPISense), 30000 /**< @todo timeout */);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (rc != VINF_SUCCESS)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync iATAPILBA += cReqSectors;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync pbBuf += s->cbATAPISector * cReqSectors;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync else
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync rc = s->pDrvBlock->pfnSendCmd(s->pDrvBlock, s->aATAPICmd, (PDMBLOCKTXDIR)s->uTxDir, s->CTX_SUFF(pbIOBuffer), &cbTransfer, abATAPISense, sizeof(abATAPISense), 30000 /**< @todo timeout */);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (pProf) { STAM_PROFILE_ADV_STOP(pProf, b); }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync STAM_PROFILE_START(&pCtl->StatLockWait, a);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync /* Update the LEDs and the read/write statistics. */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (cbTransfer >= 2048)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->Led.Actual.s.fReading = 0;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync STAM_REL_COUNTER_ADD(&s->StatBytesRead, cbTransfer);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync else
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->Led.Actual.s.fWriting = 0;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync STAM_REL_COUNTER_ADD(&s->StatBytesWritten, cbTransfer);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (RT_SUCCESS(rc))
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync Assert(cbTransfer <= s->cbTotalTransfer);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync /* Reply with the same amount of data as the real drive. */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->cbTotalTransfer = cbTransfer;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync /* The initial buffer end value has been set up based on the total
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * transfer size. But the I/O buffer size limits what can actually be
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * done in one transfer, so set the actual value of the buffer end. */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->cbElementaryTransfer = cbTransfer;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if ( s->aATAPICmd[0] == SCSI_INQUIRY
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync && !s->fATAPIPassthrough)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync /* Make sure that the real drive cannot be identified.
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * Motivation: changing the VM configuration should be as
661bfa5aae55ac2f94fa1cb131ea2323e5f6e633vboxsync * invisible as possible to the guest.
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * Exception: passthrough. Otherwise Windows will not detect
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync * CDR/CDRW burning capabilities */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync Log3(("ATAPI PT inquiry data before (%d): %.*Vhxs\n", cbTransfer, cbTransfer, s->CTX_SUFF(pbIOBuffer)));
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataSCSIPadStr(s->CTX_SUFF(pbIOBuffer) + 8, "VBOX", 8);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataSCSIPadStr(s->CTX_SUFF(pbIOBuffer) + 16, "CD-ROM", 16);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataSCSIPadStr(s->CTX_SUFF(pbIOBuffer) + 32, "1.0", 4);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (cbTransfer)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync Log3(("ATAPI PT data read (%d): %.*Vhxs\n", cbTransfer, cbTransfer, s->CTX_SUFF(pbIOBuffer)));
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->iSourceSink = ATAFN_SS_NULL;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync atapiCmdOK(s);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync else
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (s->cErrors < MAX_LOG_REL_ERRORS)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync uint8_t u8Cmd = s->aATAPICmd[0];
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync do
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync /* don't log superflous errors */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if ( rc == VERR_DEV_IO_ERROR
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync && ( u8Cmd == SCSI_TEST_UNIT_READY
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync || u8Cmd == SCSI_READ_CAPACITY
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync || u8Cmd == SCSI_READ_DVD_STRUCTURE
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync || u8Cmd == SCSI_READ_TOC_PMA_ATIP))
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->cErrors++;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync LogRel(("PIIX3 ATA: LUN#%d: CD-ROM passthrough cmd=%#04x sense=%d ASC=%#02x ASCQ=%#02x %Rrc\n",
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->iLUN, u8Cmd, abATAPISense[2] & 0x0f, abATAPISense[12], abATAPISense[13], rc));
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync } while (0);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync atapiCmdError(s, abATAPISense, sizeof(abATAPISense));
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync return false;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync}
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsyncstatic bool atapiReadSectors(ATADevState *s, uint32_t iATAPILBA, uint32_t cSectors, uint32_t cbSector)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync{
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync Assert(cSectors > 0);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->iATAPILBA = iATAPILBA;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->cbATAPISector = cbSector;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataStartTransfer(s, cSectors * cbSector, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ, true);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync return false;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync}
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsyncstatic bool atapiReadCapacitySS(ATADevState *s)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync{
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync Assert(s->cbElementaryTransfer <= 8);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataH2BE_U32(pbBuf, s->cTotalSectors - 1);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataH2BE_U32(pbBuf + 4, 2048);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync s->iSourceSink = ATAFN_SS_NULL;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync atapiCmdOK(s);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync return false;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync}
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsyncstatic bool atapiReadDiscInformationSS(ATADevState *s)
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync{
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync Assert(s->cbElementaryTransfer <= 34);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync memset(pbBuf, '\0', 34);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U16(pbBuf, 32);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[2] = (0 << 4) | (3 << 2) | (2 << 0); /* not erasable, complete session, complete disc */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[3] = 1; /* number of first track */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[4] = 1; /* number of sessions (LSB) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[5] = 1; /* first track number in last session (LSB) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[6] = 1; /* last track number in last session (LSB) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[7] = (0 << 7) | (0 << 6) | (1 << 5) | (0 << 2) | (0 << 0); /* disc id not valid, disc bar code not valid, unrestricted use, not dirty, not RW medium */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[8] = 0; /* disc type = CD-ROM */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[9] = 0; /* number of sessions (MSB) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[10] = 0; /* number of sessions (MSB) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[11] = 0; /* number of sessions (MSB) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U32(pbBuf + 16, 0x00ffffff); /* last session lead-in start time is not available */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U32(pbBuf + 20, 0x00ffffff); /* last possible start time for lead-out is not available */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync s->iSourceSink = ATAFN_SS_NULL;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync atapiCmdOK(s);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync return false;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync}
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
f40cc8247b1da75ce42e73e6c557ec29b8f830a5vboxsync
f40cc8247b1da75ce42e73e6c557ec29b8f830a5vboxsyncstatic bool atapiReadTrackInformationSS(ATADevState *s)
f40cc8247b1da75ce42e73e6c557ec29b8f830a5vboxsync{
f40cc8247b1da75ce42e73e6c557ec29b8f830a5vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync Assert(s->cbElementaryTransfer <= 36);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync /* Accept address/number type of 1 only, and only track 1 exists. */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync if ((s->aATAPICmd[1] & 0x03) != 1 || ataBE2H_U32(&s->aATAPICmd[2]) != 1)
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync {
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync return false;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync }
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync memset(pbBuf, '\0', 36);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U16(pbBuf, 34);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[2] = 1; /* track number (LSB) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[3] = 1; /* session number (LSB) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[5] = (0 << 5) | (0 << 4) | (4 << 0); /* not damaged, primary copy, data track */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[6] = (0 << 7) | (0 << 6) | (0 << 5) | (0 << 6) | (1 << 0); /* not reserved track, not blank, not packet writing, not fixed packet, data mode 1 */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[7] = (0 << 1) | (0 << 0); /* last recorded address not valid, next recordable address not valid */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U32(pbBuf + 8, 0); /* track start address is 0 */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U32(pbBuf + 24, s->cTotalSectors); /* track size */
f40cc8247b1da75ce42e73e6c557ec29b8f830a5vboxsync pbBuf[32] = 0; /* track number (MSB) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[33] = 0; /* session number (MSB) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync s->iSourceSink = ATAFN_SS_NULL;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync atapiCmdOK(s);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync return false;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync}
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsyncstatic bool atapiGetConfigurationSS(ATADevState *s)
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync{
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync Assert(s->cbElementaryTransfer <= 32);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync /* Accept valid request types only, and only starting feature 0. */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync if ((s->aATAPICmd[1] & 0x03) == 3 || ataBE2H_U16(&s->aATAPICmd[2]) != 0)
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync {
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync return false;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync }
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync memset(pbBuf, '\0', 32);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U32(pbBuf, 16);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync /** @todo implement switching between CD-ROM and DVD-ROM profile (the only
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync * way to differentiate them right now is based on the image size). Also
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync * implement signalling "no current profile" if no medium is loaded. */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U16(pbBuf + 6, 0x08); /* current profile: read-only CD */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U16(pbBuf + 8, 0); /* feature 0: list of profiles supported */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[10] = (0 << 2) | (1 << 1) | (1 || 0); /* version 0, persistent, current */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[11] = 8; /* additional bytes for profiles */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync /* The MMC-3 spec says that DVD-ROM read capability should be reported
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync * before CD-ROM read capability. */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U16(pbBuf + 12, 0x10); /* profile: read-only DVD */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[14] = (0 << 0); /* NOT current profile */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U16(pbBuf + 16, 0x08); /* profile: read only CD */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[18] = (1 << 0); /* current profile */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync /* Other profiles we might want to add in the future: 0x40 (BD-ROM) and 0x50 (HDDVD-ROM) */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync s->iSourceSink = ATAFN_SS_NULL;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync atapiCmdOK(s);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync return false;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync}
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsyncstatic bool atapiInquirySS(ATADevState *s)
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync{
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync Assert(s->cbElementaryTransfer <= 36);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[0] = 0x05; /* CD-ROM */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[1] = 0x80; /* removable */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync#if 1/*ndef VBOX*/ /** @todo implement MESN + AENC. (async notification on removal and stuff.) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[2] = 0x00; /* ISO */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync#else
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[2] = 0x00; /* ISO */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[3] = 0x91; /* format 1, MESN=1, AENC=9 ??? */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync#endif
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[4] = 31; /* additional length */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[5] = 0; /* reserved */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[6] = 0; /* reserved */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[7] = 0; /* reserved */
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataSCSIPadStr(pbBuf + 8, "VBOX", 8);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataSCSIPadStr(pbBuf + 16, "CD-ROM", 16);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataSCSIPadStr(pbBuf + 32, "1.0", 4);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync s->iSourceSink = ATAFN_SS_NULL;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync atapiCmdOK(s);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync return false;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync}
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsyncstatic bool atapiModeSenseErrorRecoverySS(ATADevState *s)
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync{
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync Assert(s->cbElementaryTransfer <= 16);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync ataH2BE_U16(&pbBuf[0], 16 + 6);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[2] = 0x70;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[3] = 0;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[4] = 0;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[5] = 0;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[6] = 0;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[7] = 0;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[8] = 0x01;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[9] = 0x06;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[10] = 0x00;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[11] = 0x05;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[12] = 0x00;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[13] = 0x00;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[14] = 0x00;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync pbBuf[15] = 0x00;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync s->iSourceSink = ATAFN_SS_NULL;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync atapiCmdOK(s);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync return false;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync}
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsyncstatic bool atapiModeSenseCDStatusSS(ATADevState *s)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync{
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync Assert(s->cbElementaryTransfer <= 40);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(&pbBuf[0], 38);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[2] = 0x70;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[3] = 0;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[4] = 0;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[5] = 0;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[6] = 0;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[7] = 0;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[8] = 0x2a;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[9] = 30; /* page length */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[10] = 0x08; /* DVD-ROM read support */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[11] = 0x00; /* no write support */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync /* The following claims we support audio play. This is obviously false,
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync * but the Linux generic CDROM support makes many features depend on this
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync * capability. If it's not set, this causes many things to be disabled. */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[12] = 0x71; /* multisession support, mode 2 form 1/2 support, audio play */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[13] = 0x00; /* no subchannel reads supported */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[14] = (1 << 0) | (1 << 3) | (1 << 5); /* lock supported, eject supported, tray type loading mechanism */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync if (s->pDrvMount->pfnIsLocked(s->pDrvMount))
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[14] |= 1 << 1; /* report lock state */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[15] = 0; /* no subchannel reads supported, no separate audio volume control, no changer etc. */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(&pbBuf[16], 5632); /* (obsolete) claim 32x speed support */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(&pbBuf[18], 2); /* number of audio volume levels */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(&pbBuf[20], s->cbIOBuffer / _1K); /* buffer size supported in Kbyte */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(&pbBuf[22], 5632); /* (obsolete) current read speed 32x */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[24] = 0; /* reserved */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[25] = 0; /* reserved for digital audio (see idx 15) */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(&pbBuf[26], 0); /* (obsolete) maximum write speed */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(&pbBuf[28], 0); /* (obsolete) current write speed */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(&pbBuf[30], 0); /* copy management revision supported 0=no CSS */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[32] = 0; /* reserved */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[33] = 0; /* reserved */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[34] = 0; /* reserved */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[35] = 1; /* rotation control CAV */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(&pbBuf[36], 0); /* current write speed */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(&pbBuf[38], 0); /* number of write speed performance descriptors */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync s->iSourceSink = ATAFN_SS_NULL;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync atapiCmdOK(s);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync return false;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync}
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsyncstatic bool atapiRequestSenseSS(ATADevState *s)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync{
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync memset(pbBuf, '\0', s->cbElementaryTransfer);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync memcpy(pbBuf, s->abATAPISense, RT_MIN(s->cbElementaryTransfer, sizeof(s->abATAPISense)));
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync s->iSourceSink = ATAFN_SS_NULL;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync atapiCmdOK(s);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync return false;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync}
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsyncstatic bool atapiMechanismStatusSS(ATADevState *s)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync{
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync Assert(s->cbElementaryTransfer <= 8);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(pbBuf, 0);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync /* no current LBA */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[2] = 0;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[3] = 0;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[4] = 0;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbBuf[5] = 1;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U16(pbBuf + 6, 0);
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync s->iSourceSink = ATAFN_SS_NULL;
8f7ee9e453c60b3b699799538a45950b35266665vboxsync atapiCmdOK(s);
8f7ee9e453c60b3b699799538a45950b35266665vboxsync return false;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync}
8f7ee9e453c60b3b699799538a45950b35266665vboxsync
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsyncstatic bool atapiReadTOCNormalSS(ATADevState *s)
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync{
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer), *q, iStartTrack;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync bool fMSF;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync uint32_t cbSize;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync fMSF = (s->aATAPICmd[1] >> 1) & 1;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync iStartTrack = s->aATAPICmd[6];
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync if (iStartTrack > 1 && iStartTrack != 0xaa)
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync {
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync return false;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync }
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync q = pbBuf + 2;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync *q++ = 1; /* first session */
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync *q++ = 1; /* last session */
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync if (iStartTrack <= 1)
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync {
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync *q++ = 0; /* reserved */
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync *q++ = 0x14; /* ADR, control */
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync *q++ = 1; /* track number */
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync *q++ = 0; /* reserved */
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync if (fMSF)
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync {
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* reserved */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync ataLBA2MSF(q, 0);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync q += 3;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync }
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync else
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync /* sector 0 */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync ataH2BE_U32(q, 0);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync q += 4;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync }
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync }
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync /* lead out track */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* reserved */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0x14; /* ADR, control */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0xaa; /* track number */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* reserved */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync if (fMSF)
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync {
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* reserved */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync ataLBA2MSF(q, s->cTotalSectors);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync q += 3;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync }
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync else
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync {
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync ataH2BE_U32(q, s->cTotalSectors);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync q += 4;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync }
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync cbSize = q - pbBuf;
0d8c2135d15345cc68111eea91052cdf5518d7e3vboxsync ataH2BE_U16(pbBuf, cbSize - 2);
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync if (cbSize < s->cbTotalTransfer)
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync s->cbTotalTransfer = cbSize;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync s->iSourceSink = ATAFN_SS_NULL;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync atapiCmdOK(s);
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync return false;
fdb40b7d2efa84fc6f03b7a695cb4b2e035c30c7vboxsync}
8f7ee9e453c60b3b699799538a45950b35266665vboxsync
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsyncstatic bool atapiReadTOCMultiSS(ATADevState *s)
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync{
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync bool fMSF;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync Assert(s->cbElementaryTransfer <= 12);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync fMSF = (s->aATAPICmd[1] >> 1) & 1;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync /* multi session: only a single session defined */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync/** @todo double-check this stuff against what a real drive says for a CD-ROM (not a CD-R) with only a single data session. Maybe solve the problem with "cdrdao read-toc" not being able to figure out whether numbers are in BCD or hex. */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync memset(pbBuf, 0, 12);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync pbBuf[1] = 0x0a;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync pbBuf[2] = 0x01;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync pbBuf[3] = 0x01;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync pbBuf[5] = 0x14; /* ADR, control */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync pbBuf[6] = 1; /* first track in last complete session */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync if (fMSF)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync pbBuf[8] = 0; /* reserved */
9c9db71d639cf066ed41d49629d46d48bff4be2fvboxsync ataLBA2MSF(&pbBuf[9], 0);
9c9db71d639cf066ed41d49629d46d48bff4be2fvboxsync }
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync else
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync {
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync /* sector 0 */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataH2BE_U32(pbBuf + 8, 0);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync }
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync s->iSourceSink = ATAFN_SS_NULL;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync atapiCmdOK(s);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync return false;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync}
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
88e56f700a3b8dfdf1646f96320f335e22339caavboxsyncstatic bool atapiReadTOCRawSS(ATADevState *s)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync{
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync uint8_t *pbBuf = s->CTX_SUFF(pbIOBuffer), *q, iStartTrack;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync bool fMSF;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync uint32_t cbSize;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync fMSF = (s->aATAPICmd[1] >> 1) & 1;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync iStartTrack = s->aATAPICmd[6];
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync q = pbBuf + 2;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 1; /* first session */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 1; /* last session */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 1; /* session number */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0x14; /* data track */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0; /* track number */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0xa0; /* first track in program area */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0; /* min */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0; /* sec */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0; /* frame */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0;
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 1; /* first track */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0x00; /* disk type CD-DA or CD data */
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *q++ = 0;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *q++ = 1; /* session number */
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *q++ = 0x14; /* data track */
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *q++ = 0; /* track number */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0xa1; /* last track in program area */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0; /* min */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* sec */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0; /* frame */
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *q++ = 0;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *q++ = 1; /* last track */
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *q++ = 0;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *q++ = 0;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *q++ = 1; /* session number */
0c94a8282c9042b02f022302a3d987746140eab9vboxsync *q++ = 0x14; /* data track */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0; /* track number */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0xa2; /* lead-out */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* min */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* sec */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* frame */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync if (fMSF)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0; /* reserved */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync ataLBA2MSF(q, s->cTotalSectors);
0c94a8282c9042b02f022302a3d987746140eab9vboxsync q += 3;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync }
0c94a8282c9042b02f022302a3d987746140eab9vboxsync else
0c94a8282c9042b02f022302a3d987746140eab9vboxsync {
0c94a8282c9042b02f022302a3d987746140eab9vboxsync ataH2BE_U32(q, s->cTotalSectors);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync q += 4;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 1; /* session number */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0x14; /* ADR, control */
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync *q++ = 0; /* track number */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 1; /* point */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* min */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* sec */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* frame */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync if (fMSF)
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync {
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync *q++ = 0; /* reserved */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync ataLBA2MSF(q, 0);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync q += 3;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync else
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /* sector 0 */
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync ataH2BE_U32(q, 0);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync q += 4;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync cbSize = q - pbBuf;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync ataH2BE_U16(pbBuf, cbSize - 2);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (cbSize < s->cbTotalTransfer)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cbTotalTransfer = cbSize;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->iSourceSink = ATAFN_SS_NULL;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdOK(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync return false;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync}
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsyncstatic void atapiParseCmdVirtualATAPI(ATADevState *s)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync{
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync const uint8_t *pbPacket;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint8_t *pbBuf;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint32_t cbMax;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync pbPacket = s->aATAPICmd;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync pbBuf = s->CTX_SUFF(pbIOBuffer);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync switch (pbPacket[0])
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c94a8282c9042b02f022302a3d987746140eab9vboxsync case SCSI_TEST_UNIT_READY:
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync if (s->cNotifiedMediaChange > 0)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync if (s->cNotifiedMediaChange-- > 2)
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync else
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync }
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync else if (s->pDrvMount->pfnIsMounted(s->pDrvMount))
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync atapiCmdOK(s);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync else
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync break;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync case SCSI_MODE_SENSE_10:
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync {
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync uint8_t uPageControl, uPageCode;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync cbMax = ataBE2H_U16(pbPacket + 7);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync uPageControl = pbPacket[2] >> 6;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync uPageCode = pbPacket[2] & 0x3f;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync switch (uPageControl)
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync {
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync case SCSI_PAGECONTROL_CURRENT:
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync switch (uPageCode)
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync {
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync case SCSI_MODEPAGE_ERROR_RECOVERY:
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync ataStartTransfer(s, RT_MIN(cbMax, 16), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY, true);
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync break;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync case SCSI_MODEPAGE_CD_STATUS:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync ataStartTransfer(s, RT_MIN(cbMax, 40), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS, true);
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync break;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync default:
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync goto error_cmd;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync break;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync case SCSI_PAGECONTROL_CHANGEABLE:
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync goto error_cmd;
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync case SCSI_PAGECONTROL_DEFAULT:
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync goto error_cmd;
88e56f700a3b8dfdf1646f96320f335e22339caavboxsync default:
49a6b09abb20015b0af3e618a1f92b7e26785e90vboxsync case SCSI_PAGECONTROL_SAVED:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_REQUEST_SENSE:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync cbMax = pbPacket[4];
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync ataStartTransfer(s, RT_MIN(cbMax, 18), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_REQUEST_SENSE, true);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (s->pDrvMount->pfnIsMounted(s->pDrvMount))
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (pbPacket[4] & 1)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->pDrvMount->pfnLock(s->pDrvMount);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync else
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->pDrvMount->pfnUnlock(s->pDrvMount);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdOK(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync else
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_READ_10:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_READ_12:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint32_t cSectors, iATAPILBA;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (s->cNotifiedMediaChange > 0)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cNotifiedMediaChange-- ;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (pbPacket[0] == SCSI_READ_10)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync cSectors = ataBE2H_U16(pbPacket + 7);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync else
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync cSectors = ataBE2H_U32(pbPacket + 6);
38745c55f37c31ba8b78cc728d2f08ea6eec38d6vboxsync iATAPILBA = ataBE2H_U32(pbPacket + 2);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if (cSectors == 0)
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync {
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync atapiCmdOK(s);
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
5c4d7e2aae42bbf39793dfa686925f076a56b4d5vboxsync if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /* Rate limited logging, one log line per second. For
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * guests that insist on reading from places outside the
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * valid area this often generates too many release log
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * entries otherwise. */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync static uint64_t uLastLogTS = 0;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (RTTimeMilliTS() >= uLastLogTS + 1000)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uLastLogTS = RTTimeMilliTS();
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiReadSectors(s, iATAPILBA, cSectors, 2048);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_READ_CD:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint32_t cSectors, iATAPILBA;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync if (s->cNotifiedMediaChange > 0)
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync s->cNotifiedMediaChange-- ;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync break;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync }
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync {
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync break;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync }
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8];
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync iATAPILBA = ataBE2H_U32(pbPacket + 2);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (cSectors == 0)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdOK(s);
56970d3a1944c7c073d66266cd52449835221badvboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if ((uint64_t)iATAPILBA + cSectors > s->cTotalSectors)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /* Rate limited logging, one log line per second. For
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * guests that insist on reading from places outside the
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync * valid area this often generates too many release log
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync * entries otherwise. */
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync static uint64_t uLastLogTS = 0;
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync if (RTTimeMilliTS() >= uLastLogTS + 1000)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync {
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", s->iLUN, (uint64_t)iATAPILBA + cSectors));
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync uLastLogTS = RTTimeMilliTS();
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync }
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync switch (pbPacket[9] & 0xf8)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync case 0x00:
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync /* nothing */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdOK(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 0x10:
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync /* normal read */
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync atapiReadSectors(s, iATAPILBA, cSectors, 2048);
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync break;
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync case 0xf8:
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync /* read all data */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiReadSectors(s, iATAPILBA, cSectors, 2352);
d7cf060b16385dd6e5af7c74cd49c9ef8ffb3b22vboxsync break;
56970d3a1944c7c073d66266cd52449835221badvboxsync default:
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync LogRel(("PIIX3 ATA: LUN#%d: CD-ROM sector format not supported\n", s->iLUN));
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync break;
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync }
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync }
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync break;
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync case SCSI_SEEK_10:
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync {
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync uint32_t iATAPILBA;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync if (s->cNotifiedMediaChange > 0)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync {
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync s->cNotifiedMediaChange-- ;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync break;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync }
82e3a4017d20f44c30ff909e6b825ff78139cbbbvboxsync else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync {
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync break;
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync }
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync iATAPILBA = ataBE2H_U32(pbPacket + 2);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (iATAPILBA > s->cTotalSectors)
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync {
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync /* Rate limited logging, one log line per second. For
d7cf060b16385dd6e5af7c74cd49c9ef8ffb3b22vboxsync * guests that insist on seeking to places outside the
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync * valid area this often generates too many release log
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * entries otherwise. */
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync static uint64_t uLastLogTS = 0;
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync if (RTTimeMilliTS() >= uLastLogTS + 1000)
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync LogRel(("PIIX3 ATA: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", s->iLUN, (uint64_t)iATAPILBA));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uLastLogTS = RTTimeMilliTS();
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync break;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdOK(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync ataSetStatus(s, ATA_STAT_SEEK); /* Linux expects this. */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync break;
19320d55d1417c39b3b5673a53aaa5ef177242c8vboxsync case SCSI_START_STOP_UNIT:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync int rc = VINF_SUCCESS;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync switch (pbPacket[4] & 3)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 0: /* 00 - Stop motor */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 1: /* 01 - Start motor */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 2: /* 10 - Eject media */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /* This must be done from EMT. */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
56970d3a1944c7c073d66266cd52449835221badvboxsync PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync PPDMDEVINS pDevIns = ATADEVSTATE_2_DEVINS(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync PVMREQ pReq;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync PDMCritSectLeave(&pCtl->lock);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync rc = VMR3ReqCall(PDMDevHlpGetVM(pDevIns), &pReq, RT_INDEFINITE_WAIT,
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync (PFNRT)s->pDrvMount->pfnUnmount, 2, s->pDrvMount, false);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync AssertReleaseRC(rc);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync VMR3ReqFree(pReq);
56970d3a1944c7c073d66266cd52449835221badvboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync STAM_PROFILE_START(&pCtl->StatLockWait, a);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 3: /* 11 - Load media */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (RT_SUCCESS(rc))
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdOK(s);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync else
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_MECHANISM_STATUS:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync cbMax = ataBE2H_U16(pbPacket + 8);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync ataStartTransfer(s, RT_MIN(cbMax, 8), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_MECHANISM_STATUS, true);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_READ_TOC_PMA_ATIP:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uint8_t format;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (s->cNotifiedMediaChange > 0)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cNotifiedMediaChange-- ;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync break;
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync }
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync cbMax = ataBE2H_U16(pbPacket + 7);
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync /* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits),
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * but Linux kernel uses offset 9 (topmost 2 bits). Hope that
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync * the other field is clear... */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6);
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync switch (format)
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync {
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync case 0:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_NORMAL, true);
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 1:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync ataStartTransfer(s, RT_MIN(cbMax, 12), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_MULTI, true);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case 2:
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync ataStartTransfer(s, cbMax, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TOC_RAW, true);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
56970d3a1944c7c073d66266cd52449835221badvboxsync default:
56970d3a1944c7c073d66266cd52449835221badvboxsync error_cmd:
56970d3a1944c7c073d66266cd52449835221badvboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
56970d3a1944c7c073d66266cd52449835221badvboxsync break;
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync }
56970d3a1944c7c073d66266cd52449835221badvboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_READ_CAPACITY:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (s->cNotifiedMediaChange > 0)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cNotifiedMediaChange-- ;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync ataStartTransfer(s, 8, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_CAPACITY, true);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_READ_DISC_INFORMATION:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync if (s->cNotifiedMediaChange > 0)
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync s->cNotifiedMediaChange-- ;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync break;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync }
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
9c9db71d639cf066ed41d49629d46d48bff4be2fvboxsync {
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync break;
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync }
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync cbMax = ataBE2H_U16(pbPacket + 7);
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync ataStartTransfer(s, RT_MIN(cbMax, 34), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_DISC_INFORMATION, true);
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync break;
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync case SCSI_READ_TRACK_INFORMATION:
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync if (s->cNotifiedMediaChange > 0)
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync {
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync s->cNotifiedMediaChange-- ;
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync break;
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync }
81614fc60e096e714022d10d38b70a36b9b21d48vboxsync else if (!s->pDrvMount->pfnIsMounted(s->pDrvMount))
44372afb953dc9f1f1ec71943f5f561a607c0307vboxsync {
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync break;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync }
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync cbMax = ataBE2H_U16(pbPacket + 7);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataStartTransfer(s, RT_MIN(cbMax, 36), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_READ_TRACK_INFORMATION, true);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync break;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync case SCSI_GET_CONFIGURATION:
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync /* No media change stuff here, it can confuse Linux guests. */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync cbMax = ataBE2H_U16(pbPacket + 7);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataStartTransfer(s, RT_MIN(cbMax, 32), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_GET_CONFIGURATION, true);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync break;
56970d3a1944c7c073d66266cd52449835221badvboxsync case SCSI_INQUIRY:
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync cbMax = pbPacket[4];
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync ataStartTransfer(s, RT_MIN(cbMax, 36), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_INQUIRY, true);
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync break;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync default:
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
56970d3a1944c7c073d66266cd52449835221badvboxsync break;
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync }
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync}
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync/*
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync * Parse ATAPI commands, passing them directly to the CD/DVD drive.
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync */
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsyncstatic void atapiParseCmdPassthrough(ATADevState *s)
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync{
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync const uint8_t *pbPacket;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync uint8_t *pbBuf;
56970d3a1944c7c073d66266cd52449835221badvboxsync uint32_t cSectors, iATAPILBA;
4f4cb69bca6bfda8f4d911759d1f3c6f528a173dvboxsync uint32_t cbTransfer = 0;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync PDMBLOCKTXDIR uTxDir = PDMBLOCKTXDIR_NONE;
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync
9540ab73f6cd0c76f44f6bbfe73f89ac145390b8vboxsync pbPacket = s->aATAPICmd;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync pbBuf = s->CTX_SUFF(pbIOBuffer);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync switch (pbPacket[0])
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync {
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync case SCSI_BLANK:
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync goto sendcmd;
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync case SCSI_CLOSE_TRACK_SESSION:
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync goto sendcmd;
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync case SCSI_ERASE_10:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync iATAPILBA = ataBE2H_U32(pbPacket + 2);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync cbTransfer = ataBE2H_U16(pbPacket + 7);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync Log2(("ATAPI PT: lba %d\n", iATAPILBA));
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
56970d3a1944c7c073d66266cd52449835221badvboxsync goto sendcmd;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_FORMAT_UNIT:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync cbTransfer = s->uATARegLCyl | (s->uATARegHCyl << 8); /* use ATAPI transfer length */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync goto sendcmd;
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync case SCSI_GET_CONFIGURATION:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync cbTransfer = ataBE2H_U16(pbPacket + 7);
29099c2d04b11e614f1fa399fab9e9162f2788b9vboxsync uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync goto sendcmd;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync#if 0
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync /* Disable this passthrough command. The guest should fallback to other means to
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * detect the disk status. We cannot emulate this command properly when in non-
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync * passthrough mode. */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_GET_EVENT_STATUS_NOTIFICATION:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync cbTransfer = ataBE2H_U16(pbPacket + 7);
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync goto sendcmd;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync#endif
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_GET_PERFORMANCE:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync cbTransfer = s->uATARegLCyl | (s->uATARegHCyl << 8); /* use ATAPI transfer length */
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync goto sendcmd;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync case SCSI_INQUIRY:
0c94a8282c9042b02f022302a3d987746140eab9vboxsync cbTransfer = pbPacket[4];
0c94a8282c9042b02f022302a3d987746140eab9vboxsync uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync goto sendcmd;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync case SCSI_LOAD_UNLOAD_MEDIUM:
0c94a8282c9042b02f022302a3d987746140eab9vboxsync goto sendcmd;
0c94a8282c9042b02f022302a3d987746140eab9vboxsync case SCSI_MECHANISM_STATUS:
0c94a8282c9042b02f022302a3d987746140eab9vboxsync cbTransfer = ataBE2H_U16(pbPacket + 8);
56970d3a1944c7c073d66266cd52449835221badvboxsync uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
56970d3a1944c7c073d66266cd52449835221badvboxsync goto sendcmd;
56970d3a1944c7c073d66266cd52449835221badvboxsync case SCSI_MODE_SELECT_10:
0c94a8282c9042b02f022302a3d987746140eab9vboxsync cbTransfer = ataBE2H_U16(pbPacket + 7);
0c94a8282c9042b02f022302a3d987746140eab9vboxsync uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync goto sendcmd;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_MODE_SENSE_10:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync cbTransfer = ataBE2H_U16(pbPacket + 7);
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
e250515922582e0410c9bcb6d24b0f17bef083a0vboxsync goto sendcmd;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_PAUSE_RESUME:
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync goto sendcmd;
0c69348b58bb8eabb1bea8867ee932b667bd0d34vboxsync case SCSI_PLAY_AUDIO_10:
goto sendcmd;
case SCSI_PLAY_AUDIO_12:
goto sendcmd;
case SCSI_PLAY_AUDIO_MSF:
goto sendcmd;
case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
/** @todo do not forget to unlock when a VM is shut down */
goto sendcmd;
case SCSI_READ_10:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U16(pbPacket + 7);
Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
s->cbATAPISector = 2048; /**< @todo this size is not always correct */
cbTransfer = cSectors * s->cbATAPISector;
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_12:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U32(pbPacket + 6);
Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
s->cbATAPISector = 2048; /**< @todo this size is not always correct */
cbTransfer = cSectors * s->cbATAPISector;
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_BUFFER:
cbTransfer = ataBE2H_U24(pbPacket + 6);
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_BUFFER_CAPACITY:
cbTransfer = ataBE2H_U16(pbPacket + 7);
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_CAPACITY:
cbTransfer = 8;
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_CD:
s->cbATAPISector = 2048; /**< @todo this size is not always correct */
cbTransfer = ataBE2H_U24(pbPacket + 6) / s->cbATAPISector * s->cbATAPISector;
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_CD_MSF:
cSectors = ataMSF2LBA(pbPacket + 6) - ataMSF2LBA(pbPacket + 3);
if (cSectors > 32)
cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */
s->cbATAPISector = 2048; /**< @todo this size is not always correct */
cbTransfer = cSectors * s->cbATAPISector;
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_DISC_INFORMATION:
cbTransfer = ataBE2H_U16(pbPacket + 7);
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_DVD_STRUCTURE:
cbTransfer = ataBE2H_U16(pbPacket + 8);
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_FORMAT_CAPACITIES:
cbTransfer = ataBE2H_U16(pbPacket + 7);
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_SUBCHANNEL:
cbTransfer = ataBE2H_U16(pbPacket + 7);
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_TOC_PMA_ATIP:
cbTransfer = ataBE2H_U16(pbPacket + 7);
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_READ_TRACK_INFORMATION:
cbTransfer = ataBE2H_U16(pbPacket + 7);
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_REPAIR_TRACK:
goto sendcmd;
case SCSI_REPORT_KEY:
cbTransfer = ataBE2H_U16(pbPacket + 8);
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_REQUEST_SENSE:
cbTransfer = pbPacket[4];
if ((s->abATAPISense[2] & 0x0f) != SCSI_SENSE_NONE)
{
ataStartTransfer(s, RT_MIN(cbTransfer, 18), PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_ATAPI_CMD, ATAFN_SS_ATAPI_REQUEST_SENSE, true);
break;
}
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_RESERVE_TRACK:
goto sendcmd;
case SCSI_SCAN:
goto sendcmd;
case SCSI_SEEK_10:
goto sendcmd;
case SCSI_SEND_CUE_SHEET:
cbTransfer = ataBE2H_U24(pbPacket + 6);
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
goto sendcmd;
case SCSI_SEND_DVD_STRUCTURE:
cbTransfer = ataBE2H_U16(pbPacket + 8);
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
goto sendcmd;
case SCSI_SEND_EVENT:
cbTransfer = ataBE2H_U16(pbPacket + 8);
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
goto sendcmd;
case SCSI_SEND_KEY:
cbTransfer = ataBE2H_U16(pbPacket + 8);
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
goto sendcmd;
case SCSI_SEND_OPC_INFORMATION:
cbTransfer = ataBE2H_U16(pbPacket + 7);
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
goto sendcmd;
case SCSI_SET_CD_SPEED:
goto sendcmd;
case SCSI_SET_READ_AHEAD:
goto sendcmd;
case SCSI_SET_STREAMING:
cbTransfer = ataBE2H_U16(pbPacket + 9);
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
goto sendcmd;
case SCSI_START_STOP_UNIT:
goto sendcmd;
case SCSI_STOP_PLAY_SCAN:
goto sendcmd;
case SCSI_SYNCHRONIZE_CACHE:
goto sendcmd;
case SCSI_TEST_UNIT_READY:
goto sendcmd;
case SCSI_VERIFY_10:
goto sendcmd;
case SCSI_WRITE_10:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U16(pbPacket + 7);
Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
#if 0
/* The sector size is determined by the async I/O thread. */
s->cbATAPISector = 0;
/* Preliminary, will be corrected once the sector size is known. */
cbTransfer = cSectors;
#else
s->cbATAPISector = 2048; /**< @todo this size is not always correct */
cbTransfer = cSectors * s->cbATAPISector;
#endif
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
goto sendcmd;
case SCSI_WRITE_12:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U32(pbPacket + 6);
Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
#if 0
/* The sector size is determined by the async I/O thread. */
s->cbATAPISector = 0;
/* Preliminary, will be corrected once the sector size is known. */
cbTransfer = cSectors;
#else
s->cbATAPISector = 2048; /**< @todo this size is not always correct */
cbTransfer = cSectors * s->cbATAPISector;
#endif
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
goto sendcmd;
case SCSI_WRITE_AND_VERIFY_10:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U16(pbPacket + 7);
Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
/* The sector size is determined by the async I/O thread. */
s->cbATAPISector = 0;
/* Preliminary, will be corrected once the sector size is known. */
cbTransfer = cSectors;
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
goto sendcmd;
case SCSI_WRITE_BUFFER:
switch (pbPacket[1] & 0x1f)
{
case 0x04: /* download microcode */
case 0x05: /* download microcode and save */
case 0x06: /* download microcode with offsets */
case 0x07: /* download microcode with offsets and save */
case 0x0e: /* download microcode with offsets and defer activation */
case 0x0f: /* activate deferred microcode */
LogRel(("PIIX3 ATA: LUN#%d: CD-ROM passthrough command attempted to update firmware, blocked\n", s->iLUN));
atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
break;
default:
cbTransfer = ataBE2H_U16(pbPacket + 6);
uTxDir = PDMBLOCKTXDIR_TO_DEVICE;
goto sendcmd;
}
break;
case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */
cbTransfer = ataBE2H_U32(pbPacket + 6);
uTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
goto sendcmd;
case SCSI_REZERO_UNIT:
/* Obsolete command used by cdrecord. What else would one expect?
* This command is not sent to the drive, it is handled internally,
* as the Linux kernel doesn't like it (message "scsi: unknown
* opcode 0x01" in syslog) and replies with a sense code of 0,
* which sends cdrecord to an endless loop. */
atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
break;
default:
LogRel(("PIIX3 ATA: LUN#%d: passthrough unimplemented for command %#x\n", s->iLUN, pbPacket[0]));
atapiCmdErrorSimple(s, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
break;
sendcmd:
/* Send a command to the drive, passing data in/out as required. */
Log2(("ATAPI PT: max size %d\n", cbTransfer));
Assert(cbTransfer <= s->cbIOBuffer);
if (cbTransfer == 0)
uTxDir = PDMBLOCKTXDIR_NONE;
ataStartTransfer(s, cbTransfer, uTxDir, ATAFN_BT_ATAPI_PASSTHROUGH_CMD, ATAFN_SS_ATAPI_PASSTHROUGH, true);
}
}
static void atapiParseCmd(ATADevState *s)
{
const uint8_t *pbPacket;
pbPacket = s->aATAPICmd;
#ifdef DEBUG
Log(("%s: LUN#%d DMA=%d CMD=%#04x \"%s\"\n", __FUNCTION__, s->iLUN, s->fDMA, pbPacket[0], SCSICmdText(pbPacket[0])));
#else /* !DEBUG */
Log(("%s: LUN#%d DMA=%d CMD=%#04x\n", __FUNCTION__, s->iLUN, s->fDMA, pbPacket[0]));
#endif /* !DEBUG */
Log2(("%s: limit=%#x packet: %.*Vhxs\n", __FUNCTION__, s->uATARegLCyl | (s->uATARegHCyl << 8), ATAPI_PACKET_SIZE, pbPacket));
if (s->fATAPIPassthrough)
atapiParseCmdPassthrough(s);
else
atapiParseCmdVirtualATAPI(s);
}
static bool ataPacketSS(ATADevState *s)
{
s->fDMA = !!(s->uATARegFeature & 1);
memcpy(s->aATAPICmd, s->CTX_SUFF(pbIOBuffer), ATAPI_PACKET_SIZE);
s->uTxDir = PDMBLOCKTXDIR_NONE;
s->cbTotalTransfer = 0;
s->cbElementaryTransfer = 0;
atapiParseCmd(s);
return false;
}
/**
* Called when a media is mounted.
*
* @param pInterface Pointer to the interface structure containing the called function pointer.
*/
static DECLCALLBACK(void) ataMountNotify(PPDMIMOUNTNOTIFY pInterface)
{
ATADevState *pIf = PDMIMOUNTNOTIFY_2_ATASTATE(pInterface);
Log(("%s: changing LUN#%d\n", __FUNCTION__, pIf->iLUN));
/* Ignore the call if we're called while being attached. */
if (!pIf->pDrvBlock)
return;
if (pIf->fATAPI)
pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 2048;
else
pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 512;
/* Report media changed in TEST UNIT and other (probably incorrect) places. */
if (pIf->cNotifiedMediaChange < 2)
pIf->cNotifiedMediaChange = 2;
}
/**
* Called when a media is unmounted
* @param pInterface Pointer to the interface structure containing the called function pointer.
*/
static DECLCALLBACK(void) ataUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
{
ATADevState *pIf = PDMIMOUNTNOTIFY_2_ATASTATE(pInterface);
Log(("%s:\n", __FUNCTION__));
pIf->cTotalSectors = 0;
/*
* Whatever I do, XP will not use the GET MEDIA STATUS nor the EVENT stuff.
* However, it will respond to TEST UNIT with a 0x6 0x28 (media changed) sense code.
* So, we'll give it 4 TEST UNIT command to catch up, two which the media is not
* present and 2 in which it is changed.
*/
pIf->cNotifiedMediaChange = 4;
}
static void ataPacketBT(ATADevState *s)
{
s->cbElementaryTransfer = s->cbTotalTransfer;
s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_CD;
Log2(("%s: interrupt reason %#04x\n", __FUNCTION__, s->uATARegNSector));
ataSetStatusValue(s, ATA_STAT_READY);
}
static void ataResetDevice(ATADevState *s)
{
s->cMultSectors = ATA_MAX_MULT_SECTORS;
s->cNotifiedMediaChange = 0;
ataUnsetIRQ(s);
s->uATARegSelect = 0x20;
ataSetStatusValue(s, ATA_STAT_READY);
ataSetSignature(s);
s->cbTotalTransfer = 0;
s->cbElementaryTransfer = 0;
s->iIOBufferPIODataStart = 0;
s->iIOBufferPIODataEnd = 0;
s->iBeginTransfer = ATAFN_BT_NULL;
s->iSourceSink = ATAFN_SS_NULL;
s->fATAPITransfer = false;
s->uATATransferMode = ATA_MODE_UDMA | 2; /* PIIX3 supports only up to UDMA2 */
s->uATARegFeature = 0;
}
static bool ataExecuteDeviceDiagnosticSS(ATADevState *s)
{
ataSetSignature(s);
if (s->fATAPI)
ataSetStatusValue(s, 0); /* NOTE: READY is _not_ set */
else
ataSetStatusValue(s, ATA_STAT_READY);
s->uATARegError = 0x01;
return false;
}
static void ataParseCmd(ATADevState *s, uint8_t cmd)
{
#ifdef DEBUG
Log(("%s: LUN#%d CMD=%#04x \"%s\"\n", __FUNCTION__, s->iLUN, cmd, ATACmdText(cmd)));
#else /* !DEBUG */
Log(("%s: LUN#%d CMD=%#04x\n", __FUNCTION__, s->iLUN, cmd));
#endif /* !DEBUG */
s->fLBA48 = false;
s->fDMA = false;
if (cmd == ATA_IDLE_IMMEDIATE)
{
/* Detect Linux timeout recovery, first tries IDLE IMMEDIATE (which
* would overwrite the failing command unfortunately), then RESET. */
int32_t uCmdWait = -1;
uint64_t uNow = RTTimeNanoTS();
if (s->u64CmdTS)
uCmdWait = (uNow - s->u64CmdTS) / 1000;
LogRel(("PIIX3 ATA: LUN#%d: IDLE IMMEDIATE, CmdIf=%#04x (%d usec ago)\n",
s->iLUN, s->uATARegCommand, uCmdWait));
}
s->uATARegCommand = cmd;
switch (cmd)
{
case ATA_IDENTIFY_DEVICE:
if (s->pDrvBlock && !s->fATAPI)
ataStartTransfer(s, 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_NULL, ATAFN_SS_IDENTIFY, false);
else
{
if (s->fATAPI)
ataSetSignature(s);
ataCmdError(s, ABRT_ERR);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
}
break;
case ATA_INITIALIZE_DEVICE_PARAMETERS:
case ATA_RECALIBRATE:
ataCmdOK(s, ATA_STAT_SEEK);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case ATA_SET_MULTIPLE_MODE:
if ( s->uATARegNSector != 0
&& ( s->uATARegNSector > ATA_MAX_MULT_SECTORS
|| (s->uATARegNSector & (s->uATARegNSector - 1)) != 0))
{
ataCmdError(s, ABRT_ERR);
}
else
{
Log2(("%s: set multi sector count to %d\n", __FUNCTION__, s->uATARegNSector));
s->cMultSectors = s->uATARegNSector;
ataCmdOK(s, 0);
}
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case ATA_READ_VERIFY_SECTORS_EXT:
s->fLBA48 = true;
case ATA_READ_VERIFY_SECTORS:
case ATA_READ_VERIFY_SECTORS_WITHOUT_RETRIES:
/* do sector number check ? */
ataCmdOK(s, 0);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case ATA_READ_SECTORS_EXT:
s->fLBA48 = true;
case ATA_READ_SECTORS:
case ATA_READ_SECTORS_WITHOUT_RETRIES:
if (!s->pDrvBlock)
goto abort_cmd;
s->cSectorsPerIRQ = 1;
ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
break;
case ATA_WRITE_SECTORS_EXT:
s->fLBA48 = true;
case ATA_WRITE_SECTORS:
case ATA_WRITE_SECTORS_WITHOUT_RETRIES:
s->cSectorsPerIRQ = 1;
ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
break;
case ATA_READ_MULTIPLE_EXT:
s->fLBA48 = true;
case ATA_READ_MULTIPLE:
if (!s->cMultSectors)
goto abort_cmd;
s->cSectorsPerIRQ = s->cMultSectors;
ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
break;
case ATA_WRITE_MULTIPLE_EXT:
s->fLBA48 = true;
case ATA_WRITE_MULTIPLE:
if (!s->cMultSectors)
goto abort_cmd;
s->cSectorsPerIRQ = s->cMultSectors;
ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
break;
case ATA_READ_DMA_EXT:
s->fLBA48 = true;
case ATA_READ_DMA:
case ATA_READ_DMA_WITHOUT_RETRIES:
if (!s->pDrvBlock)
goto abort_cmd;
s->cSectorsPerIRQ = ATA_MAX_MULT_SECTORS;
s->fDMA = true;
ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_READ_SECTORS, false);
break;
case ATA_WRITE_DMA_EXT:
s->fLBA48 = true;
case ATA_WRITE_DMA:
case ATA_WRITE_DMA_WITHOUT_RETRIES:
if (!s->pDrvBlock)
goto abort_cmd;
s->cSectorsPerIRQ = ATA_MAX_MULT_SECTORS;
s->fDMA = true;
ataStartTransfer(s, ataGetNSectors(s) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_READ_WRITE_SECTORS, ATAFN_SS_WRITE_SECTORS, false);
break;
case ATA_READ_NATIVE_MAX_ADDRESS_EXT:
s->fLBA48 = true;
ataSetSector(s, s->cTotalSectors - 1);
ataCmdOK(s, 0);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case ATA_SEEK: /* Used by the SCO OpenServer. Command is marked as obsolete */
ataCmdOK(s, 0);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case ATA_READ_NATIVE_MAX_ADDRESS:
ataSetSector(s, RT_MIN(s->cTotalSectors, 1 << 28) - 1);
ataCmdOK(s, 0);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case ATA_CHECK_POWER_MODE:
s->uATARegNSector = 0xff; /* drive active or idle */
ataCmdOK(s, 0);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case ATA_SET_FEATURES:
Log2(("%s: feature=%#x\n", __FUNCTION__, s->uATARegFeature));
if (!s->pDrvBlock)
goto abort_cmd;
switch (s->uATARegFeature)
{
case 0x02: /* write cache enable */
Log2(("%s: write cache enable\n", __FUNCTION__));
ataCmdOK(s, ATA_STAT_SEEK);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case 0xaa: /* read look-ahead enable */
Log2(("%s: read look-ahead enable\n", __FUNCTION__));
ataCmdOK(s, ATA_STAT_SEEK);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case 0x55: /* read look-ahead disable */
Log2(("%s: read look-ahead disable\n", __FUNCTION__));
ataCmdOK(s, ATA_STAT_SEEK);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case 0xcc: /* reverting to power-on defaults enable */
Log2(("%s: revert to power-on defaults enable\n", __FUNCTION__));
ataCmdOK(s, ATA_STAT_SEEK);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case 0x66: /* reverting to power-on defaults disable */
Log2(("%s: revert to power-on defaults disable\n", __FUNCTION__));
ataCmdOK(s, ATA_STAT_SEEK);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case 0x82: /* write cache disable */
Log2(("%s: write cache disable\n", __FUNCTION__));
/* As per the ATA/ATAPI-6 specs, a write cache disable
* command MUST flush the write buffers to disc. */
ataStartTransfer(s, 0, PDMBLOCKTXDIR_NONE, ATAFN_BT_NULL, ATAFN_SS_FLUSH, false);
break;
case 0x03: { /* set transfer mode */
Log2(("%s: transfer mode %#04x\n", __FUNCTION__, s->uATARegNSector));
switch (s->uATARegNSector & 0xf8)
{
case 0x00: /* PIO default */
case 0x08: /* PIO mode */
break;
case ATA_MODE_MDMA: /* MDMA mode */
s->uATATransferMode = (s->uATARegNSector & 0xf8) | RT_MIN(s->uATARegNSector & 0x07, ATA_MDMA_MODE_MAX);
break;
case ATA_MODE_UDMA: /* UDMA mode */
s->uATATransferMode = (s->uATARegNSector & 0xf8) | RT_MIN(s->uATARegNSector & 0x07, ATA_UDMA_MODE_MAX);
break;
default:
goto abort_cmd;
}
ataCmdOK(s, ATA_STAT_SEEK);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
}
default:
goto abort_cmd;
}
/*
* OS/2 workarond:
* The OS/2 IDE driver from MCP2 appears to rely on the feature register being
* reset here. According to the specification, this is a driver bug as the register
* contents are undefined after the call. This means we can just as well reset it.
*/
s->uATARegFeature = 0;
break;
case ATA_FLUSH_CACHE_EXT:
case ATA_FLUSH_CACHE:
if (!s->pDrvBlock || s->fATAPI)
goto abort_cmd;
ataStartTransfer(s, 0, PDMBLOCKTXDIR_NONE, ATAFN_BT_NULL, ATAFN_SS_FLUSH, false);
break;
case ATA_STANDBY_IMMEDIATE:
ataCmdOK(s, 0);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
case ATA_IDLE_IMMEDIATE:
LogRel(("PIIX3 ATA: LUN#%d: aborting current command\n", s->iLUN));
ataAbortCurrentCommand(s, false);
break;
/* ATAPI commands */
case ATA_IDENTIFY_PACKET_DEVICE:
if (s->fATAPI)
ataStartTransfer(s, 512, PDMBLOCKTXDIR_FROM_DEVICE, ATAFN_BT_NULL, ATAFN_SS_ATAPI_IDENTIFY, false);
else
{
ataCmdError(s, ABRT_ERR);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
}
break;
case ATA_EXECUTE_DEVICE_DIAGNOSTIC:
ataStartTransfer(s, 0, PDMBLOCKTXDIR_NONE, ATAFN_BT_NULL, ATAFN_SS_EXECUTE_DEVICE_DIAGNOSTIC, false);
break;
case ATA_DEVICE_RESET:
if (!s->fATAPI)
goto abort_cmd;
LogRel(("PIIX3 ATA: LUN#%d: performing device RESET\n", s->iLUN));
ataAbortCurrentCommand(s, true);
break;
case ATA_PACKET:
if (!s->fATAPI)
goto abort_cmd;
/* overlapping commands not supported */
if (s->uATARegFeature & 0x02)
goto abort_cmd;
ataStartTransfer(s, ATAPI_PACKET_SIZE, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_PACKET, ATAFN_SS_PACKET, false);
break;
default:
abort_cmd:
ataCmdError(s, ABRT_ERR);
ataSetIRQ(s); /* Shortcut, do not use AIO thread. */
break;
}
}
/**
* Waits for a particular async I/O thread to complete whatever it
* is doing at the moment.
*
* @returns true on success.
* @returns false when the thread is still processing.
* @param pThis Pointer to the controller data.
* @param cMillies How long to wait (total).
*/
static bool ataWaitForAsyncIOIsIdle(PATACONTROLLER pCtl, unsigned cMillies)
{
uint64_t u64Start;
/*
* Wait for any pending async operation to finish
*/
u64Start = RTTimeMilliTS();
for (;;)
{
if (ataAsyncIOIsIdle(pCtl, false))
return true;
if (RTTimeMilliTS() - u64Start >= cMillies)
break;
/* Sleep for a bit. */
RTThreadSleep(100);
}
return false;
}
#endif /* IN_RING3 */
static int ataIOPortWriteU8(PATACONTROLLER pCtl, uint32_t addr, uint32_t val)
{
Log2(("%s: write addr=%#x val=%#04x\n", __FUNCTION__, addr, val));
addr &= 7;
switch (addr)
{
case 0:
break;
case 1: /* feature register */
/* NOTE: data is written to the two drives */
pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
pCtl->aIfs[0].uATARegFeatureHOB = pCtl->aIfs[0].uATARegFeature;
pCtl->aIfs[1].uATARegFeatureHOB = pCtl->aIfs[1].uATARegFeature;
pCtl->aIfs[0].uATARegFeature = val;
pCtl->aIfs[1].uATARegFeature = val;
break;
case 2: /* sector count */
pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
pCtl->aIfs[0].uATARegNSectorHOB = pCtl->aIfs[0].uATARegNSector;
pCtl->aIfs[1].uATARegNSectorHOB = pCtl->aIfs[1].uATARegNSector;
pCtl->aIfs[0].uATARegNSector = val;
pCtl->aIfs[1].uATARegNSector = val;
break;
case 3: /* sector number */
pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
pCtl->aIfs[0].uATARegSectorHOB = pCtl->aIfs[0].uATARegSector;
pCtl->aIfs[1].uATARegSectorHOB = pCtl->aIfs[1].uATARegSector;
pCtl->aIfs[0].uATARegSector = val;
pCtl->aIfs[1].uATARegSector = val;
break;
case 4: /* cylinder low */
pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
pCtl->aIfs[0].uATARegLCylHOB = pCtl->aIfs[0].uATARegLCyl;
pCtl->aIfs[1].uATARegLCylHOB = pCtl->aIfs[1].uATARegLCyl;
pCtl->aIfs[0].uATARegLCyl = val;
pCtl->aIfs[1].uATARegLCyl = val;
break;
case 5: /* cylinder high */
pCtl->aIfs[0].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
pCtl->aIfs[1].uATARegDevCtl &= ~ATA_DEVCTL_HOB;
pCtl->aIfs[0].uATARegHCylHOB = pCtl->aIfs[0].uATARegHCyl;
pCtl->aIfs[1].uATARegHCylHOB = pCtl->aIfs[1].uATARegHCyl;
pCtl->aIfs[0].uATARegHCyl = val;
pCtl->aIfs[1].uATARegHCyl = val;
break;
case 6: /* drive/head */
pCtl->aIfs[0].uATARegSelect = (val & ~0x10) | 0xa0;
pCtl->aIfs[1].uATARegSelect = (val | 0x10) | 0xa0;
if (((val >> 4) & 1) != pCtl->iSelectedIf)
{
PPDMDEVINS pDevIns = CONTROLLER_2_DEVINS(pCtl);
/* select another drive */
pCtl->iSelectedIf = (val >> 4) & 1;
/* The IRQ line is multiplexed between the two drives, so
* update the state when switching to another drive. Only need
* to update interrupt line if it is enabled and there is a
* state change. */
if ( !(pCtl->aIfs[pCtl->iSelectedIf].uATARegDevCtl & ATA_DEVCTL_DISABLE_IRQ)
&& ( pCtl->aIfs[pCtl->iSelectedIf].fIrqPending
!= pCtl->aIfs[pCtl->iSelectedIf ^ 1].fIrqPending))
{
if (pCtl->aIfs[pCtl->iSelectedIf].fIrqPending)
{
Log2(("%s: LUN#%d asserting IRQ (drive select change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN));
/* The BMDMA unit unconditionally sets BM_STATUS_INT if
* the interrupt line is asserted. It monitors the line
* for a rising edge. */
pCtl->BmDma.u8Status |= BM_STATUS_INT;
if (pCtl->irq == 16)
PDMDevHlpPCISetIrqNoWait(pDevIns, 0, 1);
else
PDMDevHlpISASetIrqNoWait(pDevIns, pCtl->irq, 1);
}
else
{
Log2(("%s: LUN#%d deasserting IRQ (drive select change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN));
if (pCtl->irq == 16)
PDMDevHlpPCISetIrqNoWait(pDevIns, 0, 0);
else
PDMDevHlpISASetIrqNoWait(pDevIns, pCtl->irq, 0);
}
}
}
break;
default:
case 7: /* command */
/* ignore commands to non existant slave */
if (pCtl->iSelectedIf && !pCtl->aIfs[pCtl->iSelectedIf].pDrvBlock)
break;
#ifndef IN_RING3
/* Don't do anything complicated in GC */
return VINF_IOM_HC_IOPORT_WRITE;
#else /* IN_RING3 */
ataParseCmd(&pCtl->aIfs[pCtl->iSelectedIf], val);
#endif /* !IN_RING3 */
}
return VINF_SUCCESS;
}
static int ataIOPortReadU8(PATACONTROLLER pCtl, uint32_t addr, uint32_t *pu32)
{
ATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
uint32_t val;
bool fHOB;
fHOB = !!(s->uATARegDevCtl & (1 << 7));
switch (addr & 7)
{
case 0: /* data register */
val = 0xff;
break;
case 1: /* error register */
/* The ATA specification is very terse when it comes to specifying
* the precise effects of reading back the error/feature register.
* The error register (read-only) shares the register number with
* the feature register (write-only), so it seems that it's not
* necessary to support the usual HOB readback here. */
if (!s->pDrvBlock)
val = 0;
else
val = s->uATARegError;
break;
case 2: /* sector count */
if (!s->pDrvBlock)
val = 0;
else if (fHOB)
val = s->uATARegNSectorHOB;
else
val = s->uATARegNSector;
break;
case 3: /* sector number */
if (!s->pDrvBlock)
val = 0;
else if (fHOB)
val = s->uATARegSectorHOB;
else
val = s->uATARegSector;
break;
case 4: /* cylinder low */
if (!s->pDrvBlock)
val = 0;
else if (fHOB)
val = s->uATARegLCylHOB;
else
val = s->uATARegLCyl;
break;
case 5: /* cylinder high */
if (!s->pDrvBlock)
val = 0;
else if (fHOB)
val = s->uATARegHCylHOB;
else
val = s->uATARegHCyl;
break;
case 6: /* drive/head */
/* This register must always work as long as there is at least
* one drive attached to the controller. It is common between
* both drives anyway (completely identical content). */
if (!pCtl->aIfs[0].pDrvBlock && !pCtl->aIfs[1].pDrvBlock)
val = 0;
else
val = s->uATARegSelect;
break;
default:
case 7: /* primary status */
{
/* Counter for number of busy status seen in GC in a row. */
static unsigned cBusy = 0;
if (!s->pDrvBlock)
val = 0;
else
val = s->uATARegStatus;
/* Give the async I/O thread an opportunity to make progress,
* don't let it starve by guests polling frequently. EMT has a
* lower priority than the async I/O thread, but sometimes the
* host OS doesn't care. With some guests we are only allowed to
* be busy for about 5 milliseconds in some situations. Note that
* this is no guarantee for any other VBox thread getting
* scheduled, so this just lowers the CPU load a bit when drives
* are busy. It cannot help with timing problems. */
if (val & ATA_STAT_BUSY)
{
#ifdef IN_RING3
cBusy = 0;
PDMCritSectLeave(&pCtl->lock);
RTThreadYield();
{
STAM_PROFILE_START(&pCtl->StatLockWait, a);
PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
}
val = s->uATARegStatus;
#else /* !IN_RING3 */
/* Cannot yield CPU in guest context. And switching to host
* context for each and every busy status is too costly,
* especially on SMP systems where we don't gain much by
* yielding the CPU to someone else. */
if (++cBusy >= 20)
{
cBusy = 0;
return VINF_IOM_HC_IOPORT_READ;
}
#endif /* !IN_RING3 */
}
else
cBusy = 0;
ataUnsetIRQ(s);
break;
}
}
Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val));
*pu32 = val;
return VINF_SUCCESS;
}
static uint32_t ataStatusRead(PATACONTROLLER pCtl, uint32_t addr)
{
ATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
uint32_t val;
if ((!pCtl->aIfs[0].pDrvBlock && !pCtl->aIfs[1].pDrvBlock) ||
(pCtl->iSelectedIf == 1 && !s->pDrvBlock))
val = 0;
else
val = s->uATARegStatus;
Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val));
return val;
}
static int ataControlWrite(PATACONTROLLER pCtl, uint32_t addr, uint32_t val)
{
#ifndef IN_RING3
if ((val ^ pCtl->aIfs[0].uATARegDevCtl) & ATA_DEVCTL_RESET)
return VINF_IOM_HC_IOPORT_WRITE; /* The RESET stuff is too complicated for GC. */
#endif /* !IN_RING3 */
Log2(("%s: addr=%#x val=%#04x\n", __FUNCTION__, addr, val));
/* RESET is common for both drives attached to a controller. */
if (!(pCtl->aIfs[0].uATARegDevCtl & ATA_DEVCTL_RESET) &&
(val & ATA_DEVCTL_RESET))
{
#ifdef IN_RING3
/* Software RESET low to high */
int32_t uCmdWait0 = -1, uCmdWait1 = -1;
uint64_t uNow = RTTimeNanoTS();
if (pCtl->aIfs[0].u64CmdTS)
uCmdWait0 = (uNow - pCtl->aIfs[0].u64CmdTS) / 1000;
if (pCtl->aIfs[1].u64CmdTS)
uCmdWait1 = (uNow - pCtl->aIfs[1].u64CmdTS) / 1000;
LogRel(("PIIX3 ATA: Ctl#%d: RESET, DevSel=%d AIOIf=%d CmdIf0=%#04x (%d usec ago) CmdIf1=%#04x (%d usec ago)\n",
ATACONTROLLER_IDX(pCtl), pCtl->iSelectedIf, pCtl->iAIOIf,
pCtl->aIfs[0].uATARegCommand, uCmdWait0,
pCtl->aIfs[1].uATARegCommand, uCmdWait1));
pCtl->fReset = true;
/* Everything must be done after the reset flag is set, otherwise
* there are unavoidable races with the currently executing request
* (which might just finish in the mean time). */
pCtl->fChainedTransfer = false;
for (uint32_t i = 0; i < RT_ELEMENTS(pCtl->aIfs); i++)
{
ataResetDevice(&pCtl->aIfs[i]);
/* The following cannot be done using ataSetStatusValue() since the
* reset flag is already set, which suppresses all status changes. */
pCtl->aIfs[i].uATARegStatus = ATA_STAT_BUSY | ATA_STAT_SEEK;
Log2(("%s: LUN#%d status %#04x\n", __FUNCTION__, pCtl->aIfs[i].iLUN, pCtl->aIfs[i].uATARegStatus));
pCtl->aIfs[i].uATARegError = 0x01;
}
ataAsyncIOClearRequests(pCtl);
Log2(("%s: Ctl#%d: message to async I/O thread, resetA\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
if (val & ATA_DEVCTL_HOB)
{
val &= ~ATA_DEVCTL_HOB;
Log2(("%s: ignored setting HOB\n", __FUNCTION__));
}
ataAsyncIOPutRequest(pCtl, &ataResetARequest);
#else /* !IN_RING3 */
AssertMsgFailed(("RESET handling is too complicated for GC\n"));
#endif /* IN_RING3 */
}
else if ((pCtl->aIfs[0].uATARegDevCtl & ATA_DEVCTL_RESET) &&
!(val & ATA_DEVCTL_RESET))
{
#ifdef IN_RING3
/* Software RESET high to low */
Log(("%s: deasserting RESET\n", __FUNCTION__));
Log2(("%s: Ctl#%d: message to async I/O thread, resetC\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
if (val & ATA_DEVCTL_HOB)
{
val &= ~ATA_DEVCTL_HOB;
Log2(("%s: ignored setting HOB\n", __FUNCTION__));
}
ataAsyncIOPutRequest(pCtl, &ataResetCRequest);
#else /* !IN_RING3 */
AssertMsgFailed(("RESET handling is too complicated for GC\n"));
#endif /* IN_RING3 */
}
/* Change of interrupt disable flag. Update interrupt line if interrupt
* is pending on the current interface. */
if ((val ^ pCtl->aIfs[0].uATARegDevCtl) & ATA_DEVCTL_DISABLE_IRQ
&& pCtl->aIfs[pCtl->iSelectedIf].fIrqPending)
{
if (!(val & ATA_DEVCTL_DISABLE_IRQ))
{
Log2(("%s: LUN#%d asserting IRQ (interrupt disable change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN));
/* The BMDMA unit unconditionally sets BM_STATUS_INT if the
* interrupt line is asserted. It monitors the line for a rising
* edge. */
pCtl->BmDma.u8Status |= BM_STATUS_INT;
if (pCtl->irq == 16)
PDMDevHlpPCISetIrqNoWait(CONTROLLER_2_DEVINS(pCtl), 0, 1);
else
PDMDevHlpISASetIrqNoWait(CONTROLLER_2_DEVINS(pCtl), pCtl->irq, 1);
}
else
{
Log2(("%s: LUN#%d deasserting IRQ (interrupt disable change)\n", __FUNCTION__, pCtl->aIfs[pCtl->iSelectedIf].iLUN));
if (pCtl->irq == 16)
PDMDevHlpPCISetIrqNoWait(CONTROLLER_2_DEVINS(pCtl), 0, 0);
else
PDMDevHlpISASetIrqNoWait(CONTROLLER_2_DEVINS(pCtl), pCtl->irq, 0);
}
}
if (val & ATA_DEVCTL_HOB)
Log2(("%s: set HOB\n", __FUNCTION__));
pCtl->aIfs[0].uATARegDevCtl = val;
pCtl->aIfs[1].uATARegDevCtl = val;
return VINF_SUCCESS;
}
#ifdef IN_RING3
static void ataPIOTransfer(PATACONTROLLER pCtl)
{
ATADevState *s;
s = &pCtl->aIfs[pCtl->iAIOIf];
Log3(("%s: if=%p\n", __FUNCTION__, s));
if (s->cbTotalTransfer && s->iIOBufferCur > s->iIOBufferEnd)
{
LogRel(("PIIX3 ATA: LUN#%d: %s data in the middle of a PIO transfer - VERY SLOW\n", s->iLUN, s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "loading" : "storing"));
/* Any guest OS that triggers this case has a pathetic ATA driver.
* In a real system it would block the CPU via IORDY, here we do it
* very similarly by not continuing with the current instruction
* until the transfer to/from the storage medium is completed. */
if (s->iSourceSink != ATAFN_SS_NULL)
{
bool fRedo;
uint8_t status = s->uATARegStatus;
ataSetStatusValue(s, ATA_STAT_BUSY);
Log2(("%s: calling source/sink function\n", __FUNCTION__));
fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
pCtl->fRedo = fRedo;
if (RT_UNLIKELY(fRedo))
return;
ataSetStatusValue(s, status);
s->iIOBufferCur = 0;
s->iIOBufferEnd = s->cbElementaryTransfer;
}
}
if (s->cbTotalTransfer)
{
if (s->fATAPITransfer)
ataPIOTransferLimitATAPI(s);
if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE && s->cbElementaryTransfer > s->cbTotalTransfer)
s->cbElementaryTransfer = s->cbTotalTransfer;
Log2(("%s: %s tx_size=%d elem_tx_size=%d index=%d end=%d\n",
__FUNCTION__, s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "T2I" : "I2T",
s->cbTotalTransfer, s->cbElementaryTransfer,
s->iIOBufferCur, s->iIOBufferEnd));
ataPIOTransferStart(s, s->iIOBufferCur, s->cbElementaryTransfer);
s->cbTotalTransfer -= s->cbElementaryTransfer;
s->iIOBufferCur += s->cbElementaryTransfer;
if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE && s->cbElementaryTransfer > s->cbTotalTransfer)
s->cbElementaryTransfer = s->cbTotalTransfer;
}
else
ataPIOTransferStop(s);
}
DECLINLINE(void) ataPIOTransferFinish(PATACONTROLLER pCtl, ATADevState *s)
{
/* Do not interfere with RESET processing if the PIO transfer finishes
* while the RESET line is asserted. */
if (pCtl->fReset)
{
Log2(("%s: Ctl#%d: suppressed continuing PIO transfer as RESET is active\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
return;
}
if ( s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE
|| ( s->iSourceSink != ATAFN_SS_NULL
&& s->iIOBufferCur >= s->iIOBufferEnd))
{
/* Need to continue the transfer in the async I/O thread. This is
* the case for write operations or generally for not yet finished
* transfers (some data might need to be read). */
ataUnsetStatus(s, ATA_STAT_READY | ATA_STAT_DRQ);
ataSetStatus(s, ATA_STAT_BUSY);
Log2(("%s: Ctl#%d: message to async I/O thread, continuing PIO transfer\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
ataAsyncIOPutRequest(pCtl, &ataPIORequest);
}
else
{
/* Either everything finished (though some data might still be pending)
* or some data is pending before the next read is due. */
/* Continue a previously started transfer. */
ataUnsetStatus(s, ATA_STAT_DRQ);
ataSetStatus(s, ATA_STAT_READY);
if (s->cbTotalTransfer)
{
/* There is more to transfer, happens usually for large ATAPI
* reads - the protocol limits the chunk size to 65534 bytes. */
ataPIOTransfer(pCtl);
ataSetIRQ(s);
}
else
{
Log2(("%s: Ctl#%d: skipping message to async I/O thread, ending PIO transfer\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
/* Finish PIO transfer. */
ataPIOTransfer(pCtl);
Assert(!pCtl->fRedo);
}
}
}
#endif /* IN_RING3 */
static int ataDataWrite(PATACONTROLLER pCtl, uint32_t addr, uint32_t cbSize, const uint8_t *pbBuf)
{
ATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
uint8_t *p;
if (s->iIOBufferPIODataStart < s->iIOBufferPIODataEnd)
{
Assert(s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE);
p = s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart;
#ifndef IN_RING3
/* All but the last transfer unit is simple enough for GC, but
* sending a request to the async IO thread is too complicated. */
if (s->iIOBufferPIODataStart + cbSize < s->iIOBufferPIODataEnd)
{
memcpy(p, pbBuf, cbSize);
s->iIOBufferPIODataStart += cbSize;
}
else
return VINF_IOM_HC_IOPORT_WRITE;
#else /* IN_RING3 */
memcpy(p, pbBuf, cbSize);
s->iIOBufferPIODataStart += cbSize;
if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd)
ataPIOTransferFinish(pCtl, s);
#endif /* !IN_RING3 */
}
else
Log2(("%s: DUMMY data\n", __FUNCTION__));
Log3(("%s: addr=%#x val=%.*Vhxs\n", __FUNCTION__, addr, cbSize, pbBuf));
return VINF_SUCCESS;
}
static int ataDataRead(PATACONTROLLER pCtl, uint32_t addr, uint32_t cbSize, uint8_t *pbBuf)
{
ATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
uint8_t *p;
if (s->iIOBufferPIODataStart < s->iIOBufferPIODataEnd)
{
Assert(s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE);
p = s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart;
#ifndef IN_RING3
/* All but the last transfer unit is simple enough for GC, but
* sending a request to the async IO thread is too complicated. */
if (s->iIOBufferPIODataStart + cbSize < s->iIOBufferPIODataEnd)
{
memcpy(pbBuf, p, cbSize);
s->iIOBufferPIODataStart += cbSize;
}
else
return VINF_IOM_HC_IOPORT_READ;
#else /* IN_RING3 */
memcpy(pbBuf, p, cbSize);
s->iIOBufferPIODataStart += cbSize;
if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd)
ataPIOTransferFinish(pCtl, s);
#endif /* !IN_RING3 */
}
else
{
Log2(("%s: DUMMY data\n", __FUNCTION__));
memset(pbBuf, '\xff', cbSize);
}
Log3(("%s: addr=%#x val=%.*Vhxs\n", __FUNCTION__, addr, cbSize, pbBuf));
return VINF_SUCCESS;
}
#ifdef IN_RING3
static void ataDMATransferStop(ATADevState *s)
{
s->cbTotalTransfer = 0;
s->cbElementaryTransfer = 0;
s->iBeginTransfer = ATAFN_BT_NULL;
s->iSourceSink = ATAFN_SS_NULL;
}
/**
* Perform the entire DMA transfer in one go (unless a source/sink operation
* has to be redone or a RESET comes in between). Unlike the PIO counterpart
* this function cannot handle empty transfers.
*
* @param pCtl Controller for which to perform the transfer.
*/
static void ataDMATransfer(PATACONTROLLER pCtl)
{
PPDMDEVINS pDevIns = CONTROLLER_2_DEVINS(pCtl);
ATADevState *s = &pCtl->aIfs[pCtl->iAIOIf];
bool fRedo;
RTGCPHYS32 pDesc;
uint32_t cbTotalTransfer, cbElementaryTransfer;
uint32_t iIOBufferCur, iIOBufferEnd;
uint32_t dmalen;
PDMBLOCKTXDIR uTxDir;
bool fLastDesc = false;
Assert(sizeof(BMDMADesc) == 8);
fRedo = pCtl->fRedo;
if (RT_LIKELY(!fRedo))
Assert(s->cbTotalTransfer);
uTxDir = (PDMBLOCKTXDIR)s->uTxDir;
cbTotalTransfer = s->cbTotalTransfer;
cbElementaryTransfer = s->cbElementaryTransfer;
iIOBufferCur = s->iIOBufferCur;
iIOBufferEnd = s->iIOBufferEnd;
/* The DMA loop is designed to hold the lock only when absolutely
* necessary. This avoids long freezes should the guest access the
* ATA registers etc. for some reason. */
PDMCritSectLeave(&pCtl->lock);
Log2(("%s: %s tx_size=%d elem_tx_size=%d index=%d end=%d\n",
__FUNCTION__, uTxDir == PDMBLOCKTXDIR_FROM_DEVICE ? "T2I" : "I2T",
cbTotalTransfer, cbElementaryTransfer,
iIOBufferCur, iIOBufferEnd));
for (pDesc = pCtl->pFirstDMADesc; pDesc <= pCtl->pLastDMADesc; pDesc += sizeof(BMDMADesc))
{
BMDMADesc DMADesc;
RTGCPHYS32 pBuffer;
uint32_t cbBuffer;
if (RT_UNLIKELY(fRedo))
{
pBuffer = pCtl->pRedoDMABuffer;
cbBuffer = pCtl->cbRedoDMABuffer;
fLastDesc = pCtl->fRedoDMALastDesc;
}
else
{
PDMDevHlpPhysRead(pDevIns, pDesc, &DMADesc, sizeof(BMDMADesc));
pBuffer = RT_LE2H_U32(DMADesc.pBuffer);
cbBuffer = RT_LE2H_U32(DMADesc.cbBuffer);
fLastDesc = !!(cbBuffer & 0x80000000);
cbBuffer &= 0xfffe;
if (cbBuffer == 0)
cbBuffer = 0x10000;
if (cbBuffer > cbTotalTransfer)
cbBuffer = cbTotalTransfer;
}
while (RT_UNLIKELY(fRedo) || (cbBuffer && cbTotalTransfer))
{
if (RT_LIKELY(!fRedo))
{
dmalen = RT_MIN(cbBuffer, iIOBufferEnd - iIOBufferCur);
Log2(("%s: DMA desc %#010x: addr=%#010x size=%#010x\n", __FUNCTION__,
(int)pDesc, pBuffer, cbBuffer));
if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
PDMDevHlpPhysWrite(pDevIns, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen);
else
PDMDevHlpPhysRead(pDevIns, pBuffer, s->CTX_SUFF(pbIOBuffer) + iIOBufferCur, dmalen);
iIOBufferCur += dmalen;
cbTotalTransfer -= dmalen;
cbBuffer -= dmalen;
pBuffer += dmalen;
}
if ( iIOBufferCur == iIOBufferEnd
&& (uTxDir == PDMBLOCKTXDIR_TO_DEVICE || cbTotalTransfer))
{
if (uTxDir == PDMBLOCKTXDIR_FROM_DEVICE && cbElementaryTransfer > cbTotalTransfer)
cbElementaryTransfer = cbTotalTransfer;
{
STAM_PROFILE_START(&pCtl->StatLockWait, a);
PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
}
/* The RESET handler could have cleared the DMA transfer
* state (since we didn't hold the lock until just now
* the guest can continue in parallel). If so, the state
* is already set up so the loop is exited immediately. */
if (s->iSourceSink != ATAFN_SS_NULL)
{
s->iIOBufferCur = iIOBufferCur;
s->iIOBufferEnd = iIOBufferEnd;
s->cbElementaryTransfer = cbElementaryTransfer;
s->cbTotalTransfer = cbTotalTransfer;
Log2(("%s: calling source/sink function\n", __FUNCTION__));
fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
if (RT_UNLIKELY(fRedo))
{
pCtl->pFirstDMADesc = pDesc;
pCtl->pRedoDMABuffer = pBuffer;
pCtl->cbRedoDMABuffer = cbBuffer;
pCtl->fRedoDMALastDesc = fLastDesc;
}
else
{
cbTotalTransfer = s->cbTotalTransfer;
cbElementaryTransfer = s->cbElementaryTransfer;
if (uTxDir == PDMBLOCKTXDIR_TO_DEVICE && cbElementaryTransfer > cbTotalTransfer)
cbElementaryTransfer = cbTotalTransfer;
iIOBufferCur = 0;
iIOBufferEnd = cbElementaryTransfer;
}
pCtl->fRedo = fRedo;
}
else
{
/* This forces the loop to exit immediately. */
pDesc = pCtl->pLastDMADesc + 1;
}
PDMCritSectLeave(&pCtl->lock);
if (RT_UNLIKELY(fRedo))
break;
}
}
if (RT_UNLIKELY(fRedo))
break;
/* end of transfer */
if (!cbTotalTransfer || fLastDesc)
break;
{
STAM_PROFILE_START(&pCtl->StatLockWait, a);
PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
}
if (!(pCtl->BmDma.u8Cmd & BM_CMD_START) || pCtl->fReset)
{
LogRel(("PIIX3 ATA: Ctl#%d: ABORT DMA%s\n", ATACONTROLLER_IDX(pCtl), pCtl->fReset ? " due to RESET" : ""));
if (!pCtl->fReset)
ataDMATransferStop(s);
/* This forces the loop to exit immediately. */
pDesc = pCtl->pLastDMADesc + 1;
}
PDMCritSectLeave(&pCtl->lock);
}
{
STAM_PROFILE_START(&pCtl->StatLockWait, a);
PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
}
if (RT_UNLIKELY(fRedo))
return;
if (fLastDesc)
pCtl->BmDma.u8Status &= ~BM_STATUS_DMAING;
s->cbTotalTransfer = cbTotalTransfer;
s->cbElementaryTransfer = cbElementaryTransfer;
s->iIOBufferCur = iIOBufferCur;
s->iIOBufferEnd = iIOBufferEnd;
}
/**
* Suspend I/O operations on a controller. Also suspends EMT, because it's
* waiting for I/O to make progress. The next attempt to perform an I/O
* operation will be made when EMT is resumed up again (as the resume
* callback below restarts I/O).
*
* @param pCtl Controller for which to suspend I/O.
*/
static void ataSuspendRedo(PATACONTROLLER pCtl)
{
PPDMDEVINS pDevIns = CONTROLLER_2_DEVINS(pCtl);
PVMREQ pReq;
int rc;
pCtl->fRedoIdle = true;
rc = VMR3ReqCall(PDMDevHlpGetVM(pDevIns), &pReq, RT_INDEFINITE_WAIT,
(PFNRT)PDMDevHlpVMSuspend, 1, pDevIns);
AssertReleaseRC(rc);
VMR3ReqFree(pReq);
}
/** Asynch I/O thread for an interface. Once upon a time this was readable
* code with several loops and a different semaphore for each purpose. But
* then came the "how can one save the state in the middle of a PIO transfer"
* question. The solution was to use an ASM, which is what's there now. */
static DECLCALLBACK(int) ataAsyncIOLoop(RTTHREAD ThreadSelf, void *pvUser)
{
const ATARequest *pReq;
uint64_t u64TS = 0; /* shut up gcc */
uint64_t uWait;
int rc = VINF_SUCCESS;
PATACONTROLLER pCtl = (PATACONTROLLER)pvUser;
ATADevState *s;
pReq = NULL;
pCtl->fChainedTransfer = false;
while (!pCtl->fShutdown)
{
/* Keep this thread from doing anything as long as EMT is suspended. */
while (pCtl->fRedoIdle)
{
rc = RTSemEventWait(pCtl->SuspendIOSem, RT_INDEFINITE_WAIT);
if (RT_FAILURE(rc) || pCtl->fShutdown)
break;
pCtl->fRedoIdle = false;
}
/* Wait for work. */
if (pReq == NULL)
{
LogBird(("ata: %x: going to sleep...\n", pCtl->IOPortBase1));
rc = RTSemEventWait(pCtl->AsyncIOSem, RT_INDEFINITE_WAIT);
LogBird(("ata: %x: waking up\n", pCtl->IOPortBase1));
if (RT_FAILURE(rc) || pCtl->fShutdown)
break;
pReq = ataAsyncIOGetCurrentRequest(pCtl);
}
if (pReq == NULL)
continue;
ATAAIO ReqType = pReq->ReqType;
Log2(("%s: Ctl#%d: state=%d, req=%d\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl), pCtl->uAsyncIOState, ReqType));
if (pCtl->uAsyncIOState != ReqType)
{
/* The new state is not the state that was expected by the normal
* state changes. This is either a RESET/ABORT or there's something
* really strange going on. */
if ( (pCtl->uAsyncIOState == ATA_AIO_PIO || pCtl->uAsyncIOState == ATA_AIO_DMA)
&& (ReqType == ATA_AIO_PIO || ReqType == ATA_AIO_DMA))
{
/* Incorrect sequence of PIO/DMA states. Dump request queue. */
ataAsyncIODumpRequests(pCtl);
}
AssertReleaseMsg(ReqType == ATA_AIO_RESET_ASSERTED || ReqType == ATA_AIO_RESET_CLEARED || ReqType == ATA_AIO_ABORT || pCtl->uAsyncIOState == ReqType, ("I/O state inconsistent: state=%d request=%d\n", pCtl->uAsyncIOState, ReqType));
}
/* Do our work. */
{
STAM_PROFILE_START(&pCtl->StatLockWait, a);
LogBird(("ata: %x: entering critsect\n", pCtl->IOPortBase1));
PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
LogBird(("ata: %x: entered\n", pCtl->IOPortBase1));
STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
}
if (pCtl->uAsyncIOState == ATA_AIO_NEW && !pCtl->fChainedTransfer)
{
u64TS = RTTimeNanoTS();
#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
STAM_PROFILE_ADV_START(&pCtl->StatAsyncTime, a);
#endif /* DEBUG || VBOX_WITH_STATISTICS */
}
switch (ReqType)
{
case ATA_AIO_NEW:
pCtl->iAIOIf = pReq->u.t.iIf;
s = &pCtl->aIfs[pCtl->iAIOIf];
s->cbTotalTransfer = pReq->u.t.cbTotalTransfer;
s->uTxDir = pReq->u.t.uTxDir;
s->iBeginTransfer = pReq->u.t.iBeginTransfer;
s->iSourceSink = pReq->u.t.iSourceSink;
s->iIOBufferEnd = 0;
s->u64CmdTS = u64TS;
if (s->fATAPI)
{
if (pCtl->fChainedTransfer)
{
/* Only count the actual transfers, not the PIO
* transfer of the ATAPI command bytes. */
if (s->fDMA)
STAM_REL_COUNTER_INC(&s->StatATAPIDMA);
else
STAM_REL_COUNTER_INC(&s->StatATAPIPIO);
}
}
else
{
if (s->fDMA)
STAM_REL_COUNTER_INC(&s->StatATADMA);
else
STAM_REL_COUNTER_INC(&s->StatATAPIO);
}
pCtl->fChainedTransfer = false;
if (s->iBeginTransfer != ATAFN_BT_NULL)
{
Log2(("%s: Ctl#%d: calling begin transfer function\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
g_apfnBeginTransFuncs[s->iBeginTransfer](s);
s->iBeginTransfer = ATAFN_BT_NULL;
if (s->uTxDir != PDMBLOCKTXDIR_FROM_DEVICE)
s->iIOBufferEnd = s->cbElementaryTransfer;
}
else
{
s->cbElementaryTransfer = s->cbTotalTransfer;
s->iIOBufferEnd = s->cbTotalTransfer;
}
s->iIOBufferCur = 0;
if (s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE)
{
if (s->iSourceSink != ATAFN_SS_NULL)
{
bool fRedo;
Log2(("%s: Ctl#%d: calling source/sink function\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
pCtl->fRedo = fRedo;
if (RT_UNLIKELY(fRedo))
{
/* Operation failed at the initial transfer, restart
* everything from scratch by resending the current
* request. Occurs very rarely, not worth optimizing. */
LogRel(("%s: Ctl#%d: redo entire operation\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
ataAsyncIOPutRequest(pCtl, pReq);
ataSuspendRedo(pCtl);
break;
}
}
else
ataCmdOK(s, 0);
s->iIOBufferEnd = s->cbElementaryTransfer;
}
/* Do not go into the transfer phase if RESET is asserted.
* The CritSect is released while waiting for the host OS
* to finish the I/O, thus RESET is possible here. Most
* important: do not change uAsyncIOState. */
if (pCtl->fReset)
break;
if (s->fDMA)
{
if (s->cbTotalTransfer)
{
ataSetStatus(s, ATA_STAT_DRQ);
pCtl->uAsyncIOState = ATA_AIO_DMA;
/* If BMDMA is already started, do the transfer now. */
if (pCtl->BmDma.u8Cmd & BM_CMD_START)
{
Log2(("%s: Ctl#%d: message to async I/O thread, continuing DMA transfer immediately\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
ataAsyncIOPutRequest(pCtl, &ataDMARequest);
}
}
else
{
Assert(s->uTxDir == PDMBLOCKTXDIR_NONE); /* Any transfer which has an initial transfer size of 0 must be marked as such. */
/* Finish DMA transfer. */
ataDMATransferStop(s);
ataSetIRQ(s);
pCtl->uAsyncIOState = ATA_AIO_NEW;
}
}
else
{
if (s->cbTotalTransfer)
{
ataPIOTransfer(pCtl);
Assert(!pCtl->fRedo);
if (s->fATAPITransfer || s->uTxDir != PDMBLOCKTXDIR_TO_DEVICE)
ataSetIRQ(s);
if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE || s->iSourceSink != ATAFN_SS_NULL)
{
/* Write operations and not yet finished transfers
* must be completed in the async I/O thread. */
pCtl->uAsyncIOState = ATA_AIO_PIO;
}
else
{
/* Finished read operation can be handled inline
* in the end of PIO transfer handling code. Linux
* depends on this, as it waits only briefly for
* devices to become ready after incoming data
* transfer. Cannot find anything in the ATA spec
* that backs this assumption, but as all kernels
* are affected (though most of the time it does
* not cause any harm) this must work. */
pCtl->uAsyncIOState = ATA_AIO_NEW;
}
}
else
{
Assert(s->uTxDir == PDMBLOCKTXDIR_NONE); /* Any transfer which has an initial transfer size of 0 must be marked as such. */
/* Finish PIO transfer. */
ataPIOTransfer(pCtl);
Assert(!pCtl->fRedo);
if (!s->fATAPITransfer)
ataSetIRQ(s);
pCtl->uAsyncIOState = ATA_AIO_NEW;
}
}
break;
case ATA_AIO_DMA:
{
BMDMAState *bm = &pCtl->BmDma;
s = &pCtl->aIfs[pCtl->iAIOIf]; /* Do not remove or there's an instant crash after loading the saved state */
ATAFNSS iOriginalSourceSink = (ATAFNSS)s->iSourceSink; /* Used by the hack below, but gets reset by then. */
if (s->uTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
AssertRelease(bm->u8Cmd & BM_CMD_WRITE);
else
AssertRelease(!(bm->u8Cmd & BM_CMD_WRITE));
if (RT_LIKELY(!pCtl->fRedo))
{
/* The specs say that the descriptor table must not cross a
* 4K boundary. */
pCtl->pFirstDMADesc = bm->pvAddr;
pCtl->pLastDMADesc = RT_ALIGN_32(bm->pvAddr + 1, _4K) - sizeof(BMDMADesc);
}
ataDMATransfer(pCtl);
if (RT_UNLIKELY(pCtl->fRedo))
{
LogRel(("PIIX3 ATA: Ctl#%d: redo DMA operation\n", ATACONTROLLER_IDX(pCtl)));
ataAsyncIOPutRequest(pCtl, &ataDMARequest);
ataSuspendRedo(pCtl);
break;
}
/* The infamous delay IRQ hack. */
if ( iOriginalSourceSink == ATAFN_SS_WRITE_SECTORS
&& s->cbTotalTransfer == 0
&& pCtl->DelayIRQMillies)
{
/* Delay IRQ for writing. Required to get the Win2K
* installation work reliably (otherwise it crashes,
* usually during component install). So far no better
* solution has been found. */
Log(("%s: delay IRQ hack\n", __FUNCTION__));
PDMCritSectLeave(&pCtl->lock);
RTThreadSleep(pCtl->DelayIRQMillies);
PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
}
ataUnsetStatus(s, ATA_STAT_DRQ);
Assert(!pCtl->fChainedTransfer);
Assert(s->iSourceSink == ATAFN_SS_NULL);
if (s->fATAPITransfer)
{
s->uATARegNSector = (s->uATARegNSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
Log2(("%s: Ctl#%d: interrupt reason %#04x\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl), s->uATARegNSector));
s->fATAPITransfer = false;
}
ataSetIRQ(s);
pCtl->uAsyncIOState = ATA_AIO_NEW;
break;
}
case ATA_AIO_PIO:
s = &pCtl->aIfs[pCtl->iAIOIf]; /* Do not remove or there's an instant crash after loading the saved state */
if (s->iSourceSink != ATAFN_SS_NULL)
{
bool fRedo;
Log2(("%s: Ctl#%d: calling source/sink function\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
fRedo = g_apfnSourceSinkFuncs[s->iSourceSink](s);
pCtl->fRedo = fRedo;
if (RT_UNLIKELY(fRedo))
{
LogRel(("PIIX3 ATA: Ctl#%d: redo PIO operation\n", ATACONTROLLER_IDX(pCtl)));
ataAsyncIOPutRequest(pCtl, &ataPIORequest);
ataSuspendRedo(pCtl);
break;
}
s->iIOBufferCur = 0;
s->iIOBufferEnd = s->cbElementaryTransfer;
}
else
{
/* Continue a previously started transfer. */
ataUnsetStatus(s, ATA_STAT_BUSY);
ataSetStatus(s, ATA_STAT_READY);
}
/* It is possible that the drives on this controller get RESET
* during the above call to the source/sink function. If that's
* the case, don't restart the transfer and don't finish it the
* usual way. RESET handling took care of all that already.
* Most important: do not change uAsyncIOState. */
if (pCtl->fReset)
break;
if (s->cbTotalTransfer)
{
ataPIOTransfer(pCtl);
ataSetIRQ(s);
if (s->uTxDir == PDMBLOCKTXDIR_TO_DEVICE || s->iSourceSink != ATAFN_SS_NULL)
{
/* Write operations and not yet finished transfers
* must be completed in the async I/O thread. */
pCtl->uAsyncIOState = ATA_AIO_PIO;
}
else
{
/* Finished read operation can be handled inline
* in the end of PIO transfer handling code. Linux
* depends on this, as it waits only briefly for
* devices to become ready after incoming data
* transfer. Cannot find anything in the ATA spec
* that backs this assumption, but as all kernels
* are affected (though most of the time it does
* not cause any harm) this must work. */
pCtl->uAsyncIOState = ATA_AIO_NEW;
}
}
else
{
/* Finish PIO transfer. */
ataPIOTransfer(pCtl);
if ( !pCtl->fChainedTransfer
&& !s->fATAPITransfer
&& s->uTxDir != PDMBLOCKTXDIR_FROM_DEVICE)
{
ataSetIRQ(s);
}
pCtl->uAsyncIOState = ATA_AIO_NEW;
}
break;
case ATA_AIO_RESET_ASSERTED:
pCtl->uAsyncIOState = ATA_AIO_RESET_CLEARED;
ataPIOTransferStop(&pCtl->aIfs[0]);
ataPIOTransferStop(&pCtl->aIfs[1]);
/* Do not change the DMA registers, they are not affected by the
* ATA controller reset logic. It should be sufficient to issue a
* new command, which is now possible as the state is cleared. */
break;
case ATA_AIO_RESET_CLEARED:
pCtl->uAsyncIOState = ATA_AIO_NEW;
pCtl->fReset = false;
LogRel(("PIIX3 ATA: Ctl#%d: finished processing RESET\n",
ATACONTROLLER_IDX(pCtl)));
for (uint32_t i = 0; i < RT_ELEMENTS(pCtl->aIfs); i++)
{
if (pCtl->aIfs[i].fATAPI)
ataSetStatusValue(&pCtl->aIfs[i], 0); /* NOTE: READY is _not_ set */
else
ataSetStatusValue(&pCtl->aIfs[i], ATA_STAT_READY | ATA_STAT_SEEK);
ataSetSignature(&pCtl->aIfs[i]);
}
break;
case ATA_AIO_ABORT:
/* Abort the current command only if it operates on the same interface. */
if (pCtl->iAIOIf == pReq->u.a.iIf)
{
s = &pCtl->aIfs[pCtl->iAIOIf];
pCtl->uAsyncIOState = ATA_AIO_NEW;
/* Do not change the DMA registers, they are not affected by the
* ATA controller reset logic. It should be sufficient to issue a
* new command, which is now possible as the state is cleared. */
if (pReq->u.a.fResetDrive)
{
ataResetDevice(s);
ataExecuteDeviceDiagnosticSS(s);
}
else
{
ataPIOTransferStop(s);
ataUnsetStatus(s, ATA_STAT_BUSY | ATA_STAT_DRQ | ATA_STAT_SEEK | ATA_STAT_ERR);
ataSetStatus(s, ATA_STAT_READY);
ataSetIRQ(s);
}
}
break;
default:
AssertMsgFailed(("Undefined async I/O state %d\n", pCtl->uAsyncIOState));
}
ataAsyncIORemoveCurrentRequest(pCtl, ReqType);
pReq = ataAsyncIOGetCurrentRequest(pCtl);
if (pCtl->uAsyncIOState == ATA_AIO_NEW && !pCtl->fChainedTransfer)
{
#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
STAM_PROFILE_ADV_STOP(&pCtl->StatAsyncTime, a);
#endif /* DEBUG || VBOX_WITH_STATISTICS */
u64TS = RTTimeNanoTS() - u64TS;
uWait = u64TS / 1000;
Log(("%s: Ctl#%d: LUN#%d finished I/O transaction in %d microseconds\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl), pCtl->aIfs[pCtl->iAIOIf].iLUN, (uint32_t)(uWait)));
/* Mark command as finished. */
pCtl->aIfs[pCtl->iAIOIf].u64CmdTS = 0;
/*
* Release logging of command execution times depends on the
* command type. ATAPI commands often take longer (due to CD/DVD
* spin up time etc.) so the threshold is different.
*/
if (pCtl->aIfs[pCtl->iAIOIf].uATARegCommand != ATA_PACKET)
{
if (uWait > 8 * 1000 * 1000)
{
/*
* Command took longer than 8 seconds. This is close
* enough or over the guest's command timeout, so place
* an entry in the release log to allow tracking such
* timing errors (which are often caused by the host).
*/
LogRel(("PIIX3 ATA: execution time for ATA command %#04x was %d seconds\n", pCtl->aIfs[pCtl->iAIOIf].uATARegCommand, uWait / (1000 * 1000)));
}
}
else
{
if (uWait > 20 * 1000 * 1000)
{
/*
* Command took longer than 20 seconds. This is close
* enough or over the guest's command timeout, so place
* an entry in the release log to allow tracking such
* timing errors (which are often caused by the host).
*/
LogRel(("PIIX3 ATA: execution time for ATAPI command %#04x was %d seconds\n", pCtl->aIfs[pCtl->iAIOIf].aATAPICmd[0], uWait / (1000 * 1000)));
}
}
#if defined(DEBUG) || defined(VBOX_WITH_STATISTICS)
if (uWait < pCtl->StatAsyncMinWait || !pCtl->StatAsyncMinWait)
pCtl->StatAsyncMinWait = uWait;
if (uWait > pCtl->StatAsyncMaxWait)
pCtl->StatAsyncMaxWait = uWait;
STAM_COUNTER_ADD(&pCtl->StatAsyncTimeUS, uWait);
STAM_COUNTER_INC(&pCtl->StatAsyncOps);
#endif /* DEBUG || VBOX_WITH_STATISTICS */
}
LogBird(("ata: %x: leaving critsect\n", pCtl->IOPortBase1));
PDMCritSectLeave(&pCtl->lock);
}
/* Cleanup the state. */
if (pCtl->AsyncIOSem)
{
RTSemEventDestroy(pCtl->AsyncIOSem);
pCtl->AsyncIOSem = NIL_RTSEMEVENT;
}
if (pCtl->SuspendIOSem)
{
RTSemEventDestroy(pCtl->SuspendIOSem);
pCtl->SuspendIOSem = NIL_RTSEMEVENT;
}
/* Do not destroy request mutex yet, still needed for proper shutdown. */
pCtl->fShutdown = false;
/* This must be last, as it also signals thread exit to EMT. */
pCtl->AsyncIOThread = NIL_RTTHREAD;
Log2(("%s: Ctl#%d: return %Rrc\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl), rc));
return rc;
}
#endif /* IN_RING3 */
static uint32_t ataBMDMACmdReadB(PATACONTROLLER pCtl, uint32_t addr)
{
uint32_t val = pCtl->BmDma.u8Cmd;
Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val));
return val;
}
static void ataBMDMACmdWriteB(PATACONTROLLER pCtl, uint32_t addr, uint32_t val)
{
Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val));
if (!(val & BM_CMD_START))
{
pCtl->BmDma.u8Status &= ~BM_STATUS_DMAING;
pCtl->BmDma.u8Cmd = val & (BM_CMD_START | BM_CMD_WRITE);
}
else
{
#ifdef IN_RING3
/* Check whether the guest OS wants to change DMA direction in
* mid-flight. Not allowed, according to the PIIX3 specs. */
Assert(!(pCtl->BmDma.u8Status & BM_STATUS_DMAING) || !((val ^ pCtl->BmDma.u8Cmd) & 0x04));
pCtl->BmDma.u8Status |= BM_STATUS_DMAING;
pCtl->BmDma.u8Cmd = val & (BM_CMD_START | BM_CMD_WRITE);
/* Do not continue DMA transfers while the RESET line is asserted. */
if (pCtl->fReset)
{
Log2(("%s: Ctl#%d: suppressed continuing DMA transfer as RESET is active\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
return;
}
/* Do not start DMA transfers if there's a PIO transfer going on. */
if (!pCtl->aIfs[pCtl->iSelectedIf].fDMA)
return;
if (pCtl->aIfs[pCtl->iAIOIf].uATARegStatus & ATA_STAT_DRQ)
{
Log2(("%s: Ctl#%d: message to async I/O thread, continuing DMA transfer\n", __FUNCTION__, ATACONTROLLER_IDX(pCtl)));
ataAsyncIOPutRequest(pCtl, &ataDMARequest);
}
#else /* !IN_RING3 */
AssertMsgFailed(("DMA START handling is too complicated for GC\n"));
#endif /* IN_RING3 */
}
}
static uint32_t ataBMDMAStatusReadB(PATACONTROLLER pCtl, uint32_t addr)
{
uint32_t val = pCtl->BmDma.u8Status;
Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val));
return val;
}
static void ataBMDMAStatusWriteB(PATACONTROLLER pCtl, uint32_t addr, uint32_t val)
{
Log2(("%s: addr=%#06x val=%#04x\n", __FUNCTION__, addr, val));
pCtl->BmDma.u8Status = (val & (BM_STATUS_D0DMA | BM_STATUS_D1DMA))
| (pCtl->BmDma.u8Status & BM_STATUS_DMAING)
| (pCtl->BmDma.u8Status & ~val & (BM_STATUS_ERROR | BM_STATUS_INT));
}
static uint32_t ataBMDMAAddrReadL(PATACONTROLLER pCtl, uint32_t addr)
{
uint32_t val = (uint32_t)pCtl->BmDma.pvAddr;
Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val));
return val;
}
static void ataBMDMAAddrWriteL(PATACONTROLLER pCtl, uint32_t addr, uint32_t val)
{
Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val));
pCtl->BmDma.pvAddr = val & ~3;
}
static void ataBMDMAAddrWriteLowWord(PATACONTROLLER pCtl, uint32_t addr, uint32_t val)
{
Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val));
pCtl->BmDma.pvAddr = (pCtl->BmDma.pvAddr & 0xFFFF0000) | RT_LOWORD(val & ~3);
}
static void ataBMDMAAddrWriteHighWord(PATACONTROLLER pCtl, uint32_t addr, uint32_t val)
{
Log2(("%s: addr=%#06x val=%#010x\n", __FUNCTION__, addr, val));
pCtl->BmDma.pvAddr = (RT_LOWORD(val) << 16) | RT_LOWORD(pCtl->BmDma.pvAddr);
}
#define VAL(port, size) ( ((port) & 7) | ((size) << 3) )
/**
* Port I/O Handler for bus master DMA IN operations.
* @see FNIOMIOPORTIN for details.
*/
PDMBOTHCBDECL(int) ataBMDMAIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
uint32_t i = (uint32_t)(uintptr_t)pvUser;
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PATACONTROLLER pCtl = &pThis->aCts[i];
int rc;
rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_HC_IOPORT_READ);
if (rc != VINF_SUCCESS)
return rc;
switch (VAL(Port, cb))
{
case VAL(0, 1): *pu32 = ataBMDMACmdReadB(pCtl, Port); break;
case VAL(0, 2): *pu32 = ataBMDMACmdReadB(pCtl, Port); break;
case VAL(2, 1): *pu32 = ataBMDMAStatusReadB(pCtl, Port); break;
case VAL(2, 2): *pu32 = ataBMDMAStatusReadB(pCtl, Port); break;
case VAL(4, 4): *pu32 = ataBMDMAAddrReadL(pCtl, Port); break;
case VAL(0, 4):
/* The SCO OpenServer tries to read 4 bytes starting from offset 0. */
*pu32 = ataBMDMACmdReadB(pCtl, Port) | (ataBMDMAStatusReadB(pCtl, Port) << 16);
break;
default:
AssertMsgFailed(("%s: Unsupported read from port %x size=%d\n", __FUNCTION__, Port, cb));
PDMCritSectLeave(&pCtl->lock);
return VERR_IOM_IOPORT_UNUSED;
}
PDMCritSectLeave(&pCtl->lock);
return rc;
}
/**
* Port I/O Handler for bus master DMA OUT operations.
* @see FNIOMIOPORTOUT for details.
*/
PDMBOTHCBDECL(int) ataBMDMAIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
uint32_t i = (uint32_t)(uintptr_t)pvUser;
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PATACONTROLLER pCtl = &pThis->aCts[i];
int rc;
rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_HC_IOPORT_WRITE);
if (rc != VINF_SUCCESS)
return rc;
switch (VAL(Port, cb))
{
case VAL(0, 1):
#ifndef IN_RING3
if (u32 & BM_CMD_START)
{
rc = VINF_IOM_HC_IOPORT_WRITE;
break;
}
#endif /* !IN_RING3 */
ataBMDMACmdWriteB(pCtl, Port, u32);
break;
case VAL(2, 1): ataBMDMAStatusWriteB(pCtl, Port, u32); break;
case VAL(4, 4): ataBMDMAAddrWriteL(pCtl, Port, u32); break;
case VAL(4, 2): ataBMDMAAddrWriteLowWord(pCtl, Port, u32); break;
case VAL(6, 2): ataBMDMAAddrWriteHighWord(pCtl, Port, u32); break;
default: AssertMsgFailed(("%s: Unsupported write to port %x size=%d val=%x\n", __FUNCTION__, Port, cb, u32)); break;
}
PDMCritSectLeave(&pCtl->lock);
return rc;
}
#undef VAL
#ifdef IN_RING3
/**
* Callback function for mapping an PCI I/O region.
*
* @return VBox status code.
* @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
* @param iRegion The region number.
* @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
* I/O port, else it's a physical address.
* This address is *NOT* relative to pci_mem_base like earlier!
* @param enmType One of the PCI_ADDRESS_SPACE_* values.
*/
static DECLCALLBACK(int) ataBMDMAIORangeMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
{
PCIATAState *pThis = PCIDEV_2_PCIATASTATE(pPciDev);
int rc = VINF_SUCCESS;
Assert(enmType == PCI_ADDRESS_SPACE_IO);
Assert(iRegion == 4);
AssertMsg(RT_ALIGN(GCPhysAddress, 8) == GCPhysAddress, ("Expected 8 byte alignment. GCPhysAddress=%#x\n", GCPhysAddress));
/* Register the port range. */
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
int rc2 = PDMDevHlpIOPortRegister(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress + i * 8, 8,
(RTHCPTR)i, ataBMDMAIOPortWrite, ataBMDMAIOPortRead, NULL, NULL, "ATA Bus Master DMA");
AssertRC(rc2);
if (rc2 < rc)
rc = rc2;
if (pThis->fGCEnabled)
{
rc2 = PDMDevHlpIOPortRegisterGC(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress + i * 8, 8,
(RTGCPTR)i, "ataBMDMAIOPortWrite", "ataBMDMAIOPortRead", NULL, NULL, "ATA Bus Master DMA");
AssertRC(rc2);
if (rc2 < rc)
rc = rc2;
}
if (pThis->fR0Enabled)
{
rc2 = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, (RTIOPORT)GCPhysAddress + i * 8, 8,
(RTR0PTR)i, "ataBMDMAIOPortWrite", "ataBMDMAIOPortRead", NULL, NULL, "ATA Bus Master DMA");
AssertRC(rc2);
if (rc2 < rc)
rc = rc2;
}
}
return rc;
}
/**
* Reset notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(void) ataReset(PPDMDEVINS pDevIns)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
pThis->aCts[i].iSelectedIf = 0;
pThis->aCts[i].iAIOIf = 0;
pThis->aCts[i].BmDma.u8Cmd = 0;
/* Report that both drives present on the bus are in DMA mode. This
* pretends that there is a BIOS that has set it up. Normal reset
* default is 0x00. */
pThis->aCts[i].BmDma.u8Status = (pThis->aCts[i].aIfs[0].pDrvBase != NULL ? BM_STATUS_D0DMA : 0)
| (pThis->aCts[i].aIfs[1].pDrvBase != NULL ? BM_STATUS_D1DMA : 0);
pThis->aCts[i].BmDma.pvAddr = 0;
pThis->aCts[i].fReset = true;
pThis->aCts[i].fRedo = false;
pThis->aCts[i].fRedoIdle = false;
ataAsyncIOClearRequests(&pThis->aCts[i]);
Log2(("%s: Ctl#%d: message to async I/O thread, reset controller\n", __FUNCTION__, i));
ataAsyncIOPutRequest(&pThis->aCts[i], &ataResetARequest);
ataAsyncIOPutRequest(&pThis->aCts[i], &ataResetCRequest);
if (!ataWaitForAsyncIOIsIdle(&pThis->aCts[i], 30000))
AssertReleaseMsgFailed(("Async I/O thread busy after reset\n"));
for (uint32_t j = 0; j < RT_ELEMENTS(pThis->aCts[i].aIfs); j++)
ataResetDevice(&pThis->aCts[i].aIfs[j]);
}
}
/* -=-=-=-=-=- PCIATAState::IBase -=-=-=-=-=- */
/**
* Queries an interface to the driver.
*
* @returns Pointer to interface.
* @returns NULL if the interface was not supported by the device.
* @param pInterface Pointer to ATADevState::IBase.
* @param enmInterface The requested interface identification.
*/
static DECLCALLBACK(void *) ataStatus_QueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
{
PCIATAState *pThis = PDMIBASE_2_PCIATASTATE(pInterface);
switch (enmInterface)
{
case PDMINTERFACE_BASE:
return &pThis->IBase;
case PDMINTERFACE_LED_PORTS:
return &pThis->ILeds;
default:
return NULL;
}
}
/* -=-=-=-=-=- PCIATAState::ILeds -=-=-=-=-=- */
/**
* Gets the pointer to the status LED of a unit.
*
* @returns VBox status code.
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param iLUN The unit which status LED we desire.
* @param ppLed Where to store the LED pointer.
*/
static DECLCALLBACK(int) ataStatus_QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
PCIATAState *pThis = PDMILEDPORTS_2_PCIATASTATE(pInterface);
if (iLUN >= 0 && iLUN <= 4)
{
switch (iLUN)
{
case 0: *ppLed = &pThis->aCts[0].aIfs[0].Led; break;
case 1: *ppLed = &pThis->aCts[0].aIfs[1].Led; break;
case 2: *ppLed = &pThis->aCts[1].aIfs[0].Led; break;
case 3: *ppLed = &pThis->aCts[1].aIfs[1].Led; break;
}
Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
return VINF_SUCCESS;
}
return VERR_PDM_LUN_NOT_FOUND;
}
/* -=-=-=-=-=- ATADevState::IBase -=-=-=-=-=- */
/**
* Queries an interface to the driver.
*
* @returns Pointer to interface.
* @returns NULL if the interface was not supported by the device.
* @param pInterface Pointer to ATADevState::IBase.
* @param enmInterface The requested interface identification.
*/
static DECLCALLBACK(void *) ataQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
{
ATADevState *pIf = PDMIBASE_2_ATASTATE(pInterface);
switch (enmInterface)
{
case PDMINTERFACE_BASE:
return &pIf->IBase;
case PDMINTERFACE_BLOCK_PORT:
return &pIf->IPort;
case PDMINTERFACE_MOUNT_NOTIFY:
return &pIf->IMountNotify;
default:
return NULL;
}
}
#endif /* IN_RING3 */
/* -=-=-=-=-=- Wrappers -=-=-=-=-=- */
/**
* Port I/O Handler for primary port range OUT operations.
* @see FNIOMIOPORTOUT for details.
*/
PDMBOTHCBDECL(int) ataIOPortWrite1(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
uint32_t i = (uint32_t)(uintptr_t)pvUser;
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PATACONTROLLER pCtl = &pThis->aCts[i];
int rc = VINF_SUCCESS;
Assert(i < 2);
rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_HC_IOPORT_WRITE);
if (rc != VINF_SUCCESS)
return rc;
if (cb == 1)
rc = ataIOPortWriteU8(pCtl, Port, u32);
else if (Port == pCtl->IOPortBase1)
{
Assert(cb == 2 || cb == 4);
rc = ataDataWrite(pCtl, Port, cb, (const uint8_t *)&u32);
}
else
AssertMsgFailed(("ataIOPortWrite1: unsupported write to port %x val=%x size=%d\n", Port, u32, cb));
LogBird(("ata: leaving critsect\n"));
PDMCritSectLeave(&pCtl->lock);
LogBird(("ata: left critsect\n"));
return rc;
}
/**
* Port I/O Handler for primary port range IN operations.
* @see FNIOMIOPORTIN for details.
*/
PDMBOTHCBDECL(int) ataIOPortRead1(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
uint32_t i = (uint32_t)(uintptr_t)pvUser;
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PATACONTROLLER pCtl = &pThis->aCts[i];
int rc = VINF_SUCCESS;
Assert(i < 2);
rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_HC_IOPORT_READ);
if (rc != VINF_SUCCESS)
return rc;
if (cb == 1)
{
rc = ataIOPortReadU8(pCtl, Port, pu32);
}
else if (Port == pCtl->IOPortBase1)
{
Assert(cb == 2 || cb == 4);
rc = ataDataRead(pCtl, Port, cb, (uint8_t *)pu32);
if (cb == 2)
*pu32 &= 0xffff;
}
else
{
AssertMsgFailed(("ataIOPortRead1: unsupported read from port %x size=%d\n", Port, cb));
rc = VERR_IOM_IOPORT_UNUSED;
}
PDMCritSectLeave(&pCtl->lock);
return rc;
}
#ifndef IN_RING0
/**
* Port I/O Handler for primary port range IN string operations.
* @see FNIOMIOPORTINSTRING for details.
*/
PDMBOTHCBDECL(int) ataIOPortReadStr1(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
{
uint32_t i = (uint32_t)(uintptr_t)pvUser;
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PATACONTROLLER pCtl = &pThis->aCts[i];
int rc = VINF_SUCCESS;
Assert(i < 2);
rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_HC_IOPORT_READ);
if (rc != VINF_SUCCESS)
return rc;
if (Port == pCtl->IOPortBase1)
{
uint32_t cTransAvailable, cTransfer = *pcTransfer, cbTransfer;
RTGCPTR GCDst = *pGCPtrDst;
ATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
Assert(cb == 2 || cb == 4);
cTransAvailable = (s->iIOBufferPIODataEnd - s->iIOBufferPIODataStart) / cb;
#ifndef IN_RING3
/* The last transfer unit cannot be handled in GC, as it involves thread communication. */
cTransAvailable--;
#endif /* !IN_RING3 */
/* Do not handle the dummy transfer stuff here, leave it to the single-word transfers.
* They are not performance-critical and generally shouldn't occur at all. */
if (cTransAvailable > cTransfer)
cTransAvailable = cTransfer;
cbTransfer = cTransAvailable * cb;
#ifdef IN_GC
for (uint32_t i = 0; i < cbTransfer; i += cb)
MMGCRamWriteNoTrapHandler((char *)GCDst + i, s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart + i, cb);
#else /* !IN_GC */
rc = PGMPhysWriteGCPtrDirty(PDMDevHlpGetVM(pDevIns), GCDst, s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart, cbTransfer);
Assert(rc == VINF_SUCCESS);
#endif /* IN_GC */
if (cbTransfer)
Log3(("%s: addr=%#x val=%.*Vhxs\n", __FUNCTION__, Port, cbTransfer, s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart));
s->iIOBufferPIODataStart += cbTransfer;
*pGCPtrDst = (RTGCPTR)((RTGCUINTPTR)GCDst + cbTransfer);
*pcTransfer = cTransfer - cTransAvailable;
#ifdef IN_RING3
if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd)
ataPIOTransferFinish(pCtl, s);
#endif /* IN_RING3 */
}
PDMCritSectLeave(&pCtl->lock);
return rc;
}
/**
* Port I/O Handler for primary port range OUT string operations.
* @see FNIOMIOPORTOUTSTRING for details.
*/
PDMBOTHCBDECL(int) ataIOPortWriteStr1(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
{
uint32_t i = (uint32_t)(uintptr_t)pvUser;
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PATACONTROLLER pCtl = &pThis->aCts[i];
int rc;
Assert(i < 2);
rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_HC_IOPORT_WRITE);
if (rc != VINF_SUCCESS)
return rc;
if (Port == pCtl->IOPortBase1)
{
uint32_t cTransAvailable, cTransfer = *pcTransfer, cbTransfer;
RTGCPTR GCSrc = *pGCPtrSrc;
ATADevState *s = &pCtl->aIfs[pCtl->iSelectedIf];
Assert(cb == 2 || cb == 4);
cTransAvailable = (s->iIOBufferPIODataEnd - s->iIOBufferPIODataStart) / cb;
#ifndef IN_RING3
/* The last transfer unit cannot be handled in GC, as it involves thread communication. */
cTransAvailable--;
#endif /* !IN_RING3 */
/* Do not handle the dummy transfer stuff here, leave it to the single-word transfers.
* They are not performance-critical and generally shouldn't occur at all. */
if (cTransAvailable > cTransfer)
cTransAvailable = cTransfer;
cbTransfer = cTransAvailable * cb;
#ifdef IN_GC
for (uint32_t i = 0; i < cbTransfer; i += cb)
MMGCRamReadNoTrapHandler(s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart + i, (char *)GCSrc + i, cb);
#else /* !IN_GC */
rc = PGMPhysReadGCPtr(PDMDevHlpGetVM(pDevIns), s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart, GCSrc, cbTransfer);
Assert(rc == VINF_SUCCESS);
#endif /* IN_GC */
if (cbTransfer)
Log3(("%s: addr=%#x val=%.*Vhxs\n", __FUNCTION__, Port, cbTransfer, s->CTX_SUFF(pbIOBuffer) + s->iIOBufferPIODataStart));
s->iIOBufferPIODataStart += cbTransfer;
*pGCPtrSrc = (RTGCPTR)((RTGCUINTPTR)GCSrc + cbTransfer);
*pcTransfer = cTransfer - cTransAvailable;
#ifdef IN_RING3
if (s->iIOBufferPIODataStart >= s->iIOBufferPIODataEnd)
ataPIOTransferFinish(pCtl, s);
#endif /* IN_RING3 */
}
PDMCritSectLeave(&pCtl->lock);
return rc;
}
#endif /* !IN_RING0 */
/**
* Port I/O Handler for secondary port range OUT operations.
* @see FNIOMIOPORTOUT for details.
*/
PDMBOTHCBDECL(int) ataIOPortWrite2(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
uint32_t i = (uint32_t)(uintptr_t)pvUser;
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PATACONTROLLER pCtl = &pThis->aCts[i];
int rc;
Assert(i < 2);
if (cb != 1)
return VINF_SUCCESS;
rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_HC_IOPORT_WRITE);
if (rc != VINF_SUCCESS)
return rc;
rc = ataControlWrite(pCtl, Port, u32);
PDMCritSectLeave(&pCtl->lock);
return rc;
}
/**
* Port I/O Handler for secondary port range IN operations.
* @see FNIOMIOPORTIN for details.
*/
PDMBOTHCBDECL(int) ataIOPortRead2(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
uint32_t i = (uint32_t)(uintptr_t)pvUser;
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PATACONTROLLER pCtl = &pThis->aCts[i];
int rc;
Assert(i < 2);
if (cb != 1)
return VERR_IOM_IOPORT_UNUSED;
rc = PDMCritSectEnter(&pCtl->lock, VINF_IOM_HC_IOPORT_READ);
if (rc != VINF_SUCCESS)
return rc;
*pu32 = ataStatusRead(pCtl, Port);
PDMCritSectLeave(&pCtl->lock);
return VINF_SUCCESS;
}
#ifdef IN_RING3
/**
* Waits for all async I/O threads to complete whatever they
* are doing at the moment.
*
* @returns true on success.
* @returns false when one or more threads is still processing.
* @param pThis Pointer to the instance data.
* @param cMillies How long to wait (total).
*/
static bool ataWaitForAllAsyncIOIsIdle(PPDMDEVINS pDevIns, unsigned cMillies)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
bool fVMLocked;
uint64_t u64Start;
PATACONTROLLER pCtl;
bool fAllIdle = false;
/* The only way to deal cleanly with the VM lock is to check whether
* it is owned now (it always is owned by EMT, which is the current
* thread). Since this function is called several times during VM
* shutdown, and the VM lock is only held for the first call (which
* can be either from ataPowerOff or ataSuspend), there is no other
* reasonable solution. */
fVMLocked = VMMR3LockIsOwner(PDMDevHlpGetVM(pDevIns));
if (fVMLocked)
pDevIns->pDevHlp->pfnUnlockVM(pDevIns);
/*
* Wait for any pending async operation to finish
*/
u64Start = RTTimeMilliTS();
for (;;)
{
/* Check all async I/O threads. */
fAllIdle = true;
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
pCtl = &pThis->aCts[i];
fAllIdle &= ataAsyncIOIsIdle(pCtl, false);
if (!fAllIdle)
break;
}
if ( fAllIdle
|| RTTimeMilliTS() - u64Start >= cMillies)
break;
/* Sleep for a bit. */
RTThreadSleep(100);
}
if (fVMLocked)
pDevIns->pDevHlp->pfnLockVM(pDevIns);
if (!fAllIdle)
LogRel(("PIIX3 ATA: Ctl#%d is still executing, DevSel=%d AIOIf=%d CmdIf0=%#04x CmdIf1=%#04x\n",
ATACONTROLLER_IDX(pCtl), pCtl->iSelectedIf, pCtl->iAIOIf,
pCtl->aIfs[0].uATARegCommand, pCtl->aIfs[1].uATARegCommand));
return fAllIdle;
}
DECLINLINE(void) ataRelocBuffer(PPDMDEVINS pDevIns, ATADevState *s)
{
if (s->pbIOBufferR3)
s->pbIOBufferRC = MMHyperR3ToRC(PDMDevHlpGetVM(pDevIns), s->pbIOBufferR3);
}
/**
* @copydoc FNPDMDEVRELOCATE
*/
static DECLCALLBACK(void) ataRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
pThis->aCts[i].pDevInsRC += offDelta;
pThis->aCts[i].aIfs[0].pDevInsRC += offDelta;
pThis->aCts[i].aIfs[0].pControllerRC += offDelta;
ataRelocBuffer(pDevIns, &pThis->aCts[i].aIfs[0]);
pThis->aCts[i].aIfs[1].pDevInsRC += offDelta;
pThis->aCts[i].aIfs[1].pControllerRC += offDelta;
ataRelocBuffer(pDevIns, &pThis->aCts[i].aIfs[1]);
}
}
/**
* Destroy a driver instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(int) ataDestruct(PPDMDEVINS pDevIns)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
int rc;
Log(("%s:\n", __FUNCTION__));
/*
* Terminate all async helper threads
*/
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
if (pThis->aCts[i].AsyncIOThread != NIL_RTTHREAD)
{
ASMAtomicXchgU32(&pThis->aCts[i].fShutdown, true);
rc = RTSemEventSignal(pThis->aCts[i].AsyncIOSem);
AssertRC(rc);
}
}
/*
* Wait for them to complete whatever they are doing and then
* for them to terminate.
*/
if (ataWaitForAllAsyncIOIsIdle(pDevIns, 20000))
{
for (unsigned i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
rc = RTThreadWait(pThis->aCts[i].AsyncIOThread, 30000 /* 30 s*/, NULL);
AssertMsg(RT_SUCCESS(rc) || rc == VERR_INVALID_HANDLE, ("rc=%Rrc i=%d\n", rc, i));
}
}
else
AssertMsgFailed(("Async I/O is still busy!\n"));
/*
* Now the request mutexes are no longer needed. Free resources.
*/
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
if (pThis->aCts[i].AsyncIORequestMutex != NIL_RTSEMEVENT)
{
RTSemMutexDestroy(pThis->aCts[i].AsyncIORequestMutex);
pThis->aCts[i].AsyncIORequestMutex = NIL_RTSEMEVENT;
}
}
return VINF_SUCCESS;
}
/**
* Detach notification.
*
* The DVD drive has been unplugged.
*
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being detached.
*/
static DECLCALLBACK(void) ataDetach(PPDMDEVINS pDevIns, unsigned iLUN)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PATACONTROLLER pCtl;
ATADevState *pIf;
unsigned iController;
unsigned iInterface;
/*
* Locate the controller and stuff.
*/
iController = iLUN / RT_ELEMENTS(pThis->aCts[0].aIfs);
AssertReleaseMsg(iController < RT_ELEMENTS(pThis->aCts), ("iController=%d iLUN=%d\n", iController, iLUN));
pCtl = &pThis->aCts[iController];
iInterface = iLUN % RT_ELEMENTS(pThis->aCts[0].aIfs);
pIf = &pCtl->aIfs[iInterface];
/*
* Zero some important members.
*/
pIf->pDrvBase = NULL;
pIf->pDrvBlock = NULL;
pIf->pDrvBlockBios = NULL;
pIf->pDrvMount = NULL;
}
/**
* Configure a LUN.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pIf The ATA unit state.
*/
static int ataConfigLun(PPDMDEVINS pDevIns, ATADevState *pIf)
{
int rc;
PDMBLOCKTYPE enmType;
/*
* Query Block, Bios and Mount interfaces.
*/
pIf->pDrvBlock = (PDMIBLOCK *)pIf->pDrvBase->pfnQueryInterface(pIf->pDrvBase, PDMINTERFACE_BLOCK);
if (!pIf->pDrvBlock)
{
AssertMsgFailed(("Configuration error: LUN#%d hasn't a block interface!\n", pIf->iLUN));
return VERR_PDM_MISSING_INTERFACE;
}
/** @todo implement the BIOS invisible code path. */
pIf->pDrvBlockBios = (PDMIBLOCKBIOS *)pIf->pDrvBase->pfnQueryInterface(pIf->pDrvBase, PDMINTERFACE_BLOCK_BIOS);
if (!pIf->pDrvBlockBios)
{
AssertMsgFailed(("Configuration error: LUN#%d hasn't a block BIOS interface!\n", pIf->iLUN));
return VERR_PDM_MISSING_INTERFACE;
}
pIf->pDrvMount = (PDMIMOUNT *)pIf->pDrvBase->pfnQueryInterface(pIf->pDrvBase, PDMINTERFACE_MOUNT);
/*
* Validate type.
*/
enmType = pIf->pDrvBlock->pfnGetType(pIf->pDrvBlock);
if ( enmType != PDMBLOCKTYPE_CDROM
&& enmType != PDMBLOCKTYPE_DVD
&& enmType != PDMBLOCKTYPE_HARD_DISK)
{
AssertMsgFailed(("Configuration error: LUN#%d isn't a disk or cd/dvd-rom. enmType=%d\n", pIf->iLUN, enmType));
return VERR_PDM_UNSUPPORTED_BLOCK_TYPE;
}
if ( ( enmType == PDMBLOCKTYPE_DVD
|| enmType == PDMBLOCKTYPE_CDROM)
&& !pIf->pDrvMount)
{
AssertMsgFailed(("Internal error: cdrom without a mountable interface, WTF???!\n"));
return VERR_INTERNAL_ERROR;
}
pIf->fATAPI = enmType == PDMBLOCKTYPE_DVD || enmType == PDMBLOCKTYPE_CDROM;
pIf->fATAPIPassthrough = pIf->fATAPI ? (pIf->pDrvBlock->pfnSendCmd != NULL) : false;
/*
* Allocate I/O buffer.
*/
PVM pVM = PDMDevHlpGetVM(pDevIns);
if (pIf->cbIOBuffer)
{
/* Buffer is (probably) already allocated. Validate the fields,
* because memory corruption can also overwrite pIf->cbIOBuffer. */
if (pIf->fATAPI)
AssertRelease(pIf->cbIOBuffer == _128K);
else
AssertRelease(pIf->cbIOBuffer == ATA_MAX_MULT_SECTORS * 512);
Assert(pIf->pbIOBufferR3);
Assert(pIf->pbIOBufferR0 == MMHyperR3ToR0(pVM, pIf->pbIOBufferR3));
Assert(pIf->pbIOBufferRC == MMHyperR3ToRC(pVM, pIf->pbIOBufferR3));
}
else
{
if (pIf->fATAPI)
pIf->cbIOBuffer = _128K;
else
pIf->cbIOBuffer = ATA_MAX_MULT_SECTORS * 512;
Assert(!pIf->pbIOBufferR3);
rc = MMHyperAlloc(pVM, pIf->cbIOBuffer, 1, MM_TAG_PDM_DEVICE_USER, (void **)&pIf->pbIOBufferR3); /** @todo rainy day: change to MMR3HyperAllocOnceNoRel */
if (RT_FAILURE(rc))
return VERR_NO_MEMORY;
pIf->pbIOBufferR0 = MMHyperR3ToR0(pVM, pIf->pbIOBufferR3);
pIf->pbIOBufferRC = MMHyperR3ToRC(pVM, pIf->pbIOBufferR3);
}
/*
* Init geometry (only for non-CD/DVD media).
*/
if (pIf->fATAPI)
{
pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 2048;
pIf->PCHSGeometry.cCylinders = 0; /* dummy */
pIf->PCHSGeometry.cHeads = 0; /* dummy */
pIf->PCHSGeometry.cSectors = 0; /* dummy */
LogRel(("PIIX3 ATA: LUN#%d: CD/DVD, total number of sectors %Ld, passthrough %s\n", pIf->iLUN, pIf->cTotalSectors, (pIf->fATAPIPassthrough ? "enabled" : "disabled")));
}
else
{
pIf->cTotalSectors = pIf->pDrvBlock->pfnGetSize(pIf->pDrvBlock) / 512;
rc = pIf->pDrvBlockBios->pfnGetPCHSGeometry(pIf->pDrvBlockBios,
&pIf->PCHSGeometry);
if (rc == VERR_PDM_MEDIA_NOT_MOUNTED)
{
pIf->PCHSGeometry.cCylinders = 0;
pIf->PCHSGeometry.cHeads = 16; /*??*/
pIf->PCHSGeometry.cSectors = 63; /*??*/
}
else if (rc == VERR_PDM_GEOMETRY_NOT_SET)
{
pIf->PCHSGeometry.cCylinders = 0; /* autodetect marker */
rc = VINF_SUCCESS;
}
AssertRC(rc);
if ( pIf->PCHSGeometry.cCylinders == 0
|| pIf->PCHSGeometry.cHeads == 0
|| pIf->PCHSGeometry.cSectors == 0
)
{
uint64_t cCylinders = pIf->cTotalSectors / (16 * 63);
pIf->PCHSGeometry.cCylinders = RT_MAX(RT_MIN(cCylinders, 16383), 1);
pIf->PCHSGeometry.cHeads = 16;
pIf->PCHSGeometry.cSectors = 63;
/* Set the disk geometry information. */
rc = pIf->pDrvBlockBios->pfnSetPCHSGeometry(pIf->pDrvBlockBios,
&pIf->PCHSGeometry);
}
LogRel(("PIIX3 ATA: LUN#%d: disk, PCHS=%u/%u/%u, total number of sectors %Ld\n", pIf->iLUN, pIf->PCHSGeometry.cCylinders, pIf->PCHSGeometry.cHeads, pIf->PCHSGeometry.cSectors, pIf->cTotalSectors));
}
return VINF_SUCCESS;
}
/**
* Attach command.
*
* This is called when we change block driver for the DVD drive.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being detached.
*/
static DECLCALLBACK(int) ataAttach(PPDMDEVINS pDevIns, unsigned iLUN)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PATACONTROLLER pCtl;
ATADevState *pIf;
int rc;
unsigned iController;
unsigned iInterface;
/*
* Locate the controller and stuff.
*/
iController = iLUN / RT_ELEMENTS(pThis->aCts[0].aIfs);
AssertReleaseMsg(iController < RT_ELEMENTS(pThis->aCts), ("iController=%d iLUN=%d\n", iController, iLUN));
pCtl = &pThis->aCts[iController];
iInterface = iLUN % RT_ELEMENTS(pThis->aCts[0].aIfs);
pIf = &pCtl->aIfs[iInterface];
/* the usual paranoia */
AssertRelease(!pIf->pDrvBase);
AssertRelease(!pIf->pDrvBlock);
Assert(ATADEVSTATE_2_CONTROLLER(pIf) == pCtl);
Assert(pIf->iLUN == iLUN);
/*
* Try attach the block device and get the interfaces,
* required as well as optional.
*/
rc = PDMDevHlpDriverAttach(pDevIns, pIf->iLUN, &pIf->IBase, &pIf->pDrvBase, NULL);
if (RT_SUCCESS(rc))
rc = ataConfigLun(pDevIns, pIf);
else
AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pIf->iLUN, rc));
if (RT_FAILURE(rc))
{
pIf->pDrvBase = NULL;
pIf->pDrvBlock = NULL;
}
return rc;
}
/**
* Suspend notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(void) ataSuspend(PPDMDEVINS pDevIns)
{
Log(("%s:\n", __FUNCTION__));
if (!ataWaitForAllAsyncIOIsIdle(pDevIns, 20000))
AssertMsgFailed(("Async I/O didn't stop in 20 seconds!\n"));
return;
}
/**
* Resume notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(void) ataResume(PPDMDEVINS pDevIns)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
int rc;
Log(("%s:\n", __FUNCTION__));
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
if (pThis->aCts[i].fRedo && pThis->aCts[i].fRedoIdle)
{
rc = RTSemEventSignal(pThis->aCts[i].SuspendIOSem);
AssertRC(rc);
}
}
return;
}
/**
* Power Off notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(void) ataPowerOff(PPDMDEVINS pDevIns)
{
Log(("%s:\n", __FUNCTION__));
if (!ataWaitForAllAsyncIOIsIdle(pDevIns, 20000))
AssertMsgFailed(("Async I/O didn't stop in 20 seconds!\n"));
return;
}
/**
* Prepare state save and load operation.
*
* @returns VBox status code.
* @param pDevIns Device instance of the device which registered the data unit.
* @param pSSM SSM operation handle.
*/
static DECLCALLBACK(int) ataSaveLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
/* sanity - the suspend notification will wait on the async stuff. */
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
Assert(ataAsyncIOIsIdle(&pThis->aCts[i], false));
if (!ataAsyncIOIsIdle(&pThis->aCts[i], false))
return VERR_SSM_IDE_ASYNC_TIMEOUT;
}
return VINF_SUCCESS;
}
/**
* Saves a state of the ATA device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to save the state to.
*/
static DECLCALLBACK(int) ataSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
SSMR3PutU8(pSSMHandle, pThis->aCts[i].iSelectedIf);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].iAIOIf);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].uAsyncIOState);
SSMR3PutBool(pSSMHandle, pThis->aCts[i].fChainedTransfer);
SSMR3PutBool(pSSMHandle, pThis->aCts[i].fReset);
SSMR3PutBool(pSSMHandle, pThis->aCts[i].fRedo);
SSMR3PutBool(pSSMHandle, pThis->aCts[i].fRedoIdle);
SSMR3PutBool(pSSMHandle, pThis->aCts[i].fRedoDMALastDesc);
SSMR3PutMem(pSSMHandle, &pThis->aCts[i].BmDma, sizeof(pThis->aCts[i].BmDma));
SSMR3PutGCPhys32(pSSMHandle, pThis->aCts[i].pFirstDMADesc);
SSMR3PutGCPhys32(pSSMHandle, pThis->aCts[i].pLastDMADesc);
SSMR3PutGCPhys32(pSSMHandle, pThis->aCts[i].pRedoDMABuffer);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].cbRedoDMABuffer);
for (uint32_t j = 0; j < RT_ELEMENTS(pThis->aCts[i].aIfs); j++)
{
SSMR3PutBool(pSSMHandle, pThis->aCts[i].aIfs[j].fLBA48);
SSMR3PutBool(pSSMHandle, pThis->aCts[i].aIfs[j].fATAPI);
SSMR3PutBool(pSSMHandle, pThis->aCts[i].aIfs[j].fIrqPending);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].cMultSectors);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].PCHSGeometry.cCylinders);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].PCHSGeometry.cHeads);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].PCHSGeometry.cSectors);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].cSectorsPerIRQ);
SSMR3PutU64(pSSMHandle, pThis->aCts[i].aIfs[j].cTotalSectors);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegFeature);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegFeatureHOB);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegError);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegNSector);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegNSectorHOB);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegSector);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegSectorHOB);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegLCyl);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegLCylHOB);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegHCyl);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegHCylHOB);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegSelect);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegStatus);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegCommand);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATARegDevCtl);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uATATransferMode);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].uTxDir);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].iBeginTransfer);
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].iSourceSink);
SSMR3PutBool(pSSMHandle, pThis->aCts[i].aIfs[j].fDMA);
SSMR3PutBool(pSSMHandle, pThis->aCts[i].aIfs[j].fATAPITransfer);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].cbTotalTransfer);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].cbElementaryTransfer);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].iIOBufferCur);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].iIOBufferEnd);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].iIOBufferPIODataStart);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].iIOBufferPIODataEnd);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].iATAPILBA);
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].cbATAPISector);
SSMR3PutMem(pSSMHandle, &pThis->aCts[i].aIfs[j].aATAPICmd, sizeof(pThis->aCts[i].aIfs[j].aATAPICmd));
SSMR3PutMem(pSSMHandle, &pThis->aCts[i].aIfs[j].abATAPISense, sizeof(pThis->aCts[i].aIfs[j].abATAPISense));
SSMR3PutU8(pSSMHandle, pThis->aCts[i].aIfs[j].cNotifiedMediaChange);
SSMR3PutMem(pSSMHandle, &pThis->aCts[i].aIfs[j].Led, sizeof(pThis->aCts[i].aIfs[j].Led));
SSMR3PutU32(pSSMHandle, pThis->aCts[i].aIfs[j].cbIOBuffer);
if (pThis->aCts[i].aIfs[j].cbIOBuffer)
SSMR3PutMem(pSSMHandle, pThis->aCts[i].aIfs[j].CTX_SUFF(pbIOBuffer), pThis->aCts[i].aIfs[j].cbIOBuffer);
else
Assert(pThis->aCts[i].aIfs[j].CTX_SUFF(pbIOBuffer) == NULL);
}
}
SSMR3PutBool(pSSMHandle, pThis->fPIIX4);
return SSMR3PutU32(pSSMHandle, ~0); /* sanity/terminator */
}
/**
* Loads a saved ATA device state.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to the saved state.
* @param u32Version The data unit version number.
*/
static DECLCALLBACK(int) ataLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
int rc;
uint32_t u32;
if ( u32Version != ATA_SAVED_STATE_VERSION
&& u32Version != ATA_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE)
{
AssertMsgFailed(("u32Version=%d\n", u32Version));
return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
}
/*
* Restore valid parts of the PCIATAState structure
*/
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
/* integrity check */
if (!ataAsyncIOIsIdle(&pThis->aCts[i], false))
{
AssertMsgFailed(("Async I/O for controller %d is active\n", i));
rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
return rc;
}
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].iSelectedIf);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].iAIOIf);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].uAsyncIOState);
SSMR3GetBool(pSSMHandle, &pThis->aCts[i].fChainedTransfer);
SSMR3GetBool(pSSMHandle, (bool *)&pThis->aCts[i].fReset);
SSMR3GetBool(pSSMHandle, (bool *)&pThis->aCts[i].fRedo);
SSMR3GetBool(pSSMHandle, (bool *)&pThis->aCts[i].fRedoIdle);
SSMR3GetBool(pSSMHandle, (bool *)&pThis->aCts[i].fRedoDMALastDesc);
SSMR3GetMem(pSSMHandle, &pThis->aCts[i].BmDma, sizeof(pThis->aCts[i].BmDma));
SSMR3GetGCPhys32(pSSMHandle, &pThis->aCts[i].pFirstDMADesc);
SSMR3GetGCPhys32(pSSMHandle, &pThis->aCts[i].pLastDMADesc);
SSMR3GetGCPhys32(pSSMHandle, &pThis->aCts[i].pRedoDMABuffer);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].cbRedoDMABuffer);
for (uint32_t j = 0; j < RT_ELEMENTS(pThis->aCts[i].aIfs); j++)
{
SSMR3GetBool(pSSMHandle, &pThis->aCts[i].aIfs[j].fLBA48);
SSMR3GetBool(pSSMHandle, &pThis->aCts[i].aIfs[j].fATAPI);
SSMR3GetBool(pSSMHandle, &pThis->aCts[i].aIfs[j].fIrqPending);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].cMultSectors);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].PCHSGeometry.cCylinders);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].PCHSGeometry.cHeads);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].PCHSGeometry.cSectors);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].cSectorsPerIRQ);
SSMR3GetU64(pSSMHandle, &pThis->aCts[i].aIfs[j].cTotalSectors);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegFeature);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegFeatureHOB);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegError);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegNSector);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegNSectorHOB);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegSector);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegSectorHOB);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegLCyl);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegLCylHOB);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegHCyl);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegHCylHOB);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegSelect);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegStatus);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegCommand);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATARegDevCtl);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uATATransferMode);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].uTxDir);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].iBeginTransfer);
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].iSourceSink);
SSMR3GetBool(pSSMHandle, &pThis->aCts[i].aIfs[j].fDMA);
SSMR3GetBool(pSSMHandle, &pThis->aCts[i].aIfs[j].fATAPITransfer);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].cbTotalTransfer);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].cbElementaryTransfer);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].iIOBufferCur);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].iIOBufferEnd);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].iIOBufferPIODataStart);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].iIOBufferPIODataEnd);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].iATAPILBA);
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].cbATAPISector);
SSMR3GetMem(pSSMHandle, &pThis->aCts[i].aIfs[j].aATAPICmd, sizeof(pThis->aCts[i].aIfs[j].aATAPICmd));
if (u32Version != ATA_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE)
{
SSMR3GetMem(pSSMHandle, pThis->aCts[i].aIfs[j].abATAPISense, sizeof(pThis->aCts[i].aIfs[j].abATAPISense));
}
else
{
uint8_t uATAPISenseKey, uATAPIASC;
memset(pThis->aCts[i].aIfs[j].abATAPISense, '\0', sizeof(pThis->aCts[i].aIfs[j].abATAPISense));
pThis->aCts[i].aIfs[j].abATAPISense[0] = 0x70 | (1 << 7);
pThis->aCts[i].aIfs[j].abATAPISense[7] = 10;
SSMR3GetU8(pSSMHandle, &uATAPISenseKey);
SSMR3GetU8(pSSMHandle, &uATAPIASC);
pThis->aCts[i].aIfs[j].abATAPISense[2] = uATAPISenseKey & 0x0f;
pThis->aCts[i].aIfs[j].abATAPISense[12] = uATAPIASC;
}
/** @todo triple-check this hack after passthrough is working */
SSMR3GetU8(pSSMHandle, &pThis->aCts[i].aIfs[j].cNotifiedMediaChange);
SSMR3GetMem(pSSMHandle, &pThis->aCts[i].aIfs[j].Led, sizeof(pThis->aCts[i].aIfs[j].Led));
SSMR3GetU32(pSSMHandle, &pThis->aCts[i].aIfs[j].cbIOBuffer);
if (pThis->aCts[i].aIfs[j].cbIOBuffer)
{
if (pThis->aCts[i].aIfs[j].CTX_SUFF(pbIOBuffer))
SSMR3GetMem(pSSMHandle, pThis->aCts[i].aIfs[j].CTX_SUFF(pbIOBuffer), pThis->aCts[i].aIfs[j].cbIOBuffer);
else
{
LogRel(("ATA: No buffer for %d/%d\n", i, j));
if (SSMR3HandleGetAfter(pSSMHandle) != SSMAFTER_DEBUG_IT)
return VERR_SSM_LOAD_CONFIG_MISMATCH;
/* skip the buffer if we're loading for the debugger / animator. */
uint8_t u8Ignored;
size_t cbLeft = pThis->aCts[i].aIfs[j].cbIOBuffer;
while (cbLeft-- > 0)
SSMR3GetU8(pSSMHandle, &u8Ignored);
}
}
else
Assert(pThis->aCts[i].aIfs[j].CTX_SUFF(pbIOBuffer) == NULL);
}
}
SSMR3GetBool(pSSMHandle, &pThis->fPIIX4);
rc = SSMR3GetU32(pSSMHandle, &u32);
if (RT_FAILURE(rc))
return rc;
if (u32 != ~0U)
{
AssertMsgFailed(("u32=%#x expected ~0\n", u32));
rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
return rc;
}
return VINF_SUCCESS;
}
/**
* Construct a device instance for a VM.
*
* @returns VBox status.
* @param pDevIns The device instance data.
* If the registration structure is needed, pDevIns->pDevReg points to it.
* @param iInstance Instance number. Use this to figure out which registers and such to use.
* The device number is also found in pDevIns->iInstance, but since it's
* likely to be freqently used PDM passes it as parameter.
* @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
* of the device instance. It's also found in pDevIns->pCfgHandle, but like
* iInstance it's expected to be used a bit in this function.
*/
static DECLCALLBACK(int) ataConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
{
PCIATAState *pThis = PDMINS_2_DATA(pDevIns, PCIATAState *);
PPDMIBASE pBase;
int rc;
bool fGCEnabled;
bool fR0Enabled;
uint32_t DelayIRQMillies;
Assert(iInstance == 0);
/*
* Initialize NIL handle values (for the destructor).
*/
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
pThis->aCts[i].AsyncIOSem = NIL_RTSEMEVENT;
pThis->aCts[i].SuspendIOSem = NIL_RTSEMEVENT;
pThis->aCts[i].AsyncIORequestMutex = NIL_RTSEMEVENT;
}
/*
* Validate and read configuration.
*/
if (!CFGMR3AreValuesValid(pCfgHandle, "GCEnabled\0IRQDelay\0R0Enabled\0PIIX4\0"))
return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("PIIX3 configuration error: unknown option specified"));
rc = CFGMR3QueryBoolDef(pCfgHandle, "GCEnabled", &fGCEnabled, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("PIIX3 configuration error: failed to read GCEnabled as boolean"));
Log(("%s: fGCEnabled=%d\n", __FUNCTION__, fGCEnabled));
rc = CFGMR3QueryBoolDef(pCfgHandle, "R0Enabled", &fR0Enabled, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("PIIX3 configuration error: failed to read R0Enabled as boolean"));
Log(("%s: fR0Enabled=%d\n", __FUNCTION__, fR0Enabled));
rc = CFGMR3QueryU32Def(pCfgHandle, "IRQDelay", &DelayIRQMillies, 0);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("PIIX3 configuration error: failed to read IRQDelay as integer"));
Log(("%s: DelayIRQMillies=%d\n", __FUNCTION__, DelayIRQMillies));
Assert(DelayIRQMillies < 50);
rc = CFGMR3QueryBoolDef(pCfgHandle, "PIIX4", &pThis->fPIIX4, false);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("PIIX3 configuration error: failed to read PIIX4 as boolean"));
Log(("%s: fPIIX4=%d\n", __FUNCTION__, pThis->fPIIX4));
/*
* Initialize data (most of it anyway).
*/
/* Status LUN. */
pThis->IBase.pfnQueryInterface = ataStatus_QueryInterface;
pThis->ILeds.pfnQueryStatusLed = ataStatus_QueryStatusLed;
/* PCI configuration space. */
PCIDevSetVendorId(&pThis->dev, 0x8086); /* Intel */
if (pThis->fPIIX4)
{
PCIDevSetDeviceId(&pThis->dev, 0x7111); /* PIIX4 IDE */
PCIDevSetRevisionId(&pThis->dev, 0x01); /* PIIX4E */
pThis->dev.config[0x48] = 0x00; /* UDMACTL */
pThis->dev.config[0x4A] = 0x00; /* UDMATIM */
pThis->dev.config[0x4B] = 0x00;
}
else
PCIDevSetDeviceId(&pThis->dev, 0x7010); /* PIIX3 IDE */
PCIDevSetCommand( &pThis->dev, PCI_COMMAND_IOACCESS | PCI_COMMAND_MEMACCESS | PCI_COMMAND_BUSMASTER);
PCIDevSetClassProg( &pThis->dev, 0x8a); /* programming interface = PCI_IDE bus master is supported */
PCIDevSetClassSub( &pThis->dev, 0x01); /* class_sub = PCI_IDE */
PCIDevSetClassBase( &pThis->dev, 0x01); /* class_base = PCI_mass_storage */
PCIDevSetHeaderType(&pThis->dev, 0x00);
pThis->pDevIns = pDevIns;
pThis->fGCEnabled = fGCEnabled;
pThis->fR0Enabled = fR0Enabled;
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
pThis->aCts[i].pDevInsR3 = pDevIns;
pThis->aCts[i].pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pThis->aCts[i].pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
pThis->aCts[i].DelayIRQMillies = (uint32_t)DelayIRQMillies;
for (uint32_t j = 0; j < RT_ELEMENTS(pThis->aCts[i].aIfs); j++)
{
pThis->aCts[i].aIfs[j].iLUN = i * RT_ELEMENTS(pThis->aCts) + j;
pThis->aCts[i].aIfs[j].pDevInsR3 = pDevIns;
pThis->aCts[i].aIfs[j].pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pThis->aCts[i].aIfs[j].pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
pThis->aCts[i].aIfs[j].pControllerR3 = &pThis->aCts[i];
pThis->aCts[i].aIfs[j].pControllerR0 = MMHyperR3ToR0(PDMDevHlpGetVM(pDevIns), &pThis->aCts[i]);
pThis->aCts[i].aIfs[j].pControllerRC = MMHyperR3ToRC(PDMDevHlpGetVM(pDevIns), &pThis->aCts[i]);
pThis->aCts[i].aIfs[j].IBase.pfnQueryInterface = ataQueryInterface;
pThis->aCts[i].aIfs[j].IMountNotify.pfnMountNotify = ataMountNotify;
pThis->aCts[i].aIfs[j].IMountNotify.pfnUnmountNotify = ataUnmountNotify;
pThis->aCts[i].aIfs[j].Led.u32Magic = PDMLED_MAGIC;
}
}
Assert(RT_ELEMENTS(pThis->aCts) == 2);
pThis->aCts[0].irq = 14;
pThis->aCts[0].IOPortBase1 = 0x1f0;
pThis->aCts[0].IOPortBase2 = 0x3f6;
pThis->aCts[1].irq = 15;
pThis->aCts[1].IOPortBase1 = 0x170;
pThis->aCts[1].IOPortBase2 = 0x376;
/*
* Register the PCI device.
* N.B. There's a hack in the PIIX3 PCI bridge device to assign this
* device the slot next to itself.
*/
rc = PDMDevHlpPCIRegister(pDevIns, &pThis->dev);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("PIIX3 cannot register PCI device"));
AssertMsg(pThis->dev.devfn == 9 || iInstance != 0, ("pThis->dev.devfn=%d\n", pThis->dev.devfn));
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 4, 0x10, PCI_ADDRESS_SPACE_IO, ataBMDMAIORangeMap);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("PIIX3 cannot register PCI I/O region for BMDMA"));
/*
* Register the I/O ports.
* The ports are all hardcoded and enforced by the PIIX3 host bridge controller.
*/
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
rc = PDMDevHlpIOPortRegister(pDevIns, pThis->aCts[i].IOPortBase1, 8, (RTHCPTR)i,
ataIOPortWrite1, ataIOPortRead1, ataIOPortWriteStr1, ataIOPortReadStr1, "ATA I/O Base 1");
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("PIIX3 cannot register I/O handlers"));
if (fGCEnabled)
{
rc = PDMDevHlpIOPortRegisterGC(pDevIns, pThis->aCts[i].IOPortBase1, 8, (RTGCPTR)i,
"ataIOPortWrite1", "ataIOPortRead1", "ataIOPortWriteStr1", "ataIOPortReadStr1", "ATA I/O Base 1");
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("PIIX3 cannot register I/O handlers (GC)"));
}
if (fR0Enabled)
{
#if 1
rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->aCts[i].IOPortBase1, 8, (RTR0PTR)i,
"ataIOPortWrite1", "ataIOPortRead1", NULL, NULL, "ATA I/O Base 1");
#else
rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->aCts[i].IOPortBase1, 8, (RTR0PTR)i,
"ataIOPortWrite1", "ataIOPortRead1", "ataIOPortWriteStr1", "ataIOPortReadStr1", "ATA I/O Base 1");
#endif
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, "PIIX3 cannot register I/O handlers (R0).");
}
rc = PDMDevHlpIOPortRegister(pDevIns, pThis->aCts[i].IOPortBase2, 1, (RTHCPTR)i,
ataIOPortWrite2, ataIOPortRead2, NULL, NULL, "ATA I/O Base 2");
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("PIIX3 cannot register base2 I/O handlers"));
if (fGCEnabled)
{
rc = PDMDevHlpIOPortRegisterGC(pDevIns, pThis->aCts[i].IOPortBase2, 1, (RTGCPTR)i,
"ataIOPortWrite2", "ataIOPortRead2", NULL, NULL, "ATA I/O Base 2");
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("PIIX3 cannot register base2 I/O handlers (GC)"));
}
if (fR0Enabled)
{
rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->aCts[i].IOPortBase2, 1, (RTR0PTR)i,
"ataIOPortWrite2", "ataIOPortRead2", NULL, NULL, "ATA I/O Base 2");
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("PIIX3 cannot register base2 I/O handlers (R0)"));
}
for (uint32_t j = 0; j < RT_ELEMENTS(pThis->aCts[i].aIfs); j++)
{
ATADevState *pIf = &pThis->aCts[i].aIfs[j];
PDMDevHlpSTAMRegisterF(pDevIns, &pIf->StatATADMA, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of ATA DMA transfers.", "/Devices/ATA%d/Unit%d/DMA", i, j);
PDMDevHlpSTAMRegisterF(pDevIns, &pIf->StatATAPIO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of ATA PIO transfers.", "/Devices/ATA%d/Unit%d/PIO", i, j);
PDMDevHlpSTAMRegisterF(pDevIns, &pIf->StatATAPIDMA, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of ATAPI DMA transfers.", "/Devices/ATA%d/Unit%d/AtapiDMA", i, j);
PDMDevHlpSTAMRegisterF(pDevIns, &pIf->StatATAPIPIO, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of ATAPI PIO transfers.", "/Devices/ATA%d/Unit%d/AtapiPIO", i, j);
#ifdef VBOX_WITH_STATISTICS /** @todo release too. */
PDMDevHlpSTAMRegisterF(pDevIns, &pIf->StatReads, STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling of the read operations.", "/Devices/ATA%d/Unit%d/Reads", i, j);
#endif
PDMDevHlpSTAMRegisterF(pDevIns, &pIf->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data read.", "/Devices/ATA%d/Unit%d/ReadBytes", i, j);
#ifdef VBOX_INSTRUMENT_DMA_WRITES
PDMDevHlpSTAMRegisterF(pDevIns, &pIf->StatInstrVDWrites,STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling of the VD DMA write operations.","/Devices/ATA%d/Unit%d/InstrVDWrites", i, j);
#endif
#ifdef VBOX_WITH_STATISTICS
PDMDevHlpSTAMRegisterF(pDevIns, &pIf->StatWrites, STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling of the write operations.","/Devices/ATA%d/Unit%d/Writes", i, j);
#endif
PDMDevHlpSTAMRegisterF(pDevIns, &pIf->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data written.", "/Devices/ATA%d/Unit%d/WrittenBytes", i, j);
#ifdef VBOX_WITH_STATISTICS
PDMDevHlpSTAMRegisterF(pDevIns, &pIf->StatFlushes, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling of the flush operations.","/Devices/ATA%d/Unit%d/Flushes", i, j);
#endif
}
#ifdef VBOX_WITH_STATISTICS /** @todo release too. */
PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aCts[i].StatAsyncOps, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "The number of async operations.", "/Devices/ATA%d/Async/Operations", i);
/** @todo STAMUNIT_MICROSECS */
PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aCts[i].StatAsyncMinWait, STAMTYPE_U64_RESET, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, "Minimum wait in microseconds.", "/Devices/ATA%d/Async/MinWait", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aCts[i].StatAsyncMaxWait, STAMTYPE_U64_RESET, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, "Maximum wait in microseconds.", "/Devices/ATA%d/Async/MaxWait", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aCts[i].StatAsyncTimeUS, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, "Total time spent in microseconds.","/Devices/ATA%d/Async/TotalTimeUS", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aCts[i].StatAsyncTime, STAMTYPE_PROFILE_ADV, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling of async operations.", "/Devices/ATA%d/Async/Time", i);
PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aCts[i].StatLockWait, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling of locks.", "/Devices/ATA%d/Async/LockWait", i);
#endif /* VBOX_WITH_STATISTICS */
/* Initialize per-controller critical section */
char szName[24];
RTStrPrintf(szName, sizeof(szName), "ATA%d", i);
rc = PDMDevHlpCritSectInit(pDevIns, &pThis->aCts[i].lock, szName);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("PIIX3 cannot initialize critical section"));
}
/*
* Attach status driver (optional).
*/
rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
if (RT_SUCCESS(rc))
pThis->pLedsConnector = (PDMILEDCONNECTORS *)pBase->pfnQueryInterface(pBase, PDMINTERFACE_LED_CONNECTORS);
else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
{
AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
return PDMDEV_SET_ERROR(pDevIns, rc, N_("PIIX3 cannot attach to status driver"));
}
/*
* Attach the units.
*/
uint32_t cbTotalBuffer = 0;
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aCts); i++)
{
PATACONTROLLER pCtl = &pThis->aCts[i];
/*
* Start the worker thread.
*/
pCtl->uAsyncIOState = ATA_AIO_NEW;
rc = RTSemEventCreate(&pCtl->AsyncIOSem);
AssertRC(rc);
rc = RTSemEventCreate(&pCtl->SuspendIOSem);
AssertRC(rc);
rc = RTSemMutexCreate(&pCtl->AsyncIORequestMutex);
AssertRC(rc);
ataAsyncIOClearRequests(pCtl);
rc = RTThreadCreate(&pCtl->AsyncIOThread, ataAsyncIOLoop, (void *)pCtl, 128*1024, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "ATA");
AssertRC(rc);
Assert(pCtl->AsyncIOThread != NIL_RTTHREAD && pCtl->AsyncIOSem != NIL_RTSEMEVENT && pCtl->SuspendIOSem != NIL_RTSEMEVENT && pCtl->AsyncIORequestMutex != NIL_RTSEMMUTEX);
Log(("%s: controller %d AIO thread id %#x; sem %p susp_sem %p mutex %p\n", __FUNCTION__, i, pCtl->AsyncIOThread, pCtl->AsyncIOSem, pCtl->SuspendIOSem, pCtl->AsyncIORequestMutex));
for (uint32_t j = 0; j < RT_ELEMENTS(pCtl->aIfs); j++)
{
static const char *s_apszDescs[RT_ELEMENTS(pThis->aCts)][RT_ELEMENTS(pCtl->aIfs)] =
{
{ "Primary Master", "Primary Slave" },
{ "Secondary Master", "Secondary Slave" }
};
/*
* Try attach the block device and get the interfaces,
* required as well as optional.
*/
ATADevState *pIf = &pCtl->aIfs[j];
rc = PDMDevHlpDriverAttach(pDevIns, pIf->iLUN, &pIf->IBase, &pIf->pDrvBase, s_apszDescs[i][j]);
if (RT_SUCCESS(rc))
rc = ataConfigLun(pDevIns, pIf);
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
pIf->pDrvBase = NULL;
pIf->pDrvBlock = NULL;
pIf->cbIOBuffer = 0;
pIf->pbIOBufferR3 = NULL;
pIf->pbIOBufferR0 = NIL_RTR0PTR;
pIf->pbIOBufferRC = NIL_RTGCPTR;
LogRel(("PIIX3 ATA: LUN#%d: no unit\n", pIf->iLUN));
}
else
{
AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pIf->iLUN, rc));
switch (rc)
{
case VERR_ACCESS_DENIED:
/* Error already catched by DrvHostBase */
return rc;
default:
return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
N_("PIIX3 cannot attach drive to the %s"),
s_apszDescs[i][j]);
}
}
cbTotalBuffer += pIf->cbIOBuffer;
}
}
rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance,
ATA_SAVED_STATE_VERSION, sizeof(*pThis) + cbTotalBuffer,
ataSaveLoadPrep, ataSaveExec, NULL,
ataSaveLoadPrep, ataLoadExec, NULL);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("PIIX3 cannot register save state handlers"));
/*
* Initialize the device state.
*/
ataReset(pDevIns);
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DevicePIIX3IDE =
{
/* u32Version */
PDM_DEVREG_VERSION,
/* szDeviceName */
"piix3ide",
/* szGCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"Intel PIIX3 ATA controller.\n"
" LUN #0 is primary master.\n"
" LUN #1 is primary slave.\n"
" LUN #2 is secondary master.\n"
" LUN #3 is secondary slave.\n"
" LUN #999 is the LED/Status connector.",
/* fFlags */
PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
/* fClass */
PDM_DEVREG_CLASS_STORAGE,
/* cMaxInstances */
1,
/* cbInstance */
sizeof(PCIATAState),
/* pfnConstruct */
ataConstruct,
/* pfnDestruct */
ataDestruct,
/* pfnRelocate */
ataRelocate,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
ataReset,
/* pfnSuspend */
ataSuspend,
/* pfnResume */
ataResume,
/* pfnAttach */
ataAttach,
/* pfnDetach */
ataDetach,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
ataPowerOff
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */