DevAHCI.cpp revision f20f7cf8c2a0f141965d11345d910cc8e65c1fb3
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync/* $Id$ */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync/** @file
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * DevAHCI - AHCI controller device (disk and cdrom).
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync *
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * Implements the AHCI standard 1.1
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync */
c7814cf6e1240a519cbec0441e033d0e2470ed00vboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync/*
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * Copyright (C) 2006-2013 Oracle Corporation
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync *
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * available from http://www.virtualbox.org. This file is free software;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * you can redistribute it and/or modify it under the terms of the GNU
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * General Public License (GPL) as published by the Free Software
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync/** @page pg_dev_ahci AHCI - Advanced Host Controller Interface Emulation.
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync *
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * This component implements an AHCI serial ATA controller. The device is split
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * into two parts. The first part implements the register interface for the
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * guest and the second one does the data transfer.
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync *
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * The guest can access the controller in two ways. The first one is the native
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * way implementing the registers described in the AHCI specification and is
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * the preferred one. The second implements the I/O ports used for booting from
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * the hard disk and for guests which don't have an AHCI SATA driver.
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync *
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * The data is transferred in an asynchronous way using one thread per implemented
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * port or using the new async completion interface which is still under
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * development. [not quite up to date]
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync/*******************************************************************************
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync* Header Files *
e4bf6817370e1a71833a02285515694afcda7599vboxsync*******************************************************************************/
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync#define LOG_GROUP LOG_GROUP_DEV_AHCI
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync#include <VBox/vmm/pdmdev.h>
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#include <VBox/vmm/pdmqueue.h>
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync#include <VBox/vmm/pdmthread.h>
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync#include <VBox/vmm/pdmcritsect.h>
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync#include <VBox/sup.h>
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync#include <VBox/scsi.h>
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#include <iprt/assert.h>
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#include <iprt/asm.h>
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#include <iprt/string.h>
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#ifdef IN_RING3
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync# include <iprt/param.h>
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# include <iprt/thread.h>
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# include <iprt/semaphore.h>
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# include <iprt/alloc.h>
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# include <iprt/uuid.h>
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# include <iprt/time.h>
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#endif
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#include "PIIX3ATABmDma.h"
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#include "ide.h"
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#include "ATAPIPassthrough.h"
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#include "VBoxDD.h"
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#if defined(VBOX_WITH_DTRACE) \
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync && defined(IN_RING3) \
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync# include "dtrace/VBoxDD.h"
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#else
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync# define VBOXDD_AHCI_REQ_SUBMIT(a,b,c,d) do { } while (0)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync# define VBOXDD_AHCI_REQ_SUBMIT_TIMESTAMP(a,b) do { } while (0)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync# define VBOXDD_AHCI_REQ_COMPLETED(a,b,c,d,e) do { } while (0)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync# define VBOXDD_AHCI_REQ_COMPLETED_TIMESTAMP(a,b) do { } while (0)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#endif
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/** Maximum number of ports available.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Spec defines 32 but we have one allocated for command completion coalescing
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * and another for a reserved future feature.
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_MAX_NR_PORTS_IMPL 30
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/** Maximum number of command slots available. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_NR_COMMAND_SLOTS 32
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_MAX_ALLOC_TOO_MUCH 20
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/** The current saved state version. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_SAVED_STATE_VERSION 8
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/** The saved state version before changing the port reset logic in an incompatible way. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_SAVED_STATE_VERSION_PRE_PORT_RESET_CHANGES 7
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/** Saved state version before the per port hotplug port was added. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_SAVED_STATE_VERSION_PRE_HOTPLUG_FLAG 6
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/** Saved state version before legacy ATA emulation was dropped. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_SAVED_STATE_VERSION_IDE_EMULATION 5
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/** Saved state version before ATAPI support was added. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_SAVED_STATE_VERSION_PRE_ATAPI 3
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/** The saved state version use in VirtualBox 3.0 and earlier.
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * This was before the config was added and ahciIOTasks was dropped. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_SAVED_STATE_VERSION_VBOX_30 2
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/* for Older ATA state Read handling */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_CTL_SAVED_STATE_VERSION 3
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_CTL_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE 1
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_CTL_SAVED_STATE_VERSION_WITHOUT_EVENT_STATUS 2
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/** The maximum number of release log entries per device. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define MAX_LOG_REL_ERRORS 1024
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/**
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * Maximum number of sectors to transfer in a READ/WRITE MULTIPLE request.
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * Set to 1 to disable multi-sector read support. According to the ATA
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * specification this must be a power of 2 and it must fit in an 8 bit
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * value. Thus the only valid values are 1, 2, 4, 8, 16, 32, 64 and 128.
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_MAX_MULT_SECTORS 128
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/**
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * Fastest PIO mode supported by the drive.
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_PIO_MODE_MAX 4
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/**
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * Fastest MDMA mode supported by the drive.
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_MDMA_MODE_MAX 2
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/**
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * Fastest UDMA mode supported by the drive.
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_UDMA_MODE_MAX 6
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/**
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * Length of the configurable VPD data (without termination)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_SERIAL_NUMBER_LENGTH 20
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_FIRMWARE_REVISION_LENGTH 8
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_MODEL_NUMBER_LENGTH 40
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_ATAPI_INQUIRY_VENDOR_ID_LENGTH 8
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_ATAPI_INQUIRY_PRODUCT_ID_LENGTH 16
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define AHCI_ATAPI_INQUIRY_REVISION_LENGTH 4
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync/* MediaEventStatus */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_EVENT_STATUS_UNCHANGED 0 /**< medium event status not changed */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_EVENT_STATUS_MEDIA_NEW 1 /**< new medium inserted */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_EVENT_STATUS_MEDIA_REMOVED 2 /**< medium removed */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_EVENT_STATUS_MEDIA_CHANGED 3 /**< medium was removed + new medium was inserted */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync#define ATA_EVENT_STATUS_MEDIA_EJECT_REQUESTED 4 /**< medium eject requested (eject button pressed) */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/* Media track type */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define ATA_MEDIA_TYPE_UNKNOWN 0 /**< unknown CD type */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/** ATAPI sense info size. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define ATAPI_SENSE_SIZE 64
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/**
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * Command Header.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#pragma pack(1)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsynctypedef struct
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync{
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Description Information. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync uint32_t u32DescInf;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Command status. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync uint32_t u32PRDBC;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Command Table Base Address. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync uint32_t u32CmdTblAddr;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Command Table Base Address - upper 32-bits. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync uint32_t u32CmdTblAddrUp;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Reserved */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync uint32_t u32Reserved[4];
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync} CmdHdr;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#pragma pack()
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsyncAssertCompileSize(CmdHdr, 32);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/* Defines for the command header. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_PRDTL_MASK 0xffff0000
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_PRDTL_ENTRIES(x) ((x & AHCI_CMDHDR_PRDTL_MASK) >> 16)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_C RT_BIT(10)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_B RT_BIT(9)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_R RT_BIT(8)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_P RT_BIT(7)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_W RT_BIT(6)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_A RT_BIT(5)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_CFL_MASK 0x1f
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_PRDT_OFFSET 0x80
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDHDR_ACMD_OFFSET 0x40
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/* Defines for the command FIS. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/* Defines that are used in the first double word. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_TYPE 0 /* The first byte. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_H2D 0x27 /* Register - Host to Device FIS. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_H2D_SIZE 20 /* Five double words. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_D2H 0x34 /* Register - Device to Host FIS. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_D2H_SIZE 20 /* Five double words. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_SETDEVBITS 0xa1 /* Set Device Bits - Device to Host FIS. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_SETDEVBITS_SIZE 8 /* Two double words. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_DMAACTD2H 0x39 /* DMA Activate - Device to Host FIS. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_DMAACTD2H_SIZE 4 /* One double word. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_DMASETUP 0x41 /* DMA Setup - Bidirectional FIS. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_DMASETUP_SIZE 28 /* Seven double words. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_PIOSETUP 0x5f /* PIO Setup - Device to Host FIS. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_PIOSETUP_SIZE 20 /* Five double words. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_TYPE_DATA 0x46 /* Data - Bidirectional FIS. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_BITS 1 /* Interrupt and Update bit. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_C RT_BIT(7) /* Host to device. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_I RT_BIT(6) /* Device to Host. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_D RT_BIT(5)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_CMD 2
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_FET 3
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_SECTN 4
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_CYLL 5
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_CYLH 6
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_HEAD 7
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_SECTNEXP 8
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_CYLLEXP 9
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_CYLHEXP 10
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_FETEXP 11
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_SECTC 12
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_SECTCEXP 13
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_CTL 15
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_CTL_SRST RT_BIT(2) /* Reset device. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync# define AHCI_CMDFIS_CTL_NIEN RT_BIT(1) /* Assert or clear interrupt. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/* For D2H FIS */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_STS 2
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#define AHCI_CMDFIS_ERR 3
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/** Pointer to a task state. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsynctypedef struct AHCIREQ *PAHCIREQ;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/**
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * Data processing callback
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * @returns VBox status.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * @param pAhciReq The task state.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * @param ppvProc Where to store the pointer to the buffer holding the processed data on success.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * Must be freed with RTMemFree().
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * @param pcbProc Where to store the size of the buffer on success.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsynctypedef DECLCALLBACK(int) FNAHCIPOSTPROCESS(PAHCIREQ pAhciReq, void **ppvProc, size_t *pcbProc);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/** Pointer to a FNAHCIPOSTPROCESS() function. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsynctypedef FNAHCIPOSTPROCESS *PFNAHCIPOSTPROCESS;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/**
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * Transfer type.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsynctypedef enum AHCITXDIR
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync{
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Invalid */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync AHCITXDIR_INVALID = 0,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** None */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync AHCITXDIR_NONE,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Read */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync AHCITXDIR_READ,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Write */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync AHCITXDIR_WRITE,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Flush */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync AHCITXDIR_FLUSH,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Trim */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync AHCITXDIR_TRIM
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync} AHCITXDIR;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/**
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * Task state.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsynctypedef enum AHCITXSTATE
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync{
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Invalid. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync AHCITXSTATE_INVALID = 0,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /** Task is not active. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync AHCITXSTATE_FREE,
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Task is active */
e4bf6817370e1a71833a02285515694afcda7599vboxsync AHCITXSTATE_ACTIVE,
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Task was canceled but the request didn't completed yet. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync AHCITXSTATE_CANCELED,
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** 32bit hack. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync AHCITXSTATE_32BIT_HACK = 0x7fffffff
e4bf6817370e1a71833a02285515694afcda7599vboxsync} AHCITXSTATE, *PAHCITXSTATE;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync/** Task encountered a buffer overflow. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_REQ_OVERFLOW RT_BIT_32(0)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/** Request is a PIO data command, if this flag is not set it either is
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * a command which does not transfer data or a DMA command based on the transfer size. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#define AHCI_REQ_PIO_DATA RT_BIT_32(1)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/** The request has the SACT register set. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#define AHCI_REQ_CLEAR_SACT RT_BIT_32(2)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/** FLag whether the request is queued. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#define AHCI_REQ_IS_QUEUED RT_BIT_32(3)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/**
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * A task state.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsynctypedef struct AHCIREQ
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Task state. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync volatile AHCITXSTATE enmTxState;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Start timestamp of the request. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint64_t tsStart;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Tag of the task. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint32_t uTag;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** The command header for this task. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync CmdHdr cmdHdr;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** The command Fis for this task. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint8_t cmdFis[AHCI_CMDFIS_TYPE_H2D_SIZE];
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** The ATAPI command data. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint8_t aATAPICmd[ATAPI_PACKET_SIZE];
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Size of one sector for the ATAPI transfer. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync size_t cbATAPISector;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Physical address of the command header. - GC */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync RTGCPHYS GCPhysCmdHdrAddr;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Physical address if the PRDT */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync RTGCPHYS GCPhysPrdtl;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Number of entries in the PRDTL. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync unsigned cPrdtlEntries;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Data direction. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync AHCITXDIR enmTxDir;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Start offset. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint64_t uOffset;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Number of bytes to transfer. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint32_t cbTransfer;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** ATA error register */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint8_t uATARegError;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** ATA status register */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint8_t uATARegStatus;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Flags for this task. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint32_t fFlags;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Additional memory allocation for this task. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync void *pvAlloc;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Siize of the allocation. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync size_t cbAlloc;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Number of times we had too much memory allocated for the request. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync unsigned cAllocTooMuch;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Data dependent on the transfer direction. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync union
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Data for an I/O request. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync struct
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Data segment. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync RTSGSEG DataSeg;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Post processing callback.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * If this is set we will use a buffer for the data
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * and the callback returns a buffer with the final data. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync PFNAHCIPOSTPROCESS pfnPostProcess;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync } Io;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync /** Data for a trim request. */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync struct
e4bf6817370e1a71833a02285515694afcda7599vboxsync {
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Pointer to the array of ranges to trim. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PRTRANGE paRanges;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Number of entries in the array. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync unsigned cRanges;
e4bf6817370e1a71833a02285515694afcda7599vboxsync } Trim;
e4bf6817370e1a71833a02285515694afcda7599vboxsync } u;
e4bf6817370e1a71833a02285515694afcda7599vboxsync} AHCIREQ;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync/**
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * Notifier queue item.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync */
5b0a093ca572a855886faa6747ad46df859dd041vboxsynctypedef struct DEVPORTNOTIFIERQUEUEITEM
5b0a093ca572a855886faa6747ad46df859dd041vboxsync{
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** The core part owned by the queue manager. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync PDMQUEUEITEMCORE Core;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** The port to process. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync uint8_t iPort;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync} DEVPORTNOTIFIERQUEUEITEM, *PDEVPORTNOTIFIERQUEUEITEM;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync/**
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @implements PDMIBASE
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @implements PDMIBLOCKPORT
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @implements PDMIBLOCKASYNCPORT
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @implements PDMIMOUNTNOTIFY
5b0a093ca572a855886faa6747ad46df859dd041vboxsync */
5b0a093ca572a855886faa6747ad46df859dd041vboxsynctypedef struct AHCIPort
5b0a093ca572a855886faa6747ad46df859dd041vboxsync{
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** Pointer to the device instance - HC ptr */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync PPDMDEVINSR3 pDevInsR3;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Pointer to the device instance - R0 ptr */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync PPDMDEVINSR0 pDevInsR0;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Pointer to the device instance - RC ptr. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync PPDMDEVINSRC pDevInsRC;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync#if HC_ARCH_BITS == 64
ad66a27959d7085aa31760f63ce082943be60e89vboxsync uint32_t Alignment0;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync#endif
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Pointer to the parent AHCI structure - R3 ptr. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync R3PTRTYPE(struct AHCI *) pAhciR3;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Pointer to the parent AHCI structure - R0 ptr. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync R0PTRTYPE(struct AHCI *) pAhciR0;
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync /** Pointer to the parent AHCI structure - RC ptr. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync RCPTRTYPE(struct AHCI *) pAhciRC;
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Command List Base Address. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync uint32_t regCLB;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Command List Base Address upper bits. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t regCLBU;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** FIS Base Address. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t regFB;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** FIS Base Address upper bits. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t regFBU;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Interrupt Status. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync volatile uint32_t regIS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Interrupt Enable. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t regIE;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Command. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t regCMD;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Task File Data. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t regTFD;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Signature */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t regSIG;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Serial ATA Status. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t regSSTS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Serial ATA Control. */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync uint32_t regSCTL;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync /** Serial ATA Error. */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync uint32_t regSERR;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync /** Serial ATA Active. */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync volatile uint32_t regSACT;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** Command Issue. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync uint32_t regCI;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Current number of active tasks. */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync volatile uint32_t cTasksActive;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** Command List Base Address */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync volatile RTGCPHYS GCPhysAddrClb;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** FIS Base Address */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync volatile RTGCPHYS GCPhysAddrFb;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** Device is powered on. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync bool fPoweredOn;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** Device has spun up. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync bool fSpunUp;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** First D2H FIS was send. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync bool fFirstD2HFisSend;
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync /** Mark the drive as having a non-rotational medium (i.e. as a SSD). */
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync bool fNonRotational;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** Attached device is a CD/DVD drive. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync bool fATAPI;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Passthrough SCSI commands. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync bool fATAPIPassthrough;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Flag whether this port is in a reset state. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync volatile bool fPortReset;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** If we use the new async interface. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync bool fAsyncInterface;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Flag if we are in a device reset. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync bool fResetDevice;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Flag whether this port is hot plug capable. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync bool fHotpluggable;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Flag whether the port is in redo task mode. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync volatile bool fRedo;
e961f5bfe1727c6816d3dad3805ebe21b6ba1c64vboxsync /** Flag whether the worker thread is sleeping. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync volatile bool fWrkThreadSleeping;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync bool afAlignment[3];
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Number of total sectors. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint64_t cTotalSectors;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Size of one sector. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t cbSector;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Currently configured number of sectors in a multi-sector transfer. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t cMultSectors;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Currently active transfer mode (MDMA/UDMA) and speed. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint8_t uATATransferMode;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** ATAPI sense data. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint8_t abATAPISense[ATAPI_SENSE_SIZE];
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** HACK: Countdown till we report a newly unmounted drive as mounted. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint8_t cNotifiedMediaChange;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** Exponent of logical sectors in a physical sector, number of logical sectors is 2^exp. */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync uint8_t cLogSectorsPerPhysicalExp;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync /** The same for GET_EVENT_STATUS for mechanism */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync volatile uint32_t MediaEventStatus;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync /** Media type if known. */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync volatile uint32_t MediaTrackType;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The LUN. */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync RTUINT iLUN;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync /** Bitmap for finished tasks (R3 -> Guest). */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync volatile uint32_t u32TasksFinished;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync /** Bitmap for finished queued tasks (R3 -> Guest). */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync volatile uint32_t u32QueuedTasksFinished;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync /** Bitmap for new queued tasks (Guest -> R3). */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync volatile uint32_t u32TasksNew;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /** Bitmap of tasks which must be redone because of a non fatal error. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync volatile uint32_t u32TasksRedo;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync /** Current command slot processed.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Accessed by the guest by reading the CMD register.
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync * Holds the command slot of the command processed at the moment. */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync volatile uint32_t u32CurrentCommandSlot;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#if HC_ARCH_BITS == 64
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync uint32_t u32Alignment2;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#endif
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /** Device specific settings (R3 only stuff). */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Pointer to the attached driver's base interface. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync R3PTRTYPE(PPDMIBASE) pDrvBase;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Pointer to the attached driver's block interface. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync R3PTRTYPE(PPDMIBLOCK) pDrvBlock;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /** Pointer to the attached driver's async block interface. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync R3PTRTYPE(PPDMIBLOCKASYNC) pDrvBlockAsync;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /** Pointer to the attached driver's block bios interface. */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync R3PTRTYPE(PPDMIBLOCKBIOS) pDrvBlockBios;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Pointer to the attached driver's mount interface. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync R3PTRTYPE(PPDMIMOUNT) pDrvMount;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The base interface. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMIBASE IBase;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The block port interface. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMIBLOCKPORT IPort;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The optional block async port interface. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMIBLOCKASYNCPORT IPortAsync;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The mount notify interface. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMIMOUNTNOTIFY IMountNotify;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Physical geometry of this image. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMMEDIAGEOMETRY PCHSGeometry;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The status LED state for this drive. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMLED Led;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uint32_t u32Alignment3;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Async IO Thread. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync R3PTRTYPE(PPDMTHREAD) pAsyncIOThread;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /**
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync * Array of cached tasks. The tag number is the index value.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Only used with the async interface.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync R3PTRTYPE(PAHCIREQ) aCachedTasks[AHCI_NR_COMMAND_SLOTS];
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** First task throwing an error. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync R3PTRTYPE(volatile PAHCIREQ) pTaskErr;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The current tracklist of the loaded medium if passthrough is used. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync R3PTRTYPE(PTRACKLIST) pTrackList;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The event semaphore the processing thread waits on. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync SUPSEMEVENT hEvtProcess;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Release statistics: number of DMA commands. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync STAMCOUNTER StatDMA;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Release statistics: number of bytes written. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync STAMCOUNTER StatBytesWritten;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Release statistics: number of bytes read. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync STAMCOUNTER StatBytesRead;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Release statistics: Number of I/O requests processed per second. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync STAMCOUNTER StatIORequestsPerSecond;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync#ifdef VBOX_WITH_STATISTICS
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Statistics: Time to complete one request. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync STAMPROFILE StatProfileProcessTime;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** Statistics: Amount of time to read/write data. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync STAMPROFILE StatProfileReadWrite;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync#endif /* VBOX_WITH_STATISTICS */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The serial numnber to use for IDENTIFY DEVICE commands. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync char szSerialNumber[AHCI_SERIAL_NUMBER_LENGTH+1]; /** < one extra byte for termination */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The firmware revision to use for IDENTIFY DEVICE commands. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync char szFirmwareRevision[AHCI_FIRMWARE_REVISION_LENGTH+1]; /** < one extra byte for termination */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The model number to use for IDENTIFY DEVICE commands. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync char szModelNumber[AHCI_MODEL_NUMBER_LENGTH+1]; /** < one extra byte for termination */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The vendor identification string for SCSI INQUIRY commands. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync char szInquiryVendorId[AHCI_ATAPI_INQUIRY_VENDOR_ID_LENGTH+1];
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /** The product identification string for SCSI INQUIRY commands. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync char szInquiryProductId[AHCI_ATAPI_INQUIRY_PRODUCT_ID_LENGTH+1];
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** The revision string for SCSI INQUIRY commands. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync char szInquiryRevision[AHCI_ATAPI_INQUIRY_REVISION_LENGTH+1];
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Error counter */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t cErrors;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t u32Alignment5;
e4bf6817370e1a71833a02285515694afcda7599vboxsync} AHCIPort;
e4bf6817370e1a71833a02285515694afcda7599vboxsync/** Pointer to the state of an AHCI port. */
e4bf6817370e1a71833a02285515694afcda7599vboxsynctypedef AHCIPort *PAHCIPort;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync/**
e4bf6817370e1a71833a02285515694afcda7599vboxsync * Main AHCI device state.
e4bf6817370e1a71833a02285515694afcda7599vboxsync *
e4bf6817370e1a71833a02285515694afcda7599vboxsync * @implements PDMILEDPORTS
e4bf6817370e1a71833a02285515694afcda7599vboxsync */
e4bf6817370e1a71833a02285515694afcda7599vboxsynctypedef struct AHCI
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** The PCI device structure. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PCIDEVICE dev;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Pointer to the device instance - R3 ptr */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PPDMDEVINSR3 pDevInsR3;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Pointer to the device instance - R0 ptr */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PPDMDEVINSR0 pDevInsR0;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Pointer to the device instance - RC ptr. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PPDMDEVINSRC pDevInsRC;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#if HC_ARCH_BITS == 64
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t Alignment0;
e4bf6817370e1a71833a02285515694afcda7599vboxsync#endif
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Status LUN: The base interface. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PDMIBASE IBase;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Status LUN: Leds interface. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PDMILEDPORTS ILeds;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Status LUN: Partner of ILeds. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Status LUN: Media Notifys. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync R3PTRTYPE(PPDMIMEDIANOTIFY) pMediaNotify;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#if HC_ARCH_BITS == 32
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t Alignment1;
e4bf6817370e1a71833a02285515694afcda7599vboxsync#endif
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /** Base address of the MMIO region. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync RTGCPHYS MMIOBase;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Base address of the I/O port region for Idx/Data. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync RTIOPORT IOPortBase;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Global Host Control register of the HBA */
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** HBA Capabilities - Readonly */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t regHbaCap;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** HBA Control */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t regHbaCtrl;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /** Interrupt Status */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint32_t regHbaIs;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Ports Implemented - Readonly */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t regHbaPi;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** AHCI Version - Readonly */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t regHbaVs;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Command completion coalescing control */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t regHbaCccCtl;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /** Command completion coalescing ports */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint32_t regHbaCccPorts;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Index register for BIOS access. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t regIdx;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#if HC_ARCH_BITS == 64
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t Alignment3;
e4bf6817370e1a71833a02285515694afcda7599vboxsync#endif
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Countdown timer for command completion coalescing - R3 ptr */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PTMTIMERR3 pHbaCccTimerR3;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Countdown timer for command completion coalescing - R0 ptr */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PTMTIMERR0 pHbaCccTimerR0;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Countdown timer for command completion coalescing - RC ptr */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PTMTIMERRC pHbaCccTimerRC;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#if HC_ARCH_BITS == 64
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t Alignment4;
e4bf6817370e1a71833a02285515694afcda7599vboxsync#endif
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Queue to send tasks to R3. - HC ptr */
e4bf6817370e1a71833a02285515694afcda7599vboxsync R3PTRTYPE(PPDMQUEUE) pNotifierQueueR3;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Queue to send tasks to R3. - HC ptr */
e4bf6817370e1a71833a02285515694afcda7599vboxsync R0PTRTYPE(PPDMQUEUE) pNotifierQueueR0;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Queue to send tasks to R3. - RC ptr */
e4bf6817370e1a71833a02285515694afcda7599vboxsync RCPTRTYPE(PPDMQUEUE) pNotifierQueueRC;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#if HC_ARCH_BITS == 64
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t Alignment5;
e4bf6817370e1a71833a02285515694afcda7599vboxsync#endif
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Which port number is used to mark an CCC interrupt */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint8_t uCccPortNr;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#if HC_ARCH_BITS == 64
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t Alignment6;
e4bf6817370e1a71833a02285515694afcda7599vboxsync#endif
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Timeout value */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint64_t uCccTimeout;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Number of completions used to assert an interrupt */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t uCccNr;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Current number of completed commands */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t uCccCurrentNr;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /** Register structure per port */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync AHCIPort ahciPort[AHCI_MAX_NR_PORTS_IMPL];
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** The critical section. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync PDMCRITSECT lock;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Bitmask of ports which asserted an interrupt. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync volatile uint32_t u32PortsInterrupted;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Number of I/O threads currently active - used for async controller reset handling. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync volatile uint32_t cThreadsActive;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Device is in a reset state. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync bool fReset;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Supports 64bit addressing */
e4bf6817370e1a71833a02285515694afcda7599vboxsync bool f64BitAddr;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** GC enabled. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync bool fGCEnabled;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** R0 enabled. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync bool fR0Enabled;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** If the new async interface is used if available. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync bool fUseAsyncInterfaceIfAvailable;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Indicates that PDMDevHlpAsyncNotificationCompleted should be called when
e4bf6817370e1a71833a02285515694afcda7599vboxsync * a port is entering the idle state. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync bool volatile fSignalIdle;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Flag whether the controller has BIOS access enabled. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync bool fBootable;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Flag whether the legacy port reset method should be used to make it work with saved states. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync bool fLegacyPortResetMethod;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Number of usable ports on this controller. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t cPortsImpl;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Number of usable command slots for each port. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t cCmdSlotsAvail;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** Flag whether we have written the first 4bytes in an 8byte MMIO write successfully. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync volatile bool f8ByteMMIO4BytesWrittenSuccessfully;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#if HC_ARCH_BITS == 64
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint32_t Alignment7;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#endif
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** The support driver session handle. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync R3R0PTRTYPE(PSUPDRVSESSION) pSupDrvSession;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync} AHCI;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/** Pointer to the state of an AHCI device. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsynctypedef AHCI *PAHCI;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/**
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * Scatter gather list entry.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
e4bf6817370e1a71833a02285515694afcda7599vboxsynctypedef struct
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Data Base Address. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t u32DBA;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /** Data Base Address - Upper 32-bits. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t u32DBAUp;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Reserved */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t u32Reserved;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /** Description information. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint32_t u32DescInf;
e4bf6817370e1a71833a02285515694afcda7599vboxsync} SGLEntry;
e4bf6817370e1a71833a02285515694afcda7599vboxsyncAssertCompileSize(SGLEntry, 16);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync/** Defines for a scatter gather list entry. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define SGLENTRY_DBA_READONLY ~(RT_BIT(0))
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define SGLENTRY_DESCINF_I RT_BIT(31)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define SGLENTRY_DESCINF_DBC 0x3fffff
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define SGLENTRY_DESCINF_READONLY 0x803fffff
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync/* Defines for the global host control registers for the HBA. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_HBA_GLOBAL_SIZE 0x100
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync/* Defines for the HBA Capabilities - Readonly */
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_S64A RT_BIT(31)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_SNCQ RT_BIT(30)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_SIS RT_BIT(28)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_SSS RT_BIT(27)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_SALP RT_BIT(26)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_SAL RT_BIT(25)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_SCLO RT_BIT(24)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_ISS (RT_BIT(23) | RT_BIT(22) | RT_BIT(21) | RT_BIT(20))
e4bf6817370e1a71833a02285515694afcda7599vboxsync# define AHCI_HBA_CAP_ISS_SHIFT(x) (((x) << 20) & AHCI_HBA_CAP_ISS)
e4bf6817370e1a71833a02285515694afcda7599vboxsync# define AHCI_HBA_CAP_ISS_GEN1 RT_BIT(0)
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync# define AHCI_HBA_CAP_ISS_GEN2 RT_BIT(1)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_SNZO RT_BIT(19)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_SAM RT_BIT(18)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_SPM RT_BIT(17)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_PMD RT_BIT(15)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_SSC RT_BIT(14)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_PSC RT_BIT(13)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_NCS (RT_BIT(12) | RT_BIT(11) | RT_BIT(10) | RT_BIT(9) | RT_BIT(8))
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_NCS_SET(x) (((x-1) << 8) & AHCI_HBA_CAP_NCS) /* 0's based */
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_CCCS RT_BIT(7)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_NP (RT_BIT(4) | RT_BIT(3) | RT_BIT(2) | RT_BIT(1) | RT_BIT(0))
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CAP_NP_SET(x) ((x-1) & AHCI_HBA_CAP_NP) /* 0's based */
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync/* Defines for the HBA Control register - Read/Write */
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CTRL_AE RT_BIT(31)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CTRL_IE RT_BIT(1)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CTRL_HR RT_BIT(0)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CTRL_RW_MASK (RT_BIT(0) | RT_BIT(1)) /* Mask for the used bits */
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync/* Defines for the HBA Version register - Readonly (We support AHCI 1.0) */
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_VS_MJR (1 << 16)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_VS_MNR 0x100
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync/* Defines for the command completion coalescing control register */
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CCC_CTL_TV 0xffff0000
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CCC_CTL_TV_SET(x) (x << 16)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CCC_CTL_TV_GET(x) ((x & AHCI_HBA_CCC_CTL_TV) >> 16)
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CCC_CTL_CC 0xff00
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CCC_CTL_CC_SET(x) (x << 8)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CCC_CTL_CC_GET(x) ((x & AHCI_HBA_CCC_CTL_CC) >> 8)
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CCC_CTL_INT 0xf8
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CCC_CTL_INT_SET(x) (x << 3)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CCC_CTL_INT_GET(x) ((x & AHCI_HBA_CCC_CTL_INT) >> 3)
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_HBA_CCC_CTL_EN RT_BIT(0)
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync/* Defines for the port registers. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_REGISTER_SIZE 0x80
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_CLB_RESERVED 0xfffffc00 /* For masking out the reserved bits. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_FB_RESERVED 0xffffff00 /* For masking out the reserved bits. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_CPDS RT_BIT(31)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_TFES RT_BIT(30)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_HBFS RT_BIT(29)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_HBDS RT_BIT(28)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_IFS RT_BIT(27)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_INFS RT_BIT(26)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_OFS RT_BIT(24)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_IPMS RT_BIT(23)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_PRCS RT_BIT(22)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IS_DIS RT_BIT(7)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_PCS RT_BIT(6)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_IS_DPS RT_BIT(5)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IS_UFS RT_BIT(4)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IS_SDBS RT_BIT(3)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IS_DSS RT_BIT(2)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IS_PSS RT_BIT(1)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IS_DHRS RT_BIT(0)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IS_READONLY 0xfd8000af /* Readonly mask including reserved bits. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_CPDE RT_BIT(31)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_TFEE RT_BIT(30)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_HBFE RT_BIT(29)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_HBDE RT_BIT(28)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_IFE RT_BIT(27)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_INFE RT_BIT(26)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_OFE RT_BIT(24)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_IPME RT_BIT(23)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#define AHCI_PORT_IE_PRCE RT_BIT(22)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#define AHCI_PORT_IE_DIE RT_BIT(7) /* Not supported for now, readonly. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_PCE RT_BIT(6)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_DPE RT_BIT(5)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_UFE RT_BIT(4)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_SDBE RT_BIT(3)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_DSE RT_BIT(2)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_PSE RT_BIT(1)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_DHRE RT_BIT(0)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_IE_READONLY (0xfdc000ff) /* Readonly mask including reserved bits. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_CMD_ICC (RT_BIT(28) | RT_BIT(29) | RT_BIT(30) | RT_BIT(31))
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_CMD_ICC_SHIFT(x) ((x) << 28)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# define AHCI_PORT_CMD_ICC_IDLE 0x0
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# define AHCI_PORT_CMD_ICC_ACTIVE 0x1
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# define AHCI_PORT_CMD_ICC_PARTIAL 0x2
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# define AHCI_PORT_CMD_ICC_SLUMBER 0x6
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_CMD_ASP RT_BIT(27) /* Not supported - Readonly */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#define AHCI_PORT_CMD_ALPE RT_BIT(26) /* Not supported - Readonly */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_CMD_DLAE RT_BIT(25)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_CMD_ATAPI RT_BIT(24)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_CMD_CPD RT_BIT(20)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_CMD_ISP RT_BIT(19) /* Readonly */
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_CMD_HPCP RT_BIT(18)
e4bf6817370e1a71833a02285515694afcda7599vboxsync#define AHCI_PORT_CMD_PMA RT_BIT(17) /* Not supported - Readonly */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_CPS RT_BIT(16)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_CR RT_BIT(15) /* Readonly */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_FR RT_BIT(14) /* Readonly */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_ISS RT_BIT(13) /* Readonly */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_CCS (RT_BIT(8) | RT_BIT(9) | RT_BIT(10) | RT_BIT(11) | RT_BIT(12))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_CCS_SHIFT(x) (x << 8) /* Readonly */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_FRE RT_BIT(4)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_CLO RT_BIT(3)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_POD RT_BIT(2)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_SUD RT_BIT(1)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_ST RT_BIT(0)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_CMD_READONLY (0xff02001f & ~(AHCI_PORT_CMD_ASP | AHCI_PORT_CMD_ALPE | AHCI_PORT_CMD_PMA))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SCTL_IPM (RT_BIT(11) | RT_BIT(10) | RT_BIT(9) | RT_BIT(8))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SCTL_IPM_GET(x) ((x & AHCI_PORT_SCTL_IPM) >> 8)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SCTL_SPD (RT_BIT(7) | RT_BIT(6) | RT_BIT(5) | RT_BIT(4))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SCTL_SPD_GET(x) ((x & AHCI_PORT_SCTL_SPD) >> 4)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SCTL_DET (RT_BIT(3) | RT_BIT(2) | RT_BIT(1) | RT_BIT(0))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SCTL_DET_GET(x) (x & AHCI_PORT_SCTL_DET)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SCTL_DET_NINIT 0
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SCTL_DET_INIT 1
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SCTL_DET_OFFLINE 4
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SCTL_READONLY 0xfff
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SSTS_IPM (RT_BIT(11) | RT_BIT(10) | RT_BIT(9) | RT_BIT(8))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SSTS_IPM_GET(x) ((x & AHCI_PORT_SCTL_IPM) >> 8)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SSTS_SPD (RT_BIT(7) | RT_BIT(6) | RT_BIT(5) | RT_BIT(4))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SSTS_SPD_GET(x) ((x & AHCI_PORT_SCTL_SPD) >> 4)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SSTS_DET (RT_BIT(3) | RT_BIT(2) | RT_BIT(1) | RT_BIT(0))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_SSTS_DET_GET(x) (x & AHCI_PORT_SCTL_DET)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_TFD_BSY RT_BIT(7)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_TFD_DRQ RT_BIT(3)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#define AHCI_PORT_TFD_ERR RT_BIT(0)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_SERR_X RT_BIT(26)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_SERR_W RT_BIT(18)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_SERR_N RT_BIT(16)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync/* Signatures for attached storage devices. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_SIG_DISK 0x00000101
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_PORT_SIG_ATAPI 0xeb140101
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync/*
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync * The AHCI spec defines an area of memory where the HBA posts received FIS's from the device.
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync * regFB points to the base of this area.
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync * Every FIS type has an offset where it is posted in this area.
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_RECFIS_DSFIS_OFFSET 0x00 /* DMA Setup FIS */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_RECFIS_PSFIS_OFFSET 0x20 /* PIO Setup FIS */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_RECFIS_RFIS_OFFSET 0x40 /* D2H Register FIS */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_RECFIS_SDBFIS_OFFSET 0x58 /* Set Device Bits FIS */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_RECFIS_UFIS_OFFSET 0x60 /* Unknown FIS type */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync/** Mask to get the LBA value from a LBA range. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_RANGE_LBA_MASK UINT64_C(0xffffffffffff)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync/** Mas to get the length value from a LBA range. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_RANGE_LENGTH_MASK UINT64_C(0xffff000000000000)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync/** Returns the length of the range in sectors. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_RANGE_LENGTH_GET(val) (((val) & AHCI_RANGE_LENGTH_MASK) >> 48)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync/**
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync * AHCI register operator.
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsynctypedef struct ahci_opreg
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync{
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync const char *pszName;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync int (*pfnRead )(PAHCI ahci, uint32_t iReg, uint32_t *pu32Value);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync int (*pfnWrite)(PAHCI ahci, uint32_t iReg, uint32_t u32Value);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync} AHCIOPREG;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync/**
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync * AHCI port register operator.
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsynctypedef struct pAhciPort_opreg
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync{
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync const char *pszName;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync int (*pfnRead )(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync int (*pfnWrite)(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync} AHCIPORTOPREG;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#ifndef VBOX_DEVICE_STRUCT_TESTCASE
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsyncRT_C_DECLS_BEGIN
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsyncstatic void ahciHBAReset(PAHCI pThis);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#ifdef IN_RING3
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsyncstatic int ahciPostFisIntoMemory(PAHCIPort pAhciPort, unsigned uFisType, uint8_t *cmdFis);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsyncstatic void ahciPostFirstD2HFisIntoMemory(PAHCIPort pAhciPort);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsyncstatic size_t ahciCopyToPrdtl(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync void *pvBuf, size_t cbBuf);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsyncstatic size_t ahciCopyFromPrdtl(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync void *pvBuf, size_t cbBuf);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsyncstatic bool ahciCancelActiveTasks(PAHCIPort pAhciPort);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#endif
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsyncRT_C_DECLS_END
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define PCIDEV_2_PAHCI(pPciDev) ( (PAHCI)(pPciDev) )
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define PDMIMOUNT_2_PAHCIPORT(pInterface) ( (PAHCIPort)((uintptr_t)(pInterface) - RT_OFFSETOF(AHCIPort, IMount)) )
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define PDMIMOUNTNOTIFY_2_PAHCIPORT(pInterface) ( (PAHCIPort)((uintptr_t)(pInterface) - RT_OFFSETOF(AHCIPort, IMountNotify)) )
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define PDMIBASE_2_PAHCIPORT(pInterface) ( (PAHCIPort)((uintptr_t)(pInterface) - RT_OFFSETOF(AHCIPort, IBase)) )
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define PDMIBLOCKPORT_2_PAHCIPORT(pInterface) ( (PAHCIPort)((uintptr_t)(pInterface) - RT_OFFSETOF(AHCIPort, IPort)) )
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define PDMIBASE_2_PAHCI(pInterface) ( (PAHCI)((uintptr_t)(pInterface) - RT_OFFSETOF(AHCI, IBase)) )
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define PDMILEDPORTS_2_PAHCI(pInterface) ( (PAHCI)((uintptr_t)(pInterface) - RT_OFFSETOF(AHCI, ILeds)) )
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#define AHCI_RTGCPHYS_FROM_U32(Hi, Lo) ( (RTGCPHYS)RT_MAKE_U64(Lo, Hi) )
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#ifdef IN_RING3
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# ifdef LOG_USE_C99
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# define ahciLog(a) \
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync Log(("R3 P%u: %M", pAhciPort->iLUN, _LogRelRemoveParentheseis a))
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# else
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# define ahciLog(a) \
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync do { Log(("R3 P%u: ", pAhciPort->iLUN)); Log(a); } while(0)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# endif
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#elif IN_RING0
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# ifdef LOG_USE_C99
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# define ahciLog(a) \
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync Log(("R0 P%u: %M", pAhciPort->iLUN, _LogRelRemoveParentheseis a))
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# else
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync# define ahciLog(a) \
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync do { Log(("R0 P%u: ", pAhciPort->iLUN)); Log(a); } while(0)
e4bf6817370e1a71833a02285515694afcda7599vboxsync# endif
e4bf6817370e1a71833a02285515694afcda7599vboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#elif IN_RC
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync# ifdef LOG_USE_C99
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync# define ahciLog(a) \
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Log(("GC P%u: %M", pAhciPort->iLUN, _LogRelRemoveParentheseis a))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync# else
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync# define ahciLog(a) \
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync do { Log(("GC P%u: ", pAhciPort->iLUN)); Log(a); } while(0)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync# endif
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#endif
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/**
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * Update PCI IRQ levels
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic void ahciHbaClearInterrupt(PAHCI pAhci)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Log(("%s: Clearing interrupt\n", __FUNCTION__));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync PDMDevHlpPCISetIrq(pAhci->CTX_SUFF(pDevIns), 0, 0);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/**
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * Updates the IRQ level and sets port bit in the global interrupt status register of the HBA.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic int ahciHbaSetInterrupt(PAHCI pAhci, uint8_t iPort, int rcBusy)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Log(("P%u: %s: Setting interrupt\n", iPort, __FUNCTION__));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync int rc = PDMCritSectEnter(&pAhci->lock, rcBusy);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (rc != VINF_SUCCESS)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return rc;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (pAhci->regHbaCtrl & AHCI_HBA_CTRL_IE)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if ((pAhci->regHbaCccCtl & AHCI_HBA_CCC_CTL_EN) && (pAhci->regHbaCccPorts & (1 << iPort)))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhci->uCccCurrentNr++;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (pAhci->uCccCurrentNr >= pAhci->uCccNr)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Reset command completion coalescing state. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync TMTimerSetMillies(pAhci->CTX_SUFF(pHbaCccTimer), pAhci->uCccTimeout);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhci->uCccCurrentNr = 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhci->u32PortsInterrupted |= (1 << pAhci->uCccPortNr);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (!(pAhci->u32PortsInterrupted & ~(1 << pAhci->uCccPortNr)))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Log(("P%u: %s: Fire interrupt\n", iPort, __FUNCTION__));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync PDMDevHlpPCISetIrq(pAhci->CTX_SUFF(pDevIns), 0, 1);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync else
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* If only the bit of the actual port is set assert an interrupt
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * because the interrupt status register was already read by the guest
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * and we need to send a new notification.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * Otherwise an interrupt is still pending.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ASMAtomicOrU32((volatile uint32_t *)&pAhci->u32PortsInterrupted, (1 << iPort));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (!(pAhci->u32PortsInterrupted & ~(1 << iPort)))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Log(("P%u: %s: Fire interrupt\n", iPort, __FUNCTION__));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync PDMDevHlpPCISetIrq(pAhci->CTX_SUFF(pDevIns), 0, 1);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync PDMCritSectLeave(&pAhci->lock);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return VINF_SUCCESS;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#ifdef IN_RING3
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/*
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * Assert irq when an CCC timeout occurs
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncDECLCALLBACK(void) ahciCccTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync PAHCI pAhci = (PAHCI)pvUser;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync int rc = ahciHbaSetInterrupt(pAhci, pAhci->uCccPortNr, VERR_IGNORED);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync AssertRC(rc);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/**
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * Finishes the port reset of the given port.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync *
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * @returns nothing.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * @param pAhciPort The port to finish the reset on.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic void ahciPortResetFinish(PAHCIPort pAhciPort)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Cancel all tasks first. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync bool fAllTasksCanceled = ahciCancelActiveTasks(pAhciPort);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Assert(fAllTasksCanceled);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Signature for SATA device. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (pAhciPort->fATAPI)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciPort->regSIG = AHCI_PORT_SIG_ATAPI;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync else
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciPort->regSIG = AHCI_PORT_SIG_DISK;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciPort->regSSTS = (0x01 << 8) | /* Interface is active. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync (0x03 << 0); /* Device detected and communication established. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /*
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * Use the maximum allowed speed.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * (Not that it changes anything really)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync switch (AHCI_PORT_SCTL_SPD_GET(pAhciPort->regSCTL))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case 0x01:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciPort->regSSTS |= (0x01 << 4); /* Generation 1 (1.5GBps) speed. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync break;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case 0x02:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case 0x00:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync default:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciPort->regSSTS |= (0x02 << 4); /* Generation 2 (3.0GBps) speed. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync break;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* We received a COMINIT from the device. Tell the guest. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_PCS);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciPort->regSERR |= AHCI_PORT_SERR_X;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciPort->regTFD |= ATA_STAT_BUSY;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if ((pAhciPort->regCMD & AHCI_PORT_CMD_FRE) && (!pAhciPort->fFirstD2HFisSend))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ahciPostFirstD2HFisIntoMemory(pAhciPort);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_DHRS);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (pAhciPort->regIE & AHCI_PORT_IE_DHRE)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync int rc = ahciHbaSetInterrupt(pAhciPort->CTX_SUFF(pAhci), pAhciPort->iLUN, VERR_IGNORED);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync AssertRC(rc);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ASMAtomicXchgBool(&pAhciPort->fPortReset, false);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#endif
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/**
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * Kicks the I/O thread from RC or R0.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync *
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * @returns nothing.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * @param pAhci The AHCI controller instance.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * @param pAhciPort The port to kick.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic void ahciIoThreadKick(PAHCI pAhci, PAHCIPort pAhciPort)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#ifdef IN_RC
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync PDEVPORTNOTIFIERQUEUEITEM pItem = (PDEVPORTNOTIFIERQUEUEITEM)PDMQueueAlloc(pAhci->CTX_SUFF(pNotifierQueue));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync AssertMsg(VALID_PTR(pItem), ("Allocating item for queue failed\n"));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (pItem)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pItem->iPort = pAhciPort->iLUN;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync PDMQueueInsert(pAhci->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#else
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync LogFlowFunc(("Signal event semaphore\n"));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync int rc = SUPSemEventSignal(pAhci->pSupDrvSession, pAhciPort->hEvtProcess);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync AssertRC(rc);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync#endif
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic int PortCmdIssue_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint32_t uCIValue;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Update the CI register first. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uCIValue = ASMAtomicXchgU32(&pAhciPort->u32TasksFinished, 0);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciPort->regCI &= ~uCIValue;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if ( (pAhciPort->regCMD & AHCI_PORT_CMD_CR)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync && u32Value > 0)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /*
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync * Clear all tasks which are already marked as busy. The guest
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync * shouldn't write already busy tasks actually.
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync u32Value &= ~pAhciPort->regCI;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ASMAtomicOrU32(&pAhciPort->u32TasksNew, u32Value);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /* Send a notification to R3 if u32TasksNew was 0 before our write. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync if (ASMAtomicReadBool(&pAhciPort->fWrkThreadSleeping))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciIoThreadKick(ahci, pAhciPort);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync }
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regCI |= u32Value;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int PortCmdIssue_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t uCIValue = 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uCIValue = ASMAtomicXchgU32(&pAhciPort->u32TasksFinished, 0);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ahciLog(("%s: read regCI=%#010x uCIValue=%#010x\n", __FUNCTION__, pAhciPort->regCI, uCIValue));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciPort->regCI &= ~uCIValue;
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync *pu32Value = pAhciPort->regCI;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int PortSActive_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regSACT |= u32Value;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int PortSActive_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t u32TasksFinished = ASMAtomicXchgU32(&pAhciPort->u32QueuedTasksFinished, 0);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regSACT &= ~u32TasksFinished;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: read regSACT=%#010x regCI=%#010x u32TasksFinished=%#010x\n",
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync __FUNCTION__, pAhciPort->regSACT, pAhciPort->regCI, u32TasksFinished));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pu32Value = pAhciPort->regSACT;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsyncstatic int PortSError_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync if ( (u32Value & AHCI_PORT_SERR_X)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync && (pAhciPort->regSERR & AHCI_PORT_SERR_X))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ASMAtomicAndU32(&pAhciPort->regIS, ~AHCI_PORT_IS_PCS);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regTFD |= ATA_STAT_ERR;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regTFD &= ~(ATA_STAT_DRQ | ATA_STAT_BUSY);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync }
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync if ( (u32Value & AHCI_PORT_SERR_N)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync && (pAhciPort->regSERR & AHCI_PORT_SERR_N))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ASMAtomicAndU32(&pAhciPort->regIS, ~AHCI_PORT_IS_PRCS);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regSERR &= ~u32Value;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsyncstatic int PortSError_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: read regSERR=%#010x\n", __FUNCTION__, pAhciPort->regSERR));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pu32Value = pAhciPort->regSERR;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsyncstatic int PortSControl_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: IPM=%d SPD=%d DET=%d\n", __FUNCTION__,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync AHCI_PORT_SCTL_IPM_GET(u32Value), AHCI_PORT_SCTL_SPD_GET(u32Value), AHCI_PORT_SCTL_DET_GET(u32Value)));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#ifndef IN_RING3
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_IOM_R3_MMIO_WRITE;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#else
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync if ((u32Value & AHCI_PORT_SCTL_DET) == AHCI_PORT_SCTL_DET_INIT)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync if (!ASMAtomicXchgBool(&pAhciPort->fPortReset, true))
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync LogRel(("AHCI#%u: Port %d reset\n", ahci->CTX_SUFF(pDevIns)->iInstance,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->iLUN));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regSSTS = 0;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regSIG = ~0;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regTFD = 0x7f;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->fFirstD2HFisSend = false;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regSCTL = u32Value;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync }
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync else if ( (u32Value & AHCI_PORT_SCTL_DET) == AHCI_PORT_SCTL_DET_NINIT
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync && (pAhciPort->regSCTL & AHCI_PORT_SCTL_DET) == AHCI_PORT_SCTL_DET_INIT
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync && pAhciPort->pDrvBase)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /* Do the port reset here, so the guest sees the new status immediately. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync if (ahci->fLegacyPortResetMethod)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciPortResetFinish(pAhciPort);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regSCTL = u32Value; /* Update after finishing the reset, so the I/O thread doesn't get a chance to do the reset. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync }
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync else
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regSSTS = 0x1; /* Indicate device presence detected but communication not established. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regSCTL = u32Value; /* Update before kicking the I/O thread. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /* Kick the thread to finish the reset. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciIoThreadKick(ahci, pAhciPort);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync }
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync }
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#endif
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsyncstatic int PortSControl_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: read regSCTL=%#010x\n", __FUNCTION__, pAhciPort->regSCTL));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: IPM=%d SPD=%d DET=%d\n", __FUNCTION__,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync AHCI_PORT_SCTL_IPM_GET(pAhciPort->regSCTL), AHCI_PORT_SCTL_SPD_GET(pAhciPort->regSCTL),
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync AHCI_PORT_SCTL_DET_GET(pAhciPort->regSCTL)));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pu32Value = pAhciPort->regSCTL;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsyncstatic int PortSStatus_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: read regSSTS=%#010x\n", __FUNCTION__, pAhciPort->regSSTS));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: IPM=%d SPD=%d DET=%d\n", __FUNCTION__,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync AHCI_PORT_SSTS_IPM_GET(pAhciPort->regSSTS), AHCI_PORT_SSTS_SPD_GET(pAhciPort->regSSTS),
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync AHCI_PORT_SSTS_DET_GET(pAhciPort->regSSTS)));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pu32Value = pAhciPort->regSSTS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsyncstatic int PortSignature_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: read regSIG=%#010x\n", __FUNCTION__, pAhciPort->regSIG));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pu32Value = pAhciPort->regSIG;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsyncstatic int PortTaskFileData_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: read regTFD=%#010x\n", __FUNCTION__, pAhciPort->regTFD));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: ERR=%x BSY=%d DRQ=%d ERR=%d\n", __FUNCTION__,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regTFD >> 8), (pAhciPort->regTFD & AHCI_PORT_TFD_BSY) >> 7,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regTFD & AHCI_PORT_TFD_DRQ) >> 3, (pAhciPort->regTFD & AHCI_PORT_TFD_ERR)));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pu32Value = pAhciPort->regTFD;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync/**
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync * Read from the port command register.
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsyncstatic int PortCmd_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: read regCMD=%#010x\n", __FUNCTION__, pAhciPort->regCMD | AHCI_PORT_CMD_CCS_SHIFT(pAhciPort->u32CurrentCommandSlot)));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciLog(("%s: ICC=%d ASP=%d ALPE=%d DLAE=%d ATAPI=%d CPD=%d ISP=%d HPCP=%d PMA=%d CPS=%d CR=%d FR=%d ISS=%d CCS=%d FRE=%d CLO=%d POD=%d SUD=%d ST=%d\n",
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync __FUNCTION__, (pAhciPort->regCMD & AHCI_PORT_CMD_ICC) >> 28, (pAhciPort->regCMD & AHCI_PORT_CMD_ASP) >> 27,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regCMD & AHCI_PORT_CMD_ALPE) >> 26, (pAhciPort->regCMD & AHCI_PORT_CMD_DLAE) >> 25,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regCMD & AHCI_PORT_CMD_ATAPI) >> 24, (pAhciPort->regCMD & AHCI_PORT_CMD_CPD) >> 20,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regCMD & AHCI_PORT_CMD_ISP) >> 19, (pAhciPort->regCMD & AHCI_PORT_CMD_HPCP) >> 18,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regCMD & AHCI_PORT_CMD_PMA) >> 17, (pAhciPort->regCMD & AHCI_PORT_CMD_CPS) >> 16,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regCMD & AHCI_PORT_CMD_CR) >> 15, (pAhciPort->regCMD & AHCI_PORT_CMD_FR) >> 14,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regCMD & AHCI_PORT_CMD_ISS) >> 13, pAhciPort->u32CurrentCommandSlot,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regCMD & AHCI_PORT_CMD_FRE) >> 4, (pAhciPort->regCMD & AHCI_PORT_CMD_CLO) >> 3,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regCMD & AHCI_PORT_CMD_POD) >> 2, (pAhciPort->regCMD & AHCI_PORT_CMD_SUD) >> 1,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync (pAhciPort->regCMD & AHCI_PORT_CMD_ST)));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pu32Value = pAhciPort->regCMD | AHCI_PORT_CMD_CCS_SHIFT(pAhciPort->u32CurrentCommandSlot);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync/**
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Write to the port command register.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * This is the register where all the data transfer is started
5b0a093ca572a855886faa6747ad46df859dd041vboxsync */
5b0a093ca572a855886faa6747ad46df859dd041vboxsyncstatic int PortCmd_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync{
5b0a093ca572a855886faa6747ad46df859dd041vboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync ahciLog(("%s: ICC=%d ASP=%d ALPE=%d DLAE=%d ATAPI=%d CPD=%d ISP=%d HPCP=%d PMA=%d CPS=%d CR=%d FR=%d ISS=%d CCS=%d FRE=%d CLO=%d POD=%d SUD=%d ST=%d\n",
5b0a093ca572a855886faa6747ad46df859dd041vboxsync __FUNCTION__, (u32Value & AHCI_PORT_CMD_ICC) >> 28, (u32Value & AHCI_PORT_CMD_ASP) >> 27,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync (u32Value & AHCI_PORT_CMD_ALPE) >> 26, (u32Value & AHCI_PORT_CMD_DLAE) >> 25,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync (u32Value & AHCI_PORT_CMD_ATAPI) >> 24, (u32Value & AHCI_PORT_CMD_CPD) >> 20,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync (u32Value & AHCI_PORT_CMD_ISP) >> 19, (u32Value & AHCI_PORT_CMD_HPCP) >> 18,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync (u32Value & AHCI_PORT_CMD_PMA) >> 17, (u32Value & AHCI_PORT_CMD_CPS) >> 16,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync (u32Value & AHCI_PORT_CMD_CR) >> 15, (u32Value & AHCI_PORT_CMD_FR) >> 14,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync (u32Value & AHCI_PORT_CMD_ISS) >> 13, (u32Value & AHCI_PORT_CMD_CCS) >> 8,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync (u32Value & AHCI_PORT_CMD_FRE) >> 4, (u32Value & AHCI_PORT_CMD_CLO) >> 3,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync (u32Value & AHCI_PORT_CMD_POD) >> 2, (u32Value & AHCI_PORT_CMD_SUD) >> 1,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync (u32Value & AHCI_PORT_CMD_ST)));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /* The PxCMD.CCS bits are R/O and maintained separately. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync u32Value &= ~AHCI_PORT_CMD_CCS;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync if (pAhciPort->fPoweredOn && pAhciPort->fSpunUp)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync {
5b0a093ca572a855886faa6747ad46df859dd041vboxsync if (u32Value & AHCI_PORT_CMD_CLO)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync {
5b0a093ca572a855886faa6747ad46df859dd041vboxsync ahciLog(("%s: Command list override requested\n", __FUNCTION__));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync u32Value &= ~(AHCI_PORT_TFD_BSY | AHCI_PORT_TFD_DRQ);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /* Clear the CLO bit. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync u32Value &= ~(AHCI_PORT_CMD_CLO);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync }
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync if (u32Value & AHCI_PORT_CMD_ST)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync {
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /*
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * Set engine state to running if there is a device attached and
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * IS.PCS is clear.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync if ( pAhciPort->pDrvBase
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync && !(pAhciPort->regIS & AHCI_PORT_IS_PCS))
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync {
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync ahciLog(("%s: Engine starts\n", __FUNCTION__));
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync u32Value |= AHCI_PORT_CMD_CR;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /* If there is something in CI, kick the I/O thread. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync if ( pAhciPort->regCI > 0
5b0a093ca572a855886faa6747ad46df859dd041vboxsync && ASMAtomicReadBool(&pAhciPort->fWrkThreadSleeping))
70aa086e9e9d2f85d2e997d0e69169018a001e54vboxsync {
5b0a093ca572a855886faa6747ad46df859dd041vboxsync ASMAtomicOrU32(&pAhciPort->u32TasksNew, pAhciPort->regCI);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync#ifdef IN_RC
5b0a093ca572a855886faa6747ad46df859dd041vboxsync PDEVPORTNOTIFIERQUEUEITEM pItem = (PDEVPORTNOTIFIERQUEUEITEM)PDMQueueAlloc(ahci->CTX_SUFF(pNotifierQueue));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync AssertMsg(VALID_PTR(pItem), ("Allocating item for queue failed\n"));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pItem->iPort = pAhciPort->iLUN;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync PDMQueueInsert(ahci->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync#else
5b0a093ca572a855886faa6747ad46df859dd041vboxsync LogFlowFunc(("Signal event semaphore\n"));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync int rc = SUPSemEventSignal(ahci->pSupDrvSession, pAhciPort->hEvtProcess);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync AssertRC(rc);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync#endif
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync }
d3dea25ec07f6546715fe3af943ea863294b392evboxsync }
d3dea25ec07f6546715fe3af943ea863294b392evboxsync else
5b0a093ca572a855886faa6747ad46df859dd041vboxsync u32Value &= ~AHCI_PORT_CMD_CR;
70aa086e9e9d2f85d2e997d0e69169018a001e54vboxsync }
5b0a093ca572a855886faa6747ad46df859dd041vboxsync else
5b0a093ca572a855886faa6747ad46df859dd041vboxsync {
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync ahciLog(("%s: Engine stops\n", __FUNCTION__));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /* Clear command issue register. */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync pAhciPort->regCI = 0;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->regSACT = 0;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /* Clear current command slot. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->u32CurrentCommandSlot = 0;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync u32Value &= ~AHCI_PORT_CMD_CR;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync else if (pAhciPort->pDrvBase)
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync {
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync if ((u32Value & AHCI_PORT_CMD_POD) && (pAhciPort->regCMD & AHCI_PORT_CMD_CPS) && !pAhciPort->fPoweredOn)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: Power on the device\n", __FUNCTION__));
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync pAhciPort->fPoweredOn = true;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync /*
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync * Set states in the Port Signature and SStatus registers.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync if (pAhciPort->fATAPI)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->regSIG = AHCI_PORT_SIG_ATAPI;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync else
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync pAhciPort->regSIG = AHCI_PORT_SIG_DISK;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->regSSTS = (0x01 << 8) | /* Interface is active. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync (0x02 << 4) | /* Generation 2 (3.0GBps) speed. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync (0x03 << 0); /* Device detected and communication established. */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync if (pAhciPort->regCMD & AHCI_PORT_CMD_FRE)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync#ifndef IN_RING3
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_IOM_R3_MMIO_WRITE;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#else
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ahciPostFirstD2HFisIntoMemory(pAhciPort);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_DHRS);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync if (pAhciPort->regIE & AHCI_PORT_IE_DHRE)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync int rc = ahciHbaSetInterrupt(pAhciPort->CTX_SUFF(pAhci), pAhciPort->iLUN, VERR_IGNORED);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync AssertRC(rc);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync }
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync#endif
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync }
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync }
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync if ((u32Value & AHCI_PORT_CMD_SUD) && pAhciPort->fPoweredOn && !pAhciPort->fSpunUp)
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: Spin up the device\n", __FUNCTION__));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync pAhciPort->fSpunUp = true;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if (u32Value & AHCI_PORT_CMD_FRE)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: FIS receive enabled\n", __FUNCTION__));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync u32Value |= AHCI_PORT_CMD_FR;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* Send the first D2H FIS only if it wasn't already send. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if ( !pAhciPort->fFirstD2HFisSend
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync && pAhciPort->pDrvBase)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync#ifndef IN_RING3
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_IOM_R3_MMIO_WRITE;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#else
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync ahciPostFirstD2HFisIntoMemory(pAhciPort);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->fFirstD2HFisSend = true;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync#endif
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync }
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync else if (!(u32Value & AHCI_PORT_CMD_FRE))
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: FIS receive disabled\n", __FUNCTION__));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync u32Value &= ~AHCI_PORT_CMD_FR;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync pAhciPort->regCMD = u32Value;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync return VINF_SUCCESS;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync/**
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Read from the port interrupt enable register.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int PortIntrEnable_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: read regIE=%#010x\n", __FUNCTION__, pAhciPort->regIE));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: CPDE=%d TFEE=%d HBFE=%d HBDE=%d IFE=%d INFE=%d OFE=%d IPME=%d PRCE=%d DIE=%d PCE=%d DPE=%d UFE=%d SDBE=%d DSE=%d PSE=%d DHRE=%d\n",
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync __FUNCTION__, (pAhciPort->regIE & AHCI_PORT_IE_CPDE) >> 31, (pAhciPort->regIE & AHCI_PORT_IE_TFEE) >> 30,
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync (pAhciPort->regIE & AHCI_PORT_IE_HBFE) >> 29, (pAhciPort->regIE & AHCI_PORT_IE_HBDE) >> 28,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync (pAhciPort->regIE & AHCI_PORT_IE_IFE) >> 27, (pAhciPort->regIE & AHCI_PORT_IE_INFE) >> 26,
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync (pAhciPort->regIE & AHCI_PORT_IE_OFE) >> 24, (pAhciPort->regIE & AHCI_PORT_IE_IPME) >> 23,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync (pAhciPort->regIE & AHCI_PORT_IE_PRCE) >> 22, (pAhciPort->regIE & AHCI_PORT_IE_DIE) >> 7,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync (pAhciPort->regIE & AHCI_PORT_IE_PCE) >> 6, (pAhciPort->regIE & AHCI_PORT_IE_DPE) >> 5,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync (pAhciPort->regIE & AHCI_PORT_IE_UFE) >> 4, (pAhciPort->regIE & AHCI_PORT_IE_SDBE) >> 3,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync (pAhciPort->regIE & AHCI_PORT_IE_DSE) >> 2, (pAhciPort->regIE & AHCI_PORT_IE_PSE) >> 1,
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync (pAhciPort->regIE & AHCI_PORT_IE_DHRE)));
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync *pu32Value = pAhciPort->regIE;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Write to the port interrupt enable register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsyncstatic int PortIntrEnable_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync{
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync int rc = VINF_SUCCESS;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync ahciLog(("%s: CPDE=%d TFEE=%d HBFE=%d HBDE=%d IFE=%d INFE=%d OFE=%d IPME=%d PRCE=%d DIE=%d PCE=%d DPE=%d UFE=%d SDBE=%d DSE=%d PSE=%d DHRE=%d\n",
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync __FUNCTION__, (u32Value & AHCI_PORT_IE_CPDE) >> 31, (u32Value & AHCI_PORT_IE_TFEE) >> 30,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (u32Value & AHCI_PORT_IE_HBFE) >> 29, (u32Value & AHCI_PORT_IE_HBDE) >> 28,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (u32Value & AHCI_PORT_IE_IFE) >> 27, (u32Value & AHCI_PORT_IE_INFE) >> 26,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (u32Value & AHCI_PORT_IE_OFE) >> 24, (u32Value & AHCI_PORT_IE_IPME) >> 23,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (u32Value & AHCI_PORT_IE_PRCE) >> 22, (u32Value & AHCI_PORT_IE_DIE) >> 7,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (u32Value & AHCI_PORT_IE_PCE) >> 6, (u32Value & AHCI_PORT_IE_DPE) >> 5,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (u32Value & AHCI_PORT_IE_UFE) >> 4, (u32Value & AHCI_PORT_IE_SDBE) >> 3,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (u32Value & AHCI_PORT_IE_DSE) >> 2, (u32Value & AHCI_PORT_IE_PSE) >> 1,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync (u32Value & AHCI_PORT_IE_DHRE)));
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync u32Value &= AHCI_PORT_IE_READONLY;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /* Check if some a interrupt status bit changed*/
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync uint32_t u32IntrStatus = ASMAtomicReadU32(&pAhciPort->regIS);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (u32Value & u32IntrStatus)
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync rc = ahciHbaSetInterrupt(ahci, pAhciPort->iLUN, VINF_IOM_R3_MMIO_WRITE);
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync if (rc == VINF_SUCCESS)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->regIE = u32Value;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync/**
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync * Read from the port interrupt status register.
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsyncstatic int PortIntrSts_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync{
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: read regIS=%#010x\n", __FUNCTION__, pAhciPort->regIS));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: CPDS=%d TFES=%d HBFS=%d HBDS=%d IFS=%d INFS=%d OFS=%d IPMS=%d PRCS=%d DIS=%d PCS=%d DPS=%d UFS=%d SDBS=%d DSS=%d PSS=%d DHRS=%d\n",
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync __FUNCTION__, (pAhciPort->regIS & AHCI_PORT_IS_CPDS) >> 31, (pAhciPort->regIS & AHCI_PORT_IS_TFES) >> 30,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (pAhciPort->regIS & AHCI_PORT_IS_HBFS) >> 29, (pAhciPort->regIS & AHCI_PORT_IS_HBDS) >> 28,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (pAhciPort->regIS & AHCI_PORT_IS_IFS) >> 27, (pAhciPort->regIS & AHCI_PORT_IS_INFS) >> 26,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (pAhciPort->regIS & AHCI_PORT_IS_OFS) >> 24, (pAhciPort->regIS & AHCI_PORT_IS_IPMS) >> 23,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (pAhciPort->regIS & AHCI_PORT_IS_PRCS) >> 22, (pAhciPort->regIS & AHCI_PORT_IS_DIS) >> 7,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync (pAhciPort->regIS & AHCI_PORT_IS_PCS) >> 6, (pAhciPort->regIS & AHCI_PORT_IS_DPS) >> 5,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync (pAhciPort->regIS & AHCI_PORT_IS_UFS) >> 4, (pAhciPort->regIS & AHCI_PORT_IS_SDBS) >> 3,
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync (pAhciPort->regIS & AHCI_PORT_IS_DSS) >> 2, (pAhciPort->regIS & AHCI_PORT_IS_PSS) >> 1,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync (pAhciPort->regIS & AHCI_PORT_IS_DHRS)));
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync *pu32Value = pAhciPort->regIS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Write to the port interrupt status register.
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsyncstatic int PortIntrSts_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ASMAtomicAndU32(&pAhciPort->regIS, ~(u32Value & AHCI_PORT_IS_READONLY));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync/**
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync * Read from the port FIS base address upper 32bit register.
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsyncstatic int PortFisAddrUp_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: read regFBU=%#010x\n", __FUNCTION__, pAhciPort->regFBU));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *pu32Value = pAhciPort->regFBU;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Write to the port FIS base address upper 32bit register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsyncstatic int PortFisAddrUp_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->regFBU = u32Value;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->GCPhysAddrFb = AHCI_RTGCPHYS_FROM_U32(pAhciPort->regFBU, pAhciPort->regFB);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Read from the port FIS base address register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int PortFisAddr_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: read regFB=%#010x\n", __FUNCTION__, pAhciPort->regFB));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *pu32Value = pAhciPort->regFB;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Write to the port FIS base address register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int PortFisAddr_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Assert(!(u32Value & ~AHCI_PORT_FB_RESERVED));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->regFB = (u32Value & AHCI_PORT_FB_RESERVED);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->GCPhysAddrFb = AHCI_RTGCPHYS_FROM_U32(pAhciPort->regFBU, pAhciPort->regFB);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Write to the port command list base address upper 32bit register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int PortCmdLstAddrUp_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->regCLBU = u32Value;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->GCPhysAddrClb = AHCI_RTGCPHYS_FROM_U32(pAhciPort->regCLBU, pAhciPort->regCLB);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Read from the port command list base address upper 32bit register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int PortCmdLstAddrUp_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync{
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync ahciLog(("%s: read regCLBU=%#010x\n", __FUNCTION__, pAhciPort->regCLBU));
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync *pu32Value = pAhciPort->regCLBU;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync/**
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Read from the port command list base address register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int PortCmdLstAddr_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: read regCLB=%#010x\n", __FUNCTION__, pAhciPort->regCLB));
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync *pu32Value = pAhciPort->regCLB;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Write to the port command list base address register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int PortCmdLstAddr_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Assert(!(u32Value & ~AHCI_PORT_CLB_RESERVED));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->regCLB = (u32Value & AHCI_PORT_CLB_RESERVED);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->GCPhysAddrClb = AHCI_RTGCPHYS_FROM_U32(pAhciPort->regCLBU, pAhciPort->regCLB);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Read from the global Version register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int HbaVersion_r(PAHCI ahci, uint32_t iReg, uint32_t *pu32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s: read regHbaVs=%#010x\n", __FUNCTION__, ahci->regHbaVs));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *pu32Value = ahci->regHbaVs;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Read from the global Ports implemented register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int HbaPortsImplemented_r(PAHCI ahci, uint32_t iReg, uint32_t *pu32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s: read regHbaPi=%#010x\n", __FUNCTION__, ahci->regHbaPi));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *pu32Value = ahci->regHbaPi;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Write to the global interrupt status register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int HbaInterruptStatus_w(PAHCI ahci, uint32_t iReg, uint32_t u32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync int rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync rc = PDMCritSectEnter(&ahci->lock, VINF_IOM_R3_MMIO_WRITE);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (rc != VINF_SUCCESS)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (u32Value > 0)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /*
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * Clear the interrupt only if no port has signalled
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * an interrupt and the guest has cleared all set interrupt
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * notification bits.
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync bool fClear = true;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahci->regHbaIs &= ~(u32Value);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync fClear = !ahci->u32PortsInterrupted && !ahci->regHbaIs;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if (fClear)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync unsigned i = 0;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /* Check if the cleared ports have a interrupt status bit set. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync while ((u32Value > 0) && (i < AHCI_MAX_NR_PORTS_IMPL))
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (u32Value & 0x01)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PAHCIPort pAhciPort = &ahci->ahciPort[i];
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (pAhciPort->regIE & pAhciPort->regIS)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s: Interrupt status of port %u set -> Set interrupt again\n", __FUNCTION__, i));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ASMAtomicOrU32(&ahci->u32PortsInterrupted, 1 << i);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync fClear = false;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync break;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync u32Value = u32Value >> 1;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync i++;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (fClear)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciHbaClearInterrupt(ahci);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync else
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s: Not clearing interrupt: u32PortsInterrupted=%#010x\n", __FUNCTION__, ahci->u32PortsInterrupted));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /*
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * We need to set the interrupt again because the I/O APIC does not set it again even if the
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * line is still high.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * We need to clear it first because the PCI bus only calls the interrupt controller if the state changes.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync PDMDevHlpPCISetIrq(ahci->CTX_SUFF(pDevIns), 0, 0);
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync PDMDevHlpPCISetIrq(ahci->CTX_SUFF(pDevIns), 0, 1);
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMCritSectLeave(&ahci->lock);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync * Read from the global interrupt status register.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsyncstatic int HbaInterruptStatus_r(PAHCI ahci, uint32_t iReg, uint32_t *pu32Value)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync uint32_t u32PortsInterrupted;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync int rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync rc = PDMCritSectEnter(&ahci->lock, VINF_IOM_R3_MMIO_READ);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (rc != VINF_SUCCESS)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync u32PortsInterrupted = ASMAtomicXchgU32(&ahci->u32PortsInterrupted, 0);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PDMCritSectLeave(&ahci->lock);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s: read regHbaIs=%#010x u32PortsInterrupted=%#010x\n", __FUNCTION__, ahci->regHbaIs, u32PortsInterrupted));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahci->regHbaIs |= u32PortsInterrupted;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#ifdef LOG_ENABLED
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s:", __FUNCTION__));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync unsigned i;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync for (i = 0; i < ahci->cPortsImpl; i++)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if ((ahci->regHbaIs >> i) & 0x01)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log((" P%d", i));
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync }
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync Log(("\n"));
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#endif
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *pu32Value = ahci->regHbaIs;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync return VINF_SUCCESS;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync}
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/**
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * Write to the global control register.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsyncstatic int HbaControl_w(PAHCI ahci, uint32_t iReg, uint32_t u32Value)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync{
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync Log(("%s: write u32Value=%#010x\n"
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync "%s: AE=%d IE=%d HR=%d\n",
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync __FUNCTION__, u32Value,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync __FUNCTION__, (u32Value & AHCI_HBA_CTRL_AE) >> 31, (u32Value & AHCI_HBA_CTRL_IE) >> 1,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync (u32Value & AHCI_HBA_CTRL_HR)));
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#ifndef IN_RING3
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync return VINF_IOM_R3_MMIO_WRITE;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync#else
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /*
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Increase the active thread counter because we might set the host controller
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * reset bit.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ASMAtomicIncU32(&ahci->cThreadsActive);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ASMAtomicWriteU32(&ahci->regHbaCtrl, (u32Value & AHCI_HBA_CTRL_RW_MASK) | AHCI_HBA_CTRL_AE);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /*
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Do the HBA reset if requested and there is no other active thread at the moment,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * the work is deferred to the last active thread otherwise.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync uint32_t cThreadsActive = ASMAtomicDecU32(&ahci->cThreadsActive);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if ( (u32Value & AHCI_HBA_CTRL_HR)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync && !cThreadsActive)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciHBAReset(ahci);
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync#endif
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Read the global control register.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int HbaControl_r(PAHCI ahci, uint32_t iReg, uint32_t *pu32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s: read regHbaCtrl=%#010x\n"
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync "%s: AE=%d IE=%d HR=%d\n",
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync __FUNCTION__, ahci->regHbaCtrl,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync __FUNCTION__, (ahci->regHbaCtrl & AHCI_HBA_CTRL_AE) >> 31, (ahci->regHbaCtrl & AHCI_HBA_CTRL_IE) >> 1,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync (ahci->regHbaCtrl & AHCI_HBA_CTRL_HR)));
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *pu32Value = ahci->regHbaCtrl;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync return VINF_SUCCESS;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync}
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/**
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * Read the global capabilities register.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsyncstatic int HbaCapabilities_r(PAHCI ahci, uint32_t iReg, uint32_t *pu32Value)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync{
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync Log(("%s: read regHbaCap=%#010x\n"
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync "%s: S64A=%d SNCQ=%d SIS=%d SSS=%d SALP=%d SAL=%d SCLO=%d ISS=%d SNZO=%d SAM=%d SPM=%d PMD=%d SSC=%d PSC=%d NCS=%d NP=%d\n",
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync __FUNCTION__, ahci->regHbaCap,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync __FUNCTION__, (ahci->regHbaCap & AHCI_HBA_CAP_S64A) >> 31, (ahci->regHbaCap & AHCI_HBA_CAP_SNCQ) >> 30,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync (ahci->regHbaCap & AHCI_HBA_CAP_SIS) >> 28, (ahci->regHbaCap & AHCI_HBA_CAP_SSS) >> 27,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync (ahci->regHbaCap & AHCI_HBA_CAP_SALP) >> 26, (ahci->regHbaCap & AHCI_HBA_CAP_SAL) >> 25,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync (ahci->regHbaCap & AHCI_HBA_CAP_SCLO) >> 24, (ahci->regHbaCap & AHCI_HBA_CAP_ISS) >> 20,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync (ahci->regHbaCap & AHCI_HBA_CAP_SNZO) >> 19, (ahci->regHbaCap & AHCI_HBA_CAP_SAM) >> 18,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync (ahci->regHbaCap & AHCI_HBA_CAP_SPM) >> 17, (ahci->regHbaCap & AHCI_HBA_CAP_PMD) >> 15,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync (ahci->regHbaCap & AHCI_HBA_CAP_SSC) >> 14, (ahci->regHbaCap & AHCI_HBA_CAP_PSC) >> 13,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync (ahci->regHbaCap & AHCI_HBA_CAP_NCS) >> 8, (ahci->regHbaCap & AHCI_HBA_CAP_NP)));
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *pu32Value = ahci->regHbaCap;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync return VINF_SUCCESS;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync}
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/**
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * Write to the global command completion coalescing control register.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic int HbaCccCtl_w(PAHCI ahci, uint32_t iReg, uint32_t u32Value)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Log(("%s: write u32Value=%#010x\n"
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync "%s: TV=%d CC=%d INT=%d EN=%d\n",
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync __FUNCTION__, u32Value,
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync __FUNCTION__, AHCI_HBA_CCC_CTL_TV_GET(u32Value), AHCI_HBA_CCC_CTL_CC_GET(u32Value),
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync AHCI_HBA_CCC_CTL_INT_GET(u32Value), (u32Value & AHCI_HBA_CCC_CTL_EN)));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ahci->regHbaCccCtl = u32Value;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ahci->uCccTimeout = AHCI_HBA_CCC_CTL_TV_GET(u32Value);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ahci->uCccPortNr = AHCI_HBA_CCC_CTL_INT_GET(u32Value);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ahci->uCccNr = AHCI_HBA_CCC_CTL_CC_GET(u32Value);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (u32Value & AHCI_HBA_CCC_CTL_EN)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Arm the timer */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync TMTimerSetMillies(ahci->CTX_SUFF(pHbaCccTimer), ahci->uCccTimeout);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync else
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync TMTimerStop(ahci->CTX_SUFF(pHbaCccTimer));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync/**
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Read the global command completion coalescing control register.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int HbaCccCtl_r(PAHCI ahci, uint32_t iReg, uint32_t *pu32Value)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s: read regHbaCccCtl=%#010x\n"
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync "%s: TV=%d CC=%d INT=%d EN=%d\n",
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync __FUNCTION__, ahci->regHbaCccCtl,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync __FUNCTION__, AHCI_HBA_CCC_CTL_TV_GET(ahci->regHbaCccCtl), AHCI_HBA_CCC_CTL_CC_GET(ahci->regHbaCccCtl),
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync AHCI_HBA_CCC_CTL_INT_GET(ahci->regHbaCccCtl), (ahci->regHbaCccCtl & AHCI_HBA_CCC_CTL_EN)));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *pu32Value = ahci->regHbaCccCtl;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync/**
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Write to the global command completion coalescing ports register.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int HbaCccPorts_w(PAHCI ahci, uint32_t iReg, uint32_t u32Value)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s: write u32Value=%#010x\n", __FUNCTION__, u32Value));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahci->regHbaCccPorts = u32Value;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync/**
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Read the global command completion coalescing ports register.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int HbaCccPorts_r(PAHCI ahci, uint32_t iReg, uint32_t *pu32Value)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log(("%s: read regHbaCccPorts=%#010x\n", __FUNCTION__, ahci->regHbaCccPorts));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#ifdef LOG_ENABLED
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s:", __FUNCTION__));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync unsigned i;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync for (i = 0; i < ahci->cPortsImpl; i++)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if ((ahci->regHbaCccPorts >> i) & 0x01)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log((" P%d", i));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log(("\n"));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync#endif
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync *pu32Value = ahci->regHbaCccPorts;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Invalid write to global register
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic int HbaInvalid_w(PAHCI ahci, uint32_t iReg, uint32_t u32Value)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log(("%s: Write denied!!! iReg=%u u32Value=%#010x\n", __FUNCTION__, iReg, u32Value));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync/**
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Invalid Port write.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int PortInvalid_w(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t u32Value)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: Write denied!!! iReg=%u u32Value=%#010x\n", __FUNCTION__, iReg, u32Value));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync/**
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Invalid Port read.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int PortInvalid_r(PAHCI ahci, PAHCIPort pAhciPort, uint32_t iReg, uint32_t *pu32Value)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: Read denied!!! iReg=%u\n", __FUNCTION__, iReg));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync/**
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Register descriptor table for global HBA registers
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic const AHCIOPREG g_aOpRegs[] =
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"HbaCapabilites", HbaCapabilities_r, HbaInvalid_w}, /* Readonly */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"HbaControl" , HbaControl_r, HbaControl_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"HbaInterruptStatus", HbaInterruptStatus_r, HbaInterruptStatus_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"HbaPortsImplemented", HbaPortsImplemented_r, HbaInvalid_w}, /* Readonly */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"HbaVersion", HbaVersion_r, HbaInvalid_w}, /* ReadOnly */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"HbaCccCtl", HbaCccCtl_r, HbaCccCtl_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"HbaCccPorts", HbaCccPorts_r, HbaCccPorts_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync};
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Register descriptor table for port registers
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic const AHCIPORTOPREG g_aPortOpRegs[] =
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortCmdLstAddr", PortCmdLstAddr_r, PortCmdLstAddr_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortCmdLstAddrUp", PortCmdLstAddrUp_r, PortCmdLstAddrUp_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortFisAddr", PortFisAddr_r, PortFisAddr_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortFisAddrUp", PortFisAddrUp_r, PortFisAddrUp_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortIntrSts", PortIntrSts_r, PortIntrSts_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortIntrEnable", PortIntrEnable_r, PortIntrEnable_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortCmd", PortCmd_r, PortCmd_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortReserved1", PortInvalid_r, PortInvalid_w}, /* Not used. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortTaskFileData", PortTaskFileData_r, PortInvalid_w}, /* Readonly */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortSignature", PortSignature_r, PortInvalid_w}, /* Readonly */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortSStatus", PortSStatus_r, PortInvalid_w}, /* Readonly */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortSControl", PortSControl_r, PortSControl_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortSError", PortSError_r, PortSError_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortSActive", PortSActive_r, PortSActive_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortCmdIssue", PortCmdIssue_r, PortCmdIssue_w},
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {"PortReserved2", PortInvalid_r, PortInvalid_w}, /* Not used. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync};
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#ifdef IN_RING3
ad66a27959d7085aa31760f63ce082943be60e89vboxsync/**
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * Reset initiated by system software for one port.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync *
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @param pAhciPort The port to reset.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync */
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncstatic void ahciPortSwReset(PAHCIPort pAhciPort)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync bool fAllTasksCanceled;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /* Cancel all tasks first. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync fAllTasksCanceled = ahciCancelActiveTasks(pAhciPort);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync Assert(fAllTasksCanceled);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync pAhciPort->regIS = 0;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pAhciPort->regIE = 0;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pAhciPort->regCMD = AHCI_PORT_CMD_CPD | /* Cold presence detection */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync AHCI_PORT_CMD_SUD | /* Device has spun up. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync AHCI_PORT_CMD_POD; /* Port is powered on. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /* Hotplugging supported?. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pAhciPort->fHotpluggable)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regCMD |= AHCI_PORT_CMD_HPCP;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regTFD = (1 << 8) | ATA_STAT_SEEK | ATA_STAT_WRERR;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regSIG = ~0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regSSTS = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regSCTL = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regSERR = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regSACT = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regCI = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->fResetDevice = false;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->fPoweredOn = true;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->fSpunUp = true;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync pAhciPort->cMultSectors = ATA_MAX_MULT_SECTORS;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->uATATransferMode = ATA_MODE_UDMA | 6;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->u32TasksNew = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->u32TasksRedo = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->u32TasksFinished = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->u32QueuedTasksFinished = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->u32CurrentCommandSlot = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->cTasksActive = 0;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ASMAtomicWriteU32(&pAhciPort->MediaEventStatus, ATA_EVENT_STATUS_UNCHANGED);
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ASMAtomicWriteU32(&pAhciPort->MediaTrackType, ATA_MEDIA_TYPE_UNKNOWN);
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync if (pAhciPort->pDrvBase)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync {
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regCMD |= AHCI_PORT_CMD_CPS; /* Indicate that there is a device on that port */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync if (pAhciPort->fPoweredOn)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync {
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync /*
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * Set states in the Port Signature and SStatus registers.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (pAhciPort->fATAPI)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regSIG = AHCI_PORT_SIG_ATAPI;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync else
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regSIG = AHCI_PORT_SIG_DISK;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhciPort->regSSTS = (0x01 << 8) | /* Interface is active. */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync (0x02 << 4) | /* Generation 2 (3.0GBps) speed. */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync (0x03 << 0); /* Device detected and communication established. */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync }
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync }
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync}
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync/**
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * Hardware reset used for machine power on and reset.
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync *
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * @param pAhciport The port to reset.
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsyncstatic void ahciPortHwReset(PAHCIPort pAhciPort)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync{
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync /* Reset the address registers. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync pAhciPort->regCLB = 0;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync pAhciPort->regCLBU = 0;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync pAhciPort->regFB = 0;
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync pAhciPort->regFBU = 0;
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* Reset calculated addresses. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync pAhciPort->GCPhysAddrClb = 0;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhciPort->GCPhysAddrFb = 0;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Create implemented ports bitmap.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @returns 32bit bitmask with a bit set for every implemented port.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @param cPorts Number of ports.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic uint32_t ahciGetPortsImplemented(unsigned cPorts)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uint32_t uPortsImplemented = 0;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync for (unsigned i = 0; i < cPorts; i++)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync uPortsImplemented |= (1 << i);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return uPortsImplemented;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Reset the entire HBA.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync *
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @param pThis The HBA state.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic void ahciHBAReset(PAHCI pThis)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync{
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync unsigned i;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync int rc = VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync LogRel(("AHCI#%u: Reset the HBA\n", pThis->CTX_SUFF(pDevIns)->iInstance));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync /* Stop the CCC timer. */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync if (pThis->regHbaCccCtl & AHCI_HBA_CCC_CTL_EN)
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync {
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync rc = TMTimerStop(pThis->CTX_SUFF(pHbaCccTimer));
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync if (RT_FAILURE(rc))
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync AssertMsgFailed(("%s: Failed to stop timer!\n", __FUNCTION__));
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync }
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* Reset every port */
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync for (i = 0; i < pThis->cPortsImpl; i++)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync PAHCIPort pAhciPort = &pThis->ahciPort[i];
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync pAhciPort->iLUN = i;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync ahciPortSwReset(pAhciPort);
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync }
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync /* Init Global registers */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pThis->regHbaCap = AHCI_HBA_CAP_ISS_SHIFT(AHCI_HBA_CAP_ISS_GEN2) |
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync AHCI_HBA_CAP_S64A | /* 64bit addressing supported */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync AHCI_HBA_CAP_SAM | /* AHCI mode only */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync AHCI_HBA_CAP_SNCQ | /* Support native command queuing */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync AHCI_HBA_CAP_SSS | /* Staggered spin up */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync AHCI_HBA_CAP_CCCS | /* Support command completion coalescing */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync AHCI_HBA_CAP_NCS_SET(pThis->cCmdSlotsAvail) | /* Number of command slots we support */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync AHCI_HBA_CAP_NP_SET(pThis->cPortsImpl); /* Number of supported ports */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pThis->regHbaCtrl = AHCI_HBA_CTRL_AE;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync pThis->regHbaPi = ahciGetPortsImplemented(pThis->cPortsImpl);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync pThis->regHbaVs = AHCI_HBA_VS_MJR | AHCI_HBA_VS_MNR;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync pThis->regHbaCccCtl = 0;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync pThis->regHbaCccPorts = 0;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync pThis->uCccTimeout = 0;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync pThis->uCccPortNr = 0;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync pThis->uCccNr = 0;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /* Clear pending interrupts. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pThis->regHbaIs = 0;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pThis->u32PortsInterrupted = 0;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciHbaClearInterrupt(pThis);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pThis->f64BitAddr = false;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pThis->u32PortsInterrupted = 0;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pThis->f8ByteMMIO4BytesWrittenSuccessfully = false;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /* Clear the HBA Reset bit */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pThis->regHbaCtrl &= ~AHCI_HBA_CTRL_HR;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync#endif
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/**
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * Reads from a AHCI controller register.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync *
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * @returns VBox status code.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync *
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @param pAhci The AHCI instance.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @param uReg The register to write.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @param pv Where to store the result.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @param cb Number of bytes read.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync */
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncstatic int ahciRegisterRead(PAHCI pAhci, uint32_t uReg, void *pv, unsigned cb)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync int rc = VINF_SUCCESS;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync uint32_t iReg;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /*
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * If the access offset is smaller than AHCI_HBA_GLOBAL_SIZE the guest accesses the global registers.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * Otherwise it accesses the registers of a port.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (uReg < AHCI_HBA_GLOBAL_SIZE)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync iReg = uReg >> 2;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Log3(("%s: Trying to read from global register %u\n", __FUNCTION__, iReg));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (iReg < RT_ELEMENTS(g_aOpRegs))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync const AHCIOPREG *pReg = &g_aOpRegs[iReg];
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync rc = pReg->pfnRead(pAhci, iReg, (uint32_t *)pv);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync else
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Log3(("%s: Trying to read global register %u/%u!!!\n", __FUNCTION__, iReg, RT_ELEMENTS(g_aOpRegs)));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync *(uint32_t *)pv = 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync else
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint32_t iRegOffset;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint32_t iPort;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Calculate accessed port. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uReg -= AHCI_HBA_GLOBAL_SIZE;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync iPort = uReg / AHCI_PORT_REGISTER_SIZE;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync iRegOffset = (uReg % AHCI_PORT_REGISTER_SIZE);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync iReg = iRegOffset >> 2;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Log3(("%s: Trying to read from port %u and register %u\n", __FUNCTION__, iPort, iReg));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (RT_LIKELY( iPort < pAhci->cPortsImpl
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync && iReg < RT_ELEMENTS(g_aPortOpRegs)))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync const AHCIPORTOPREG *pPortReg = &g_aPortOpRegs[iReg];
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync rc = pPortReg->pfnRead(pAhci, &pAhci->ahciPort[iPort], iReg, (uint32_t *)pv);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync else
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync {
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync Log3(("%s: Trying to read port %u register %u/%u!!!\n", __FUNCTION__, iPort, iReg, RT_ELEMENTS(g_aPortOpRegs)));
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync rc = VINF_IOM_MMIO_UNUSED_00;
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync }
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync /*
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync * Windows Vista tries to read one byte from some registers instead of four.
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync * Correct the value according to the read size.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (RT_SUCCESS(rc) && cb != sizeof(uint32_t))
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync switch (cb)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync {
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync case 1:
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync uint8_t uNewValue;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync uint8_t *p = (uint8_t *)pv;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync iRegOffset &= 3;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log3(("%s: iRegOffset=%u\n", __FUNCTION__, iRegOffset));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uNewValue = p[iRegOffset];
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* Clear old value */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync *(uint32_t *)pv = 0;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync *(uint8_t *)pv = uNewValue;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync break;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync default:
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync AssertMsgFailed(("%s: unsupported access width cb=%d iPort=%x iRegOffset=%x iReg=%x!!!\n",
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync __FUNCTION__, cb, iPort, iRegOffset, iReg));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return rc;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync/**
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * Writes a value to one of the AHCI controller registers.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync *
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @returns VBox status code.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync *
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @param pAhci The AHCI instance.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @param offReg The offset of the register to write to.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @param u32Value The value to write.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int ahciRegisterWrite(PAHCI pAhci, uint32_t offReg, uint32_t u32Value)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync int rc;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uint32_t iReg;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /*
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * If the access offset is smaller than 100h the guest accesses the global registers.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Otherwise it accesses the registers of a port.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if (offReg < AHCI_HBA_GLOBAL_SIZE)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log3(("Write global HBA register\n"));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync iReg = offReg >> 2;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if (iReg < RT_ELEMENTS(g_aOpRegs))
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync const AHCIOPREG *pReg = &g_aOpRegs[iReg];
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync rc = pReg->pfnWrite(pAhci, iReg, u32Value);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync else
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log3(("%s: Trying to write global register %u/%u!!!\n", __FUNCTION__, iReg, RT_ELEMENTS(g_aOpRegs)));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync rc = VINF_SUCCESS;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync else
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync uint32_t iPort;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync Log3(("Write Port register\n"));
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync /* Calculate accessed port. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync offReg -= AHCI_HBA_GLOBAL_SIZE;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync iPort = offReg / AHCI_PORT_REGISTER_SIZE;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync iReg = (offReg % AHCI_PORT_REGISTER_SIZE) >> 2;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log3(("%s: Trying to write to port %u and register %u\n", __FUNCTION__, iPort, iReg));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if (RT_LIKELY( iPort < pAhci->cPortsImpl
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync && iReg < RT_ELEMENTS(g_aPortOpRegs)))
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync const AHCIPORTOPREG *pPortReg = &g_aPortOpRegs[iReg];
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync rc = pPortReg->pfnWrite(pAhci, &pAhci->ahciPort[iPort], iReg, u32Value);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync }
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync else
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync Log3(("%s: Trying to write port %u register %u/%u!!!\n", __FUNCTION__, iPort, iReg, RT_ELEMENTS(g_aPortOpRegs)));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync rc = VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return rc;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync/**
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync * Memory mapped I/O Handler for read operations.
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync * @returns VBox status code.
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync *
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync * @param pDevIns The device instance.
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync * @param pvUser User argument.
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync * @param GCPhysAddr Physical address (in GC) where the read starts.
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync * @param pv Where to store the result.
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync * @param cb Number of bytes read.
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsyncPDMBOTHCBDECL(int) ahciMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log2(("#%d ahciMMIORead: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp\n",
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync int rc = ahciRegisterRead(pAhci, GCPhysAddr - pAhci->MMIOBase, pv, cb);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log2(("#%d ahciMMIORead: return pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp rc=%Rrc\n",
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr, rc));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Memory mapped I/O Handler for write operations.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @returns VBox status code.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param pDevIns The device instance.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param pvUser User argument.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param GCPhysAddr Physical address (in GC) where the read starts.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param pv Where to fetch the result.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param cb Number of bytes to write.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsyncPDMBOTHCBDECL(int) ahciMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Assert(cb == 4 || cb == 8);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Assert(!(GCPhysAddr & (cb - 1)));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /* Break up 64 bits writes into two dword writes. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /** @todo Eliminate this code once the IOM/EM starts taking care of these
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * situations. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (cb == 8)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /*
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Only write the first 4 bytes if they weren't already.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * It is possible that the last write to the register caused a world
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * switch and we entered this function again.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Writing the first 4 bytes again could cause indeterminate behavior
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * which can cause errors in the guest.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync int rc = VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (!pAhci->f8ByteMMIO4BytesWrittenSuccessfully)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync {
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync rc = ahciMMIOWrite(pDevIns, pvUser, GCPhysAddr, pv, 4);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (rc != VINF_SUCCESS)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhci->f8ByteMMIO4BytesWrittenSuccessfully = true;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync rc = ahciMMIOWrite(pDevIns, pvUser, GCPhysAddr + 4, (uint8_t *)pv + 4, 4);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /*
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * Reset flag again so that the first 4 bytes are written again on the next
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * 8byte MMIO access.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (rc == VINF_SUCCESS)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pAhci->f8ByteMMIO4BytesWrittenSuccessfully = false;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync /* Do the access. */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync Log2(("#%d ahciMMIOWrite: pvUser=%p:{%.*Rhxs} cb=%d GCPhysAddr=%RGp\n", pDevIns->iInstance, pv, cb, pv, cb, GCPhysAddr));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync return ahciRegisterWrite(pAhci, GCPhysAddr - pAhci->MMIOBase, *(uint32_t const *)pv);
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync}
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
cecb0b0ed75fde05db9e1958b7c813c572cf81fevboxsyncPDMBOTHCBDECL(int) ahciLegacyFakeWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync{
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync AssertMsgFailed(("Should not happen\n"));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync return VINF_SUCCESS;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync}
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsyncPDMBOTHCBDECL(int) ahciLegacyFakeRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync{
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync AssertMsgFailed(("Should not happen\n"));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * I/O port handler for writes to the index/data register pair.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @returns VBox status code.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param pDevIns The device instance.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param pvUser User argument.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param Port Port address where the write starts.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param pv Where to fetch the result.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * @param cb Number of bytes to write.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncPDMBOTHCBDECL(int) ahciIdxDataWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync int rc = VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync if (Port - pAhci->IOPortBase >= 8)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync {
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync unsigned iReg = (Port - pAhci->IOPortBase - 8) / 4;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Assert(cb == 4);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (iReg == 0)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync /* Write the index register. */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pAhci->regIdx = u32;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync }
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync else
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync /** @todo range check? */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Assert(iReg == 1);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync rc = ahciRegisterWrite(pAhci, pAhci->regIdx, u32);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (rc == VINF_IOM_R3_MMIO_WRITE)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync rc = VINF_IOM_R3_IOPORT_WRITE;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /* else: ignore */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync Log2(("#%d ahciIdxDataWrite: pu32=%p:{%.*Rhxs} cb=%d Port=%#x rc=%Rrc\n",
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pDevIns->iInstance, &u32, cb, &u32, cb, Port, rc));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * I/O port handler for reads from the index/data register pair.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @returns VBox status code.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * @param pDevIns The device instance.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param pvUser User argument.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param Port Port address where the read starts.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param pv Where to fetch the result.
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * @param cb Number of bytes to write.
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsyncPDMBOTHCBDECL(int) ahciIdxDataRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync int rc = VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (Port - pAhci->IOPortBase >= 8)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync unsigned iReg = (Port - pAhci->IOPortBase - 8) / 4;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Assert(cb == 4);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (iReg == 0)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /* Read the index register. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *pu32 = pAhci->regIdx;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync else
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Assert(iReg == 1);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /** @todo range check? */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync rc = ahciRegisterRead(pAhci, pAhci->regIdx, pu32, cb);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (rc == VINF_IOM_R3_MMIO_READ)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync rc = VINF_IOM_R3_IOPORT_READ;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync else if (rc == VINF_IOM_MMIO_UNUSED_00)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync rc = VERR_IOM_IOPORT_UNUSED;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync else
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *pu32 = UINT32_C(0xffffffff);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log2(("#%d ahciIdxDataRead: pu32=%p:{%.*Rhxs} cb=%d Port=%#x rc=%Rrc\n",
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync pDevIns->iInstance, pu32, cb, pu32, cb, Port, rc));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync#ifdef IN_RING3
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic DECLCALLBACK(int) ahciR3MMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync PAHCI pThis = PCIDEV_2_PAHCI(pPciDev);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PPDMDEVINS pDevIns = pPciDev->pDevIns;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync int rc = VINF_SUCCESS;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log2(("%s: registering MMIO area at GCPhysAddr=%RGp cb=%u\n", __FUNCTION__, GCPhysAddress, cb));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync Assert(enmType == PCI_ADDRESS_SPACE_MEM);
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync Assert(cb >= 4352);
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /** @todo change this to IOMMMIO_FLAGS_WRITE_ONLY_DWORD once EM/IOM starts
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * handling 2nd DWORD failures on split accesses correctly. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, NULL /*pvUser*/,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_ONLY_DWORD_QWORD,
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciMMIOWrite, ahciMMIORead, "AHCI");
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (RT_FAILURE(rc))
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync return rc;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (pThis->fR0Enabled)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync {
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync rc = PDMDevHlpMMIORegisterR0(pDevIns, GCPhysAddress, cb, NIL_RTR0PTR /*pvUser*/, "ahciMMIOWrite", "ahciMMIORead");
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (RT_FAILURE(rc))
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync return rc;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync }
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync if (pThis->fGCEnabled)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync rc = PDMDevHlpMMIORegisterRC(pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/, "ahciMMIOWrite", "ahciMMIORead");
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync if (RT_FAILURE(rc))
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync return rc;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync }
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pThis->MMIOBase = GCPhysAddress;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync return rc;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync}
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync/**
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * Map the legacy I/O port ranges to make Solaris work with the controller.
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsyncstatic DECLCALLBACK(int) ahciR3LegacyFakeIORangeMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync{
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync PAHCI pThis = PCIDEV_2_PAHCI(pPciDev);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync PPDMDEVINS pDevIns = pPciDev->pDevIns;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync int rc = VINF_SUCCESS;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync Log2(("%s: registering fake I/O area at GCPhysAddr=%RGp cb=%u\n", __FUNCTION__, GCPhysAddress, cb));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync Assert(enmType == PCI_ADDRESS_SPACE_IO);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, cb, NULL,
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync ahciLegacyFakeWrite, ahciLegacyFakeRead, NULL, NULL, "AHCI Fake");
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (RT_FAILURE(rc))
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync return rc;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (pThis->fR0Enabled)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync rc = PDMDevHlpIOPortRegisterR0(pDevIns, (RTIOPORT)GCPhysAddress, cb, 0,
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync "ahciLegacyFakeWrite", "ahciLegacyFakeRead", NULL, NULL, "AHCI Fake");
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (RT_FAILURE(rc))
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync return rc;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync }
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (pThis->fGCEnabled)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync rc = PDMDevHlpIOPortRegisterRC(pDevIns, (RTIOPORT)GCPhysAddress, cb, 0,
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync "ahciLegacyFakeWrite", "ahciLegacyFakeRead", NULL, NULL, "AHCI Fake");
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync if (RT_FAILURE(rc))
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync/**
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * Map the BMDMA I/O port range (used for the Index/Data pair register access)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsyncstatic DECLCALLBACK(int) ahciR3IdxDataIORangeMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync{
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync PAHCI pThis = PCIDEV_2_PAHCI(pPciDev);
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync PPDMDEVINS pDevIns = pPciDev->pDevIns;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync int rc = VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Log2(("%s: registering fake I/O area at GCPhysAddr=%RGp cb=%u\n", __FUNCTION__, GCPhysAddress, cb));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Assert(enmType == PCI_ADDRESS_SPACE_IO);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync /* We use the assigned size here, because we currently only support page aligned MMIO ranges. */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, cb, NULL,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciIdxDataWrite, ahciIdxDataRead, NULL, NULL, "AHCI IDX/DATA");
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (RT_FAILURE(rc))
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (pThis->fR0Enabled)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync rc = PDMDevHlpIOPortRegisterR0(pDevIns, (RTIOPORT)GCPhysAddress, cb, 0,
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync "ahciIdxDataWrite", "ahciIdxDataRead", NULL, NULL, "AHCI IDX/DATA");
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync if (RT_FAILURE(rc))
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (pThis->fGCEnabled)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync rc = PDMDevHlpIOPortRegisterRC(pDevIns, (RTIOPORT)GCPhysAddress, cb, 0,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync "ahciIdxDataWrite", "ahciIdxDataRead", NULL, NULL, "AHCI IDX/DATA");
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (RT_FAILURE(rc))
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync pThis->IOPortBase = (RTIOPORT)GCPhysAddress;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return rc;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync/* -=-=-=-=-=- PAHCI::ILeds -=-=-=-=-=- */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * Gets the pointer to the status LED of a unit.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @returns VBox status code.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param pInterface Pointer to the interface structure containing the called function pointer.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param iLUN The unit which status LED we desire.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @param ppLed Where to store the LED pointer.
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic DECLCALLBACK(int) ahciR3Status_QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PAHCI pAhci = PDMILEDPORTS_2_PAHCI(pInterface);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (iLUN < AHCI_MAX_NR_PORTS_IMPL)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync *ppLed = &pAhci->ahciPort[iLUN].Led;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VINF_SUCCESS;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return VERR_PDM_LUN_NOT_FOUND;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * @interface_method_impl{PDMIBASE,pfnQueryInterface}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic DECLCALLBACK(void *) ahciR3Status_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PAHCI pThis = PDMIBASE_2_PAHCI(pInterface);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync return NULL;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync}
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @interface_method_impl{PDMIBASE,pfnQueryInterface}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic DECLCALLBACK(void *) ahciR3PortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PAHCIPort pAhciPort = PDMIBASE_2_PAHCIPORT(pInterface);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pAhciPort->IBase);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pAhciPort->IPort);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pAhciPort->IPortAsync);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pAhciPort->IMountNotify);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return NULL;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync/**
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync * @interface_method_impl{PDMIBLOCKPORT,pfnQueryDeviceLocation}
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync */
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsyncstatic DECLCALLBACK(int) ahciR3PortQueryDeviceLocation(PPDMIBLOCKPORT pInterface, const char **ppcszController,
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync uint32_t *piInstance, uint32_t *piLUN)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync{
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync PAHCIPort pAhciPort = PDMIBLOCKPORT_2_PAHCIPORT(pInterface);
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync PPDMDEVINS pDevIns = pAhciPort->CTX_SUFF(pDevIns);
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync AssertPtrReturn(ppcszController, VERR_INVALID_POINTER);
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync AssertPtrReturn(piInstance, VERR_INVALID_POINTER);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync AssertPtrReturn(piLUN, VERR_INVALID_POINTER);
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync *ppcszController = pDevIns->pReg->szName;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync *piInstance = pDevIns->iInstance;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync *piLUN = pAhciPort->iLUN;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync return VINF_SUCCESS;
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync}
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync#ifdef LOG_ENABLED
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync/**
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * Dump info about the FIS
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync *
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * @returns nothing
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * @param pAhciPort The port the command FIS was read from.
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync * @param cmdFis The FIS to print info from.
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsyncstatic void ahciDumpFisInfo(PAHCIPort pAhciPort, uint8_t *cmdFis)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync{
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: *** Begin FIS info dump. ***\n", __FUNCTION__));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync /* Print FIS type. */
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync switch (cmdFis[AHCI_CMDFIS_TYPE])
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync {
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync case AHCI_CMDFIS_TYPE_H2D:
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync {
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: Command Fis type: H2D\n", __FUNCTION__));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: Command Fis size: %d bytes\n", __FUNCTION__, AHCI_CMDFIS_TYPE_H2D_SIZE));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync if (cmdFis[AHCI_CMDFIS_BITS] & AHCI_CMDFIS_C)
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: Command register update\n", __FUNCTION__));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync else
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: Control register update\n", __FUNCTION__));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: CMD=%#04x \"%s\"\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_CMD], ATACmdText(cmdFis[AHCI_CMDFIS_CMD])));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: FEAT=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_FET]));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: SECTN=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_SECTN]));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: CYLL=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_CYLL]));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: CYLH=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_CYLH]));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: HEAD=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_HEAD]));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: SECTNEXP=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_SECTNEXP]));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: CYLLEXP=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_CYLLEXP]));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: CYLHEXP=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_CYLHEXP]));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: FETEXP=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_FETEXP]));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: SECTC=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_SECTC]));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: SECTCEXP=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_SECTCEXP]));
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync ahciLog(("%s: CTL=%#04x\n", __FUNCTION__, cmdFis[AHCI_CMDFIS_CTL]));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync if (cmdFis[AHCI_CMDFIS_CTL] & AHCI_CMDFIS_CTL_SRST)
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: Reset bit is set\n", __FUNCTION__));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync break;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync case AHCI_CMDFIS_TYPE_D2H:
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: Command Fis type D2H\n", __FUNCTION__));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: Command Fis size: %d\n", __FUNCTION__, AHCI_CMDFIS_TYPE_D2H_SIZE));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync break;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync case AHCI_CMDFIS_TYPE_SETDEVBITS:
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: Command Fis type Set Device Bits\n", __FUNCTION__));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: Command Fis size: %d\n", __FUNCTION__, AHCI_CMDFIS_TYPE_SETDEVBITS_SIZE));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync break;
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync }
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync case AHCI_CMDFIS_TYPE_DMAACTD2H:
d3dea25ec07f6546715fe3af943ea863294b392evboxsync {
d3dea25ec07f6546715fe3af943ea863294b392evboxsync ahciLog(("%s: Command Fis type DMA Activate H2D\n", __FUNCTION__));
d3dea25ec07f6546715fe3af943ea863294b392evboxsync ahciLog(("%s: Command Fis size: %d\n", __FUNCTION__, AHCI_CMDFIS_TYPE_DMAACTD2H_SIZE));
d3dea25ec07f6546715fe3af943ea863294b392evboxsync break;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync }
d3dea25ec07f6546715fe3af943ea863294b392evboxsync case AHCI_CMDFIS_TYPE_DMASETUP:
d3dea25ec07f6546715fe3af943ea863294b392evboxsync {
d3dea25ec07f6546715fe3af943ea863294b392evboxsync ahciLog(("%s: Command Fis type DMA Setup\n", __FUNCTION__));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ahciLog(("%s: Command Fis size: %d\n", __FUNCTION__, AHCI_CMDFIS_TYPE_DMASETUP_SIZE));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync break;
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync }
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync case AHCI_CMDFIS_TYPE_PIOSETUP:
b26e5b503baa3dffc58048982eaf17ad1b53f207vboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: Command Fis type PIO Setup\n", __FUNCTION__));
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: Command Fis size: %d\n", __FUNCTION__, AHCI_CMDFIS_TYPE_PIOSETUP_SIZE));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync break;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync }
5b0a093ca572a855886faa6747ad46df859dd041vboxsync case AHCI_CMDFIS_TYPE_DATA:
5a5b5956f8b592c807c94785d58c25e717d430c4vboxsync {
71e045383a3316b27563b2f44b0e0e1231968bdcvboxsync ahciLog(("%s: Command Fis type Data\n", __FUNCTION__));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync break;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync }
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync default:
5b0a093ca572a855886faa6747ad46df859dd041vboxsync ahciLog(("%s: ERROR Unknown command FIS type\n", __FUNCTION__));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync break;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: *** End FIS info dump. ***\n", __FUNCTION__));
d3dea25ec07f6546715fe3af943ea863294b392evboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync/**
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * Dump info about the command header
d3dea25ec07f6546715fe3af943ea863294b392evboxsync *
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @returns nothing
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @param pAhciPort Pointer to the port the command header was read from.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @param pCmdHdr The command header to print info from.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync */
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncstatic void ahciDumpCmdHdrInfo(PAHCIPort pAhciPort, CmdHdr *pCmdHdr)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: *** Begin command header info dump. ***\n", __FUNCTION__));
d3dea25ec07f6546715fe3af943ea863294b392evboxsync ahciLog(("%s: Number of Scatter/Gatther List entries: %u\n", __FUNCTION__, AHCI_CMDHDR_PRDTL_ENTRIES(pCmdHdr->u32DescInf)));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pCmdHdr->u32DescInf & AHCI_CMDHDR_C)
d3dea25ec07f6546715fe3af943ea863294b392evboxsync ahciLog(("%s: Clear busy upon R_OK\n", __FUNCTION__));
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync if (pCmdHdr->u32DescInf & AHCI_CMDHDR_B)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync ahciLog(("%s: BIST Fis\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pCmdHdr->u32DescInf & AHCI_CMDHDR_R)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: Device Reset Fis\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pCmdHdr->u32DescInf & AHCI_CMDHDR_P)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: Command prefetchable\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pCmdHdr->u32DescInf & AHCI_CMDHDR_W)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: Device write\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync else
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: Device read\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pCmdHdr->u32DescInf & AHCI_CMDHDR_A)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: ATAPI command\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync else
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: ATA command\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: Command FIS length %u DW\n", __FUNCTION__, (pCmdHdr->u32DescInf & AHCI_CMDHDR_CFL_MASK)));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: *** End command header info dump. ***\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync#endif /* LOG_ENABLED */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync/**
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * Post the first D2H FIS from the device into guest memory.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync *
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @returns nothing
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * @param pAhciPort Pointer to the port which "receives" the FIS.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync */
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncstatic void ahciPostFirstD2HFisIntoMemory(PAHCIPort pAhciPort)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync uint8_t d2hFis[AHCI_CMDFIS_TYPE_D2H_SIZE];
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pAhciPort->fFirstD2HFisSend = true;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: Sending First D2H FIS from FIFO\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync memset(&d2hFis[0], 0, sizeof(d2hFis));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync d2hFis[AHCI_CMDFIS_TYPE] = AHCI_CMDFIS_TYPE_D2H;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync d2hFis[AHCI_CMDFIS_ERR] = 0x01;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync d2hFis[AHCI_CMDFIS_STS] = 0x00;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /* Set the signature based on the device type. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pAhciPort->fATAPI)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync {
ad66a27959d7085aa31760f63ce082943be60e89vboxsync d2hFis[AHCI_CMDFIS_CYLL] = 0x14;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync d2hFis[AHCI_CMDFIS_CYLH] = 0xeb;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync else
ad66a27959d7085aa31760f63ce082943be60e89vboxsync {
ad66a27959d7085aa31760f63ce082943be60e89vboxsync d2hFis[AHCI_CMDFIS_CYLL] = 0x00;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync d2hFis[AHCI_CMDFIS_CYLH] = 0x00;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync d2hFis[AHCI_CMDFIS_HEAD] = 0x00;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync d2hFis[AHCI_CMDFIS_SECTN] = 0x01;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync d2hFis[AHCI_CMDFIS_SECTC] = 0x01;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pAhciPort->regTFD = (1 << 8) | ATA_STAT_SEEK | ATA_STAT_WRERR;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (!pAhciPort->fATAPI)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pAhciPort->regTFD |= ATA_STAT_READY;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync ahciPostFisIntoMemory(pAhciPort, AHCI_CMDFIS_TYPE_D2H, d2hFis);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync/**
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * Post the FIS in the memory area allocated by the guest and set interrupt if necessary.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync *
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @returns VBox status code
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @param pAhciPort The port which "receives" the FIS.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @param uFisType The type of the FIS.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * @param pCmdFis Pointer to the FIS which is to be posted into memory.
ad66a27959d7085aa31760f63ce082943be60e89vboxsync */
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncstatic int ahciPostFisIntoMemory(PAHCIPort pAhciPort, unsigned uFisType, uint8_t *pCmdFis)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync int rc = VINF_SUCCESS;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync RTGCPHYS GCPhysAddrRecFis = pAhciPort->GCPhysAddrFb;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync unsigned cbFis = 0;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: pAhciPort=%p uFisType=%u pCmdFis=%p\n", __FUNCTION__, pAhciPort, uFisType, pCmdFis));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pAhciPort->regCMD & AHCI_PORT_CMD_FRE)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync {
ad66a27959d7085aa31760f63ce082943be60e89vboxsync AssertMsg(GCPhysAddrRecFis, ("%s: GCPhysAddrRecFis is 0\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /* Determine the offset and size of the FIS based on uFisType. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync switch (uFisType)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync {
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case AHCI_CMDFIS_TYPE_D2H:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync GCPhysAddrRecFis += AHCI_RECFIS_RFIS_OFFSET;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync cbFis = AHCI_CMDFIS_TYPE_D2H_SIZE;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync break;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case AHCI_CMDFIS_TYPE_SETDEVBITS:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync {
ad66a27959d7085aa31760f63ce082943be60e89vboxsync GCPhysAddrRecFis += AHCI_RECFIS_SDBFIS_OFFSET;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync cbFis = AHCI_CMDFIS_TYPE_SETDEVBITS_SIZE;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync break;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case AHCI_CMDFIS_TYPE_DMASETUP:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync {
ad66a27959d7085aa31760f63ce082943be60e89vboxsync GCPhysAddrRecFis += AHCI_RECFIS_DSFIS_OFFSET;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync cbFis = AHCI_CMDFIS_TYPE_DMASETUP_SIZE;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync break;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case AHCI_CMDFIS_TYPE_PIOSETUP:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync {
ad66a27959d7085aa31760f63ce082943be60e89vboxsync GCPhysAddrRecFis += AHCI_RECFIS_PSFIS_OFFSET;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync cbFis = AHCI_CMDFIS_TYPE_PIOSETUP_SIZE;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync break;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync default:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /*
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * We should post the unknown FIS into memory too but this never happens because
ad66a27959d7085aa31760f63ce082943be60e89vboxsync * we know which FIS types we generate. ;)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync AssertMsgFailed(("%s: Unknown FIS type!\n", __FUNCTION__));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync /* Post the FIS into memory. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ahciLog(("%s: PDMDevHlpPCIPhysWrite GCPhysAddrRecFis=%RGp cbFis=%u\n", __FUNCTION__, GCPhysAddrRecFis, cbFis));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync PDMDevHlpPCIPhysWrite(pAhciPort->CTX_SUFF(pDevIns), GCPhysAddrRecFis, pCmdFis, cbFis);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync return rc;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncDECLINLINE(void) ataH2BE_U16(uint8_t *pbBuf, uint16_t val)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pbBuf[0] = val >> 8;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pbBuf[1] = val;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncDECLINLINE(void) ataH2BE_U24(uint8_t *pbBuf, uint32_t val)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync pbBuf[0] = val >> 16;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pbBuf[1] = val >> 8;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pbBuf[2] = val;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsyncDECLINLINE(void) ataH2BE_U32(uint8_t *pbBuf, uint32_t val)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
d3dea25ec07f6546715fe3af943ea863294b392evboxsync pbBuf[0] = val >> 24;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync pbBuf[1] = val >> 16;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync pbBuf[2] = val >> 8;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync pbBuf[3] = val;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
d3dea25ec07f6546715fe3af943ea863294b392evboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncDECLINLINE(uint16_t) ataBE2H_U16(const uint8_t *pbBuf)
d3dea25ec07f6546715fe3af943ea863294b392evboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync return (pbBuf[0] << 8) | pbBuf[1];
d3dea25ec07f6546715fe3af943ea863294b392evboxsync}
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
d3dea25ec07f6546715fe3af943ea863294b392evboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncDECLINLINE(uint32_t) ataBE2H_U24(const uint8_t *pbBuf)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync return (pbBuf[0] << 16) | (pbBuf[1] << 8) | pbBuf[2];
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncDECLINLINE(uint32_t) ataBE2H_U32(const uint8_t *pbBuf)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync return (pbBuf[0] << 24) | (pbBuf[1] << 16) | (pbBuf[2] << 8) | pbBuf[3];
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncDECLINLINE(void) ataLBA2MSF(uint8_t *pbBuf, uint32_t iATAPILBA)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync iATAPILBA += 150;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pbBuf[0] = (iATAPILBA / 75) / 60;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pbBuf[1] = (iATAPILBA / 75) % 60;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pbBuf[2] = iATAPILBA % 75;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncDECLINLINE(uint32_t) ataMSF2LBA(const uint8_t *pbBuf)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic void atapiCmdOK(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync{
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync pAhciReq->uATARegError = 0;
e4bf6817370e1a71833a02285515694afcda7599vboxsync pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK;
e4bf6817370e1a71833a02285515694afcda7599vboxsync pAhciReq->cmdFis[AHCI_CMDFIS_SECTN] = (pAhciReq->cmdFis[AHCI_CMDFIS_SECTN] & ~7)
e4bf6817370e1a71833a02285515694afcda7599vboxsync | ((pAhciReq->enmTxDir != AHCITXDIR_WRITE) ? ATAPI_INT_REASON_IO : 0)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync | (!pAhciReq->cbTransfer ? ATAPI_INT_REASON_CD : 0);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync memset(pAhciPort->abATAPISense, '\0', sizeof(pAhciPort->abATAPISense));
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync pAhciPort->abATAPISense[0] = 0x70;
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync pAhciPort->abATAPISense[7] = 10;
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic void atapiCmdError(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, const uint8_t *pabATAPISense, size_t cbATAPISense)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync Log(("%s: sense=%#x (%s) asc=%#x ascq=%#x (%s)\n", __FUNCTION__, pabATAPISense[2] & 0x0f, SCSISenseText(pabATAPISense[2] & 0x0f),
e4bf6817370e1a71833a02285515694afcda7599vboxsync pabATAPISense[12], pabATAPISense[13], SCSISenseExtText(pabATAPISense[12], pabATAPISense[13])));
e4bf6817370e1a71833a02285515694afcda7599vboxsync pAhciReq->uATARegError = pabATAPISense[2] << 4;
e4bf6817370e1a71833a02285515694afcda7599vboxsync pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
e4bf6817370e1a71833a02285515694afcda7599vboxsync pAhciReq->cmdFis[AHCI_CMDFIS_SECTN] = (pAhciReq->cmdFis[AHCI_CMDFIS_SECTN] & ~7) |
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
e4bf6817370e1a71833a02285515694afcda7599vboxsync memset(pAhciPort->abATAPISense, '\0', sizeof(pAhciPort->abATAPISense));
e4bf6817370e1a71833a02285515694afcda7599vboxsync memcpy(pAhciPort->abATAPISense, pabATAPISense, RT_MIN(cbATAPISense, sizeof(pAhciPort->abATAPISense)));
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync/** @todo deprecated function - doesn't provide enough info. Replace by direct
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * calls to atapiCmdError() with full data. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsyncstatic void atapiCmdErrorSimple(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t uATAPISenseKey, uint8_t uATAPIASC)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint8_t abATAPISense[ATAPI_SENSE_SIZE];
e4bf6817370e1a71833a02285515694afcda7599vboxsync memset(abATAPISense, '\0', sizeof(abATAPISense));
e4bf6817370e1a71833a02285515694afcda7599vboxsync abATAPISense[0] = 0x70 | (1 << 7);
e4bf6817370e1a71833a02285515694afcda7599vboxsync abATAPISense[2] = uATAPISenseKey & 0x0f;
e4bf6817370e1a71833a02285515694afcda7599vboxsync abATAPISense[7] = 10;
e4bf6817370e1a71833a02285515694afcda7599vboxsync abATAPISense[12] = uATAPIASC;
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync atapiCmdError(pAhciPort, pAhciReq, abATAPISense, sizeof(abATAPISense));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic void ataSCSIPadStr(uint8_t *pbDst, const char *pbSrc, uint32_t cbSize)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync for (uint32_t i = 0; i < cbSize; i++)
e4bf6817370e1a71833a02285515694afcda7599vboxsync {
e4bf6817370e1a71833a02285515694afcda7599vboxsync if (*pbSrc)
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbDst[i] = *pbSrc++;
e4bf6817370e1a71833a02285515694afcda7599vboxsync else
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbDst[i] = ' ';
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic void ataPadString(uint8_t *pbDst, const char *pbSrc, uint32_t cbSize)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync for (uint32_t i = 0; i < cbSize; i++)
e4bf6817370e1a71833a02285515694afcda7599vboxsync {
e4bf6817370e1a71833a02285515694afcda7599vboxsync if (*pbSrc)
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbDst[i ^ 1] = *pbSrc++;
e4bf6817370e1a71833a02285515694afcda7599vboxsync else
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbDst[i ^ 1] = ' ';
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic uint32_t ataChecksum(void* ptr, size_t count)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync uint8_t u8Sum = 0xa5, *p = (uint8_t*)ptr;
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync size_t i;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync for (i = 0; i < count; i++)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync u8Sum += *p++;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync return (uint8_t)-(int32_t)u8Sum;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync}
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsyncstatic int ahciIdentifySS(PAHCIPort pAhciPort, void *pvBuf)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync{
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint16_t *p;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync int rc = VINF_SUCCESS;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p = (uint16_t *)pvBuf;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync memset(p, 0, 512);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[0] = RT_H2LE_U16(0x0040);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync p[1] = RT_H2LE_U16(RT_MIN(pAhciPort->PCHSGeometry.cCylinders, 16383));
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[3] = RT_H2LE_U16(pAhciPort->PCHSGeometry.cHeads);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* Block size; obsolete, but required for the BIOS. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[5] = RT_H2LE_U16(512);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[6] = RT_H2LE_U16(pAhciPort->PCHSGeometry.cSectors);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataPadString((uint8_t *)(p + 10), pAhciPort->szSerialNumber, AHCI_SERIAL_NUMBER_LENGTH); /* serial number */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[21] = RT_H2LE_U16(512); /* XXX: retired, cache size in sectors */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync p[22] = RT_H2LE_U16(0); /* ECC bytes per sector */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataPadString((uint8_t *)(p + 23), pAhciPort->szFirmwareRevision, AHCI_FIRMWARE_REVISION_LENGTH); /* firmware version */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataPadString((uint8_t *)(p + 27), pAhciPort->szModelNumber, AHCI_MODEL_NUMBER_LENGTH); /* model */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync#if ATA_MAX_MULT_SECTORS > 1
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[47] = RT_H2LE_U16(0x8000 | ATA_MAX_MULT_SECTORS);
e4bf6817370e1a71833a02285515694afcda7599vboxsync#endif
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[48] = RT_H2LE_U16(1); /* dword I/O, used by the BIOS */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[49] = RT_H2LE_U16(1 << 11 | 1 << 9 | 1 << 8); /* DMA and LBA supported */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync p[50] = RT_H2LE_U16(1 << 14); /* No drive specific standby timer minimum */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[51] = RT_H2LE_U16(240); /* PIO transfer cycle */
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync p[52] = RT_H2LE_U16(240); /* DMA transfer cycle */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[53] = RT_H2LE_U16(1 | 1 << 1 | 1 << 2); /* words 54-58,64-70,88 valid */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[54] = RT_H2LE_U16(RT_MIN(pAhciPort->PCHSGeometry.cCylinders, 16383));
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[55] = RT_H2LE_U16(pAhciPort->PCHSGeometry.cHeads);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[56] = RT_H2LE_U16(pAhciPort->PCHSGeometry.cSectors);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[57] = RT_H2LE_U16(RT_MIN(pAhciPort->PCHSGeometry.cCylinders, 16383) * pAhciPort->PCHSGeometry.cHeads * pAhciPort->PCHSGeometry.cSectors);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[58] = RT_H2LE_U16(RT_MIN(pAhciPort->PCHSGeometry.cCylinders, 16383) * pAhciPort->PCHSGeometry.cHeads * pAhciPort->PCHSGeometry.cSectors >> 16);
e4bf6817370e1a71833a02285515694afcda7599vboxsync if (pAhciPort->cMultSectors)
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[59] = RT_H2LE_U16(0x100 | pAhciPort->cMultSectors);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (pAhciPort->cTotalSectors <= (1 << 28) - 1)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[60] = RT_H2LE_U16(pAhciPort->cTotalSectors);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync p[61] = RT_H2LE_U16(pAhciPort->cTotalSectors >> 16);
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
e4bf6817370e1a71833a02285515694afcda7599vboxsync else
e4bf6817370e1a71833a02285515694afcda7599vboxsync {
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* Report maximum number of sectors possible with LBA28 */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[60] = RT_H2LE_U16(((1 << 28) - 1) & 0xffff);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[61] = RT_H2LE_U16(((1 << 28) - 1) >> 16);
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[63] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_MDMA, ATA_MDMA_MODE_MAX, pAhciPort->uATATransferMode)); /* MDMA modes supported / mode enabled */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[64] = RT_H2LE_U16(ATA_PIO_MODE_MAX > 2 ? (1 << (ATA_PIO_MODE_MAX - 2)) - 1 : 0); /* PIO modes beyond PIO2 supported */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[65] = RT_H2LE_U16(120); /* minimum DMA multiword tx cycle time */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[66] = RT_H2LE_U16(120); /* recommended DMA multiword tx cycle time */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync if ( pAhciPort->pDrvBlock->pfnDiscard
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync || ( pAhciPort->fAsyncInterface
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync && pAhciPort->pDrvBlockAsync->pfnStartDiscard)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync || pAhciPort->cbSector != 512
e4bf6817370e1a71833a02285515694afcda7599vboxsync || pAhciPort->fNonRotational)
e4bf6817370e1a71833a02285515694afcda7599vboxsync {
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[80] = RT_H2LE_U16(0x1f0); /* support everything up to ATA/ATAPI-8 ACS */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync p[81] = RT_H2LE_U16(0x28); /* conforms to ATA/ATAPI-8 ACS */
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
e4bf6817370e1a71833a02285515694afcda7599vboxsync else
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync {
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync p[82] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* supports power management, write cache and look-ahead */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[83] = RT_H2LE_U16(1 << 14 | 1 << 10 | 1 << 12 | 1 << 13); /* supports LBA48, FLUSH CACHE and FLUSH CACHE EXT */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[84] = RT_H2LE_U16(1 << 14);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[85] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* enabled power management, write cache and look-ahead */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[86] = RT_H2LE_U16(1 << 10 | 1 << 12 | 1 << 13); /* enabled LBA48, FLUSH CACHE and FLUSH CACHE EXT */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[87] = RT_H2LE_U16(1 << 14);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[88] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_UDMA, ATA_UDMA_MODE_MAX, pAhciPort->uATATransferMode)); /* UDMA modes supported / mode enabled */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[93] = RT_H2LE_U16(0x00);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[100] = RT_H2LE_U16(pAhciPort->cTotalSectors);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync p[101] = RT_H2LE_U16(pAhciPort->cTotalSectors >> 16);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[102] = RT_H2LE_U16(pAhciPort->cTotalSectors >> 32);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[103] = RT_H2LE_U16(pAhciPort->cTotalSectors >> 48);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* valid information, more than one logical sector per physical sector, 2^cLogSectorsPerPhysicalExp logical sectors per physical sector */
e4bf6817370e1a71833a02285515694afcda7599vboxsync if (pAhciPort->cLogSectorsPerPhysicalExp)
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[106] = RT_H2LE_U16(RT_BIT(14) | RT_BIT(13) | pAhciPort->cLogSectorsPerPhysicalExp);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (pAhciPort->cbSector != 512)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint32_t cSectorSizeInWords = pAhciPort->cbSector / sizeof(uint16_t);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* Enable reporting of logical sector size. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[106] |= RT_H2LE_U16(RT_BIT(12) | RT_BIT(14));
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[117] = RT_H2LE_U16(cSectorSizeInWords);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[118] = RT_H2LE_U16(cSectorSizeInWords >> 16);
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
e4bf6817370e1a71833a02285515694afcda7599vboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (pAhciPort->fNonRotational)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[217] = RT_H2LE_U16(1); /* Non-rotational medium */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if ( pAhciPort->pDrvBlock->pfnDiscard
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync || ( pAhciPort->fAsyncInterface
e4bf6817370e1a71833a02285515694afcda7599vboxsync && pAhciPort->pDrvBlockAsync->pfnStartDiscard)) /** @todo: Set bit 14 in word 69 too? (Deterministic read after TRIM). */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[169] = RT_H2LE_U16(1); /* DATA SET MANAGEMENT command supported. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* The following are SATA specific */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync p[75] = RT_H2LE_U16(pAhciPort->CTX_SUFF(pAhci)->cCmdSlotsAvail-1); /* Number of commands we support, 0's based */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync p[76] = RT_H2LE_U16((1 << 8) | (1 << 2)); /* Native command queuing and Serial ATA Gen2 (3.0 Gbps) speed supported */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync uint32_t uCsum = ataChecksum(p, 510);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync p[255] = RT_H2LE_U16(0xa5 | (uCsum << 8)); /* Integrity word */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync return VINF_SUCCESS;
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsynctypedef int (*PAtapiFunc)(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsync
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsyncstatic int atapiGetConfigurationSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsyncstatic int atapiGetEventStatusNotificationSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiIdentifySS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiInquirySS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiMechanismStatusSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiModeSenseErrorRecoverySS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiModeSenseCDStatusSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiReadCapacitySS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiReadDiscInformationSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiReadTOCNormalSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiReadTOCMultiSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiReadTOCRawSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiReadTrackInformationSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiRequestSenseSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiPassthroughSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiReadDVDStructureSS(PAHCIREQ, PAHCIPort, size_t, size_t *);
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync/**
e4bf6817370e1a71833a02285515694afcda7599vboxsync * Source/sink function indexes for g_apfnAtapiFuncs.
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsynctypedef enum ATAPIFN
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ATAFN_SS_NULL = 0,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_GET_CONFIGURATION,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_IDENTIFY,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_INQUIRY,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_MECHANISM_STATUS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_READ_CAPACITY,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_READ_DISC_INFORMATION,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_READ_TOC_NORMAL,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ATAFN_SS_ATAPI_READ_TOC_MULTI,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ATAFN_SS_ATAPI_READ_TOC_RAW,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ATAFN_SS_ATAPI_READ_TRACK_INFORMATION,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ATAFN_SS_ATAPI_REQUEST_SENSE,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ATAFN_SS_ATAPI_PASSTHROUGH,
e4bf6817370e1a71833a02285515694afcda7599vboxsync ATAFN_SS_ATAPI_READ_DVD_STRUCTURE,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ATAFN_SS_MAX
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync} ATAPIFN;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync/**
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * Array of source/sink functions, the index is ATAFNSS.
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * Make sure ATAFNSS and this array match!
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsyncstatic const PAtapiFunc g_apfnAtapiFuncs[ATAFN_SS_MAX] =
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync NULL,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiGetConfigurationSS,
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync atapiGetEventStatusNotificationSS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiIdentifySS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiInquirySS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiMechanismStatusSS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiModeSenseErrorRecoverySS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiModeSenseCDStatusSS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiReadCapacitySS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiReadDiscInformationSS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiReadTOCNormalSS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiReadTOCMultiSS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiReadTOCRawSS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiReadTrackInformationSS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiRequestSenseSS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiPassthroughSS,
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiReadDVDStructureSS
e4bf6817370e1a71833a02285515694afcda7599vboxsync};
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiIdentifySS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint16_t p[256];
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync memset(p, 0, 512);
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* Removable CDROM, 50us response, 12 byte packets */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[0] = RT_H2LE_U16(2 << 14 | 5 << 8 | 1 << 7 | 2 << 5 | 0 << 0);
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataPadString((uint8_t *)(p + 10), pAhciPort->szSerialNumber, AHCI_SERIAL_NUMBER_LENGTH); /* serial number */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[20] = RT_H2LE_U16(3); /* XXX: retired, cache type */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[21] = RT_H2LE_U16(512); /* XXX: retired, cache size in sectors */
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataPadString((uint8_t *)(p + 23), pAhciPort->szFirmwareRevision, AHCI_FIRMWARE_REVISION_LENGTH); /* firmware version */
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataPadString((uint8_t *)(p + 27), pAhciPort->szModelNumber, AHCI_MODEL_NUMBER_LENGTH); /* model */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[49] = RT_H2LE_U16(1 << 11 | 1 << 9 | 1 << 8); /* DMA and LBA supported */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[50] = RT_H2LE_U16(1 << 14); /* No drive specific standby timer minimum */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[51] = RT_H2LE_U16(240); /* PIO transfer cycle */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[52] = RT_H2LE_U16(240); /* DMA transfer cycle */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[53] = RT_H2LE_U16(1 << 1 | 1 << 2); /* words 64-70,88 are valid */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[63] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_MDMA, ATA_MDMA_MODE_MAX, pAhciPort->uATATransferMode)); /* MDMA modes supported / mode enabled */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[64] = RT_H2LE_U16(ATA_PIO_MODE_MAX > 2 ? (1 << (ATA_PIO_MODE_MAX - 2)) - 1 : 0); /* PIO modes beyond PIO2 supported */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[65] = RT_H2LE_U16(120); /* minimum DMA multiword tx cycle time */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[66] = RT_H2LE_U16(120); /* recommended DMA multiword tx cycle time */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[73] = RT_H2LE_U16(0x003e); /* ATAPI CDROM major */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[74] = RT_H2LE_U16(9); /* ATAPI CDROM minor */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[82] = RT_H2LE_U16(1 << 4 | 1 << 9); /* supports packet command set and DEVICE RESET */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[83] = RT_H2LE_U16(1 << 14);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync p[84] = RT_H2LE_U16(1 << 14);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[85] = RT_H2LE_U16(1 << 4 | 1 << 9); /* enabled packet command set and DEVICE RESET */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[86] = RT_H2LE_U16(0);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[87] = RT_H2LE_U16(1 << 14);
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[88] = RT_H2LE_U16(ATA_TRANSFER_ID(ATA_MODE_UDMA, ATA_UDMA_MODE_MAX, pAhciPort->uATATransferMode)); /* UDMA modes supported / mode enabled */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[93] = RT_H2LE_U16((1 | 1 << 1) << ((pAhciPort->iLUN & 1) == 0 ? 0 : 8) | 1 << 13 | 1 << 14);
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* The following are SATA specific */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[75] = RT_H2LE_U16(31); /* We support 32 commands */
e4bf6817370e1a71833a02285515694afcda7599vboxsync p[76] = RT_H2LE_U16((1 << 8) | (1 << 2)); /* Native command queuing and Serial ATA Gen2 (3.0 Gbps) speed supported */
e4bf6817370e1a71833a02285515694afcda7599vboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Copy the buffer in to the scatter gather list. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&p[0],
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync RT_MIN(cbData, sizeof(p)));
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync atapiCmdOK(pAhciPort, pAhciReq);
e4bf6817370e1a71833a02285515694afcda7599vboxsync return VINF_SUCCESS;
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsyncstatic int atapiReadCapacitySS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync uint8_t aBuf[8];
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U32(aBuf, pAhciPort->cTotalSectors - 1);
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U32(aBuf + 4, 2048);
e4bf6817370e1a71833a02285515694afcda7599vboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* Copy the buffer in to the scatter gather list. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync RT_MIN(cbData, sizeof(aBuf)));
e4bf6817370e1a71833a02285515694afcda7599vboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync atapiCmdOK(pAhciPort, pAhciReq);
e4bf6817370e1a71833a02285515694afcda7599vboxsync return VINF_SUCCESS;
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiReadDiscInformationSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint8_t aBuf[34];
e4bf6817370e1a71833a02285515694afcda7599vboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync memset(aBuf, '\0', 34);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U16(aBuf, 32);
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[2] = (0 << 4) | (3 << 2) | (2 << 0); /* not erasable, complete session, complete disc */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[3] = 1; /* number of first track */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[4] = 1; /* number of sessions (LSB) */
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[5] = 1; /* first track number in last session (LSB) */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[6] = 1; /* last track number in last session (LSB) */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[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 */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[8] = 0; /* disc type = CD-ROM */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[9] = 0; /* number of sessions (MSB) */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[10] = 0; /* number of sessions (MSB) */
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync aBuf[11] = 0; /* number of sessions (MSB) */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U32(aBuf + 16, 0x00ffffff); /* last session lead-in start time is not available */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U32(aBuf + 20, 0x00ffffff); /* last possible start time for lead-out is not available */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync /* Copy the buffer in to the scatter gather list. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
e4bf6817370e1a71833a02285515694afcda7599vboxsync RT_MIN(cbData, sizeof(aBuf)));
e4bf6817370e1a71833a02285515694afcda7599vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync atapiCmdOK(pAhciPort, pAhciReq);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return VINF_SUCCESS;
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiReadTrackInformationSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint8_t aBuf[36];
e4bf6817370e1a71833a02285515694afcda7599vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /* Accept address/number type of 1 only, and only track 1 exists. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync if ((pAhciReq->aATAPICmd[1] & 0x03) != 1 || ataBE2H_U32(&pAhciReq->aATAPICmd[2]) != 1)
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return VINF_SUCCESS;
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync memset(aBuf, '\0', 36);
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U16(aBuf, 34);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[2] = 1; /* track number (LSB) */
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[3] = 1; /* session number (LSB) */
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[5] = (0 << 5) | (0 << 4) | (4 << 0); /* not damaged, primary copy, data track */
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[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 */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[7] = (0 << 1) | (0 << 0); /* last recorded address not valid, next recordable address not valid */
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U32(aBuf + 8, 0); /* track start address is 0 */
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U32(aBuf + 24, pAhciPort->cTotalSectors); /* track size */
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[32] = 0; /* track number (MSB) */
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[33] = 0; /* session number (MSB) */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /* Copy the buffer in to the scatter gather list. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync RT_MIN(cbData, sizeof(aBuf)));
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiCmdOK(pAhciPort, pAhciReq);
e4bf6817370e1a71833a02285515694afcda7599vboxsync return VINF_SUCCESS;
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic size_t atapiGetConfigurationFillFeatureListProfiles(PAHCIPort pAhciPort, uint8_t *pbBuf, size_t cbBuf)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync if (cbBuf < 3*4)
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync return 0;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U16(pbBuf, 0x0); /* feature 0: list of profiles supported */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync pbBuf[2] = (0 << 2) | (1 << 1) | (1 || 0); /* version 0, persistent, current */
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbBuf[3] = 8; /* additional bytes for profiles */
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* The MMC-3 spec says that DVD-ROM read capability should be reported
e4bf6817370e1a71833a02285515694afcda7599vboxsync * before CD-ROM read capability. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U16(pbBuf + 4, 0x10); /* profile: read-only DVD */
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbBuf[6] = (0 << 0); /* NOT current profile */
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U16(pbBuf + 8, 0x08); /* profile: read only CD */
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbBuf[10] = (1 << 0); /* current profile */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync return 3*4; /* Header + 2 profiles entries */
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic size_t atapiGetConfigurationFillFeatureCore(PAHCIPort pAhciPort, uint8_t *pbBuf, size_t cbBuf)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync if (cbBuf < 12)
e4bf6817370e1a71833a02285515694afcda7599vboxsync return 0;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync ataH2BE_U16(pbBuf, 0x1); /* feature 0001h: Core Feature */
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbBuf[3] = 8; /* Additional length */
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync ataH2BE_U16(pbBuf + 4, 0x00000002); /* Physical interface ATAPI. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbBuf[8] = RT_BIT(0); /* DBE */
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* Rest is reserved. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync return 12;
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsyncstatic size_t atapiGetConfigurationFillFeatureMorphing(PAHCIPort pAhciPort, uint8_t *pbBuf, size_t cbBuf)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync if (cbBuf < 8)
e4bf6817370e1a71833a02285515694afcda7599vboxsync return 0;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U16(pbBuf, 0x2); /* feature 0002h: Morphing Feature */
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbBuf[2] = (0x1 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbBuf[3] = 4; /* Additional length */
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync pbBuf[4] = RT_BIT(1) | 0x0; /* OCEvent | !ASYNC */
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync /* Rest is reserved. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync return 8;
e4bf6817370e1a71833a02285515694afcda7599vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic size_t atapiGetConfigurationFillFeatureRemovableMedium(PAHCIPort pAhciPort, uint8_t *pbBuf, size_t cbBuf)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync if (cbBuf < 8)
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync return 0;
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U16(pbBuf, 0x3); /* feature 0003h: Removable Medium Feature */
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbBuf[3] = 4; /* Additional length */
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* Tray type loading | Load | Eject | !Pvnt Jmpr | !DBML | Lock */
e4bf6817370e1a71833a02285515694afcda7599vboxsync pbBuf[4] = (0x2 << 5) | RT_BIT(4) | RT_BIT(3) | (0x0 << 2) | (0x0 << 1) | RT_BIT(0);
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* Rest is reserved. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync return 8;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync}
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic size_t atapiGetConfigurationFillFeatureRandomReadable(PAHCIPort pAhciPort, uint8_t *pbBuf, size_t cbBuf)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (cbBuf < 12)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(pbBuf, 0x10); /* feature 0010h: Random Readable Feature */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[3] = 8; /* Additional length */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U32(pbBuf + 4, 2048); /* Logical block size. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(pbBuf + 8, 0x10); /* Blocking (0x10 for DVD, CD is not defined). */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[10] = 0; /* PP not present */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Rest is reserved. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return 12;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic size_t atapiGetConfigurationFillFeatureCDRead(PAHCIPort pAhciPort, uint8_t *pbBuf, size_t cbBuf)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (cbBuf < 8)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(pbBuf, 0x1e); /* feature 001Eh: CD Read Feature */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[2] = (0x2 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[3] = 0; /* Additional length */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[4] = (0x0 << 7) | (0x0 << 1) | 0x0; /* !DAP | !C2-Flags | !CD-Text. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Rest is reserved. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return 8;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic size_t atapiGetConfigurationFillFeaturePowerManagement(PAHCIPort pAhciPort, uint8_t *pbBuf, size_t cbBuf)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (cbBuf < 4)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(pbBuf, 0x100); /* feature 0100h: Power Management Feature */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[3] = 0; /* Additional length */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return 4;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic size_t atapiGetConfigurationFillFeatureTimeout(PAHCIPort pAhciPort, uint8_t *pbBuf, size_t cbBuf)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (cbBuf < 8)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(pbBuf, 0x105); /* feature 0105h: Timeout Feature */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[2] = (0x0 << 2) | RT_BIT(1) | RT_BIT(0); /* Version | Persistent | Current */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[3] = 4; /* Additional length */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf[4] = 0x0; /* !Group3 */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return 8;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic int atapiGetConfigurationSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint8_t aBuf[80];
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint8_t *pbBuf = &aBuf[0];
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync size_t cbBuf = sizeof(aBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync size_t cbCopied = 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Accept valid request types only, and only starting feature 0. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if ((pAhciReq->aATAPICmd[1] & 0x03) == 3 || ataBE2H_U16(&pAhciReq->aATAPICmd[2]) != 0)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return VINF_SUCCESS;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /** @todo implement switching between CD-ROM and DVD-ROM profile (the only
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync * way to differentiate them right now is based on the image size). */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (pAhciPort->cTotalSectors)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(pbBuf + 6, 0x08); /* current profile: read-only CD */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync else
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(pbBuf + 6, 0x00); /* current profile: none -> no media */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbBuf -= 8;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf += 8;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbCopied = atapiGetConfigurationFillFeatureListProfiles(pAhciPort, pbBuf, cbBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbBuf -= cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf += cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbCopied = atapiGetConfigurationFillFeatureCore(pAhciPort, pbBuf, cbBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbBuf -= cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf += cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbCopied = atapiGetConfigurationFillFeatureMorphing(pAhciPort, pbBuf, cbBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbBuf -= cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf += cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbCopied = atapiGetConfigurationFillFeatureRemovableMedium(pAhciPort, pbBuf, cbBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbBuf -= cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf += cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbCopied = atapiGetConfigurationFillFeatureRandomReadable(pAhciPort, pbBuf, cbBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbBuf -= cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf += cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbCopied = atapiGetConfigurationFillFeatureCDRead(pAhciPort, pbBuf, cbBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbBuf -= cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf += cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbCopied = atapiGetConfigurationFillFeaturePowerManagement(pAhciPort, pbBuf, cbBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbBuf -= cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf += cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbCopied = atapiGetConfigurationFillFeatureTimeout(pAhciPort, pbBuf, cbBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync cbBuf -= cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf += cbCopied;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Set data length now. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U32(&aBuf[0], sizeof(aBuf) - cbBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Copy the buffer in to the scatter gather list. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync RT_MIN(cbData, sizeof(aBuf)));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync atapiCmdOK(pAhciPort, pAhciReq);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return VINF_SUCCESS;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic int atapiGetEventStatusNotificationSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint8_t abBuf[8];
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Assert(pAhciReq->enmTxDir == AHCITXDIR_READ);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync Assert(pAhciReq->cbTransfer <= 8);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (!(pAhciReq->aATAPICmd[1] & 1))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* no asynchronous operation supported */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return VINF_SUCCESS;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint32_t OldStatus, NewStatus;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync do
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync OldStatus = ASMAtomicReadU32(&pAhciPort->MediaEventStatus);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync NewStatus = ATA_EVENT_STATUS_UNCHANGED;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync switch (OldStatus)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case ATA_EVENT_STATUS_MEDIA_NEW:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* mount */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(abBuf + 0, 6);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[2] = 0x04; /* media */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[4] = 0x02; /* new medium */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[5] = 0x02; /* medium present / door closed */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[6] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[7] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync break;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case ATA_EVENT_STATUS_MEDIA_CHANGED:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case ATA_EVENT_STATUS_MEDIA_REMOVED:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* umount */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(abBuf + 0, 6);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[2] = 0x04; /* media */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[4] = 0x03; /* media removal */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[5] = 0x00; /* medium absent / door closed */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[6] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[7] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (OldStatus == ATA_EVENT_STATUS_MEDIA_CHANGED)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync NewStatus = ATA_EVENT_STATUS_MEDIA_NEW;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync break;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case ATA_EVENT_STATUS_MEDIA_EJECT_REQUESTED: /* currently unused */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(abBuf + 0, 6);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[2] = 0x04; /* media */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[4] = 0x01; /* eject requested (eject button pressed) */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[5] = 0x02; /* medium present / door closed */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[6] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[7] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync break;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case ATA_EVENT_STATUS_UNCHANGED:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync default:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(abBuf + 0, 6);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[2] = 0x01; /* operational change request / notification */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[3] = 0x5e; /* supported = busy|media|external|power|operational */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[4] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[5] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[6] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync abBuf[7] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync break;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync } while (!ASMAtomicCmpXchgU32(&pAhciPort->MediaEventStatus, NewStatus, OldStatus));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&abBuf[0],
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync RT_MIN(cbData, sizeof(abBuf)));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync atapiCmdOK(pAhciPort, pAhciReq);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return VINF_SUCCESS;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic int atapiInquirySS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint8_t aBuf[36];
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[0] = 0x05; /* CD-ROM */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[1] = 0x80; /* removable */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[2] = 0x00; /* ISO */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[4] = 31; /* additional length */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[5] = 0; /* reserved */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[6] = 0; /* reserved */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[7] = 0; /* reserved */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataSCSIPadStr(aBuf + 8, pAhciPort->szInquiryVendorId, 8);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataSCSIPadStr(aBuf + 16, pAhciPort->szInquiryProductId, 16);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataSCSIPadStr(aBuf + 32, pAhciPort->szInquiryRevision, 4);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync /* Copy the buffer in to the scatter gather list. */
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync RT_MIN(cbData, sizeof(aBuf)));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync atapiCmdOK(pAhciPort, pAhciReq);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return VINF_SUCCESS;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsyncstatic int atapiModeSenseErrorRecoverySS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync{
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync uint8_t aBuf[16];
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync ataH2BE_U16(&aBuf[0], 16 + 6);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[2] = 0x70;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[3] = 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[4] = 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[5] = 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[6] = 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[7] = 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[8] = 0x01;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[9] = 0x06;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[10] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[11] = 0x05;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[12] = 0x00;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync aBuf[13] = 0x00;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aBuf[14] = 0x00;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync aBuf[15] = 0x00;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /* Copy the buffer in to the scatter gather list. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync RT_MIN(cbData, sizeof(aBuf)));
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync atapiCmdOK(pAhciPort, pAhciReq);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync return VINF_SUCCESS;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiModeSenseCDStatusSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint8_t aBuf[40];
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U16(&aBuf[0], 38);
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[2] = 0x70;
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[3] = 0;
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[4] = 0;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[5] = 0;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[6] = 0;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[7] = 0;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[8] = 0x2a;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[9] = 30; /* page length */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[10] = 0x08; /* DVD-ROM read support */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[11] = 0x00; /* no write support */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* The following claims we support audio play. This is obviously false,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync * but the Linux generic CDROM support makes many features depend on this
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync * capability. If it's not set, this causes many things to be disabled. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[12] = 0x71; /* multisession support, mode 2 form 1/2 support, audio play */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[13] = 0x00; /* no subchannel reads supported */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[14] = (1 << 0) | (1 << 3) | (1 << 5); /* lock supported, eject supported, tray type loading mechanism */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (pAhciPort->pDrvMount->pfnIsLocked(pAhciPort->pDrvMount))
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[14] |= 1 << 1; /* report lock state */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[15] = 0; /* no subchannel reads supported, no separate audio volume control, no changer etc. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U16(&aBuf[16], 5632); /* (obsolete) claim 32x speed support */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U16(&aBuf[18], 2); /* number of audio volume levels */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U16(&aBuf[20], 128); /* buffer size supported in Kbyte - We don't have a buffer because we write directly into guest memory.
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync Just write the value DevATA is using. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U16(&aBuf[22], 5632); /* (obsolete) current read speed 32x */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[24] = 0; /* reserved */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[25] = 0; /* reserved for digital audio (see idx 15) */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U16(&aBuf[26], 0); /* (obsolete) maximum write speed */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U16(&aBuf[28], 0); /* (obsolete) current write speed */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U16(&aBuf[30], 0); /* copy management revision supported 0=no CSS */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync aBuf[32] = 0; /* reserved */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync aBuf[33] = 0; /* reserved */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[34] = 0; /* reserved */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync aBuf[35] = 1; /* rotation control CAV */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ataH2BE_U16(&aBuf[36], 0); /* current write speed */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ataH2BE_U16(&aBuf[38], 0); /* number of write speed performance descriptors */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /* Copy the buffer in to the scatter gather list. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync RT_MIN(cbData, sizeof(aBuf)));
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync atapiCmdOK(pAhciPort, pAhciReq);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsyncstatic int atapiRequestSenseSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /* Copy the buffer in to the scatter gather list. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync pAhciPort->abATAPISense, RT_MIN(cbData, sizeof(pAhciPort->abATAPISense)));
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync atapiCmdOK(pAhciPort, pAhciReq);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync return VINF_SUCCESS;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync}
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsyncstatic int atapiMechanismStatusSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint8_t aBuf[8];
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ataH2BE_U16(&aBuf[0], 0);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /* no current LBA */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync aBuf[2] = 0;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync aBuf[3] = 0;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync aBuf[4] = 0;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync aBuf[5] = 1;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync ataH2BE_U16(aBuf + 6, 0);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync /* Copy the buffer in to the scatter gather list. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync RT_MIN(cbData, sizeof(aBuf)));
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync atapiCmdOK(pAhciPort, pAhciReq);
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync return VINF_SUCCESS;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync}
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsyncstatic int atapiReadTOCNormalSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint8_t aBuf[20], *q, iStartTrack;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync bool fMSF;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint32_t cbSize;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync fMSF = (pAhciReq->aATAPICmd[1] >> 1) & 1;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync iStartTrack = pAhciReq->aATAPICmd[6];
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync if (iStartTrack > 1 && iStartTrack != 0xaa)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync return VINF_SUCCESS;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
e4bf6817370e1a71833a02285515694afcda7599vboxsync q = aBuf + 2;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *q++ = 1; /* first session */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *q++ = 1; /* last session */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (iStartTrack <= 1)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *q++ = 0; /* reserved */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *q++ = 0x14; /* ADR, control */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *q++ = 1; /* track number */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* reserved */
ed521f15dde29451cbdb1df104f666204e04ba65vboxsync if (fMSF)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* reserved */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataLBA2MSF(q, 0);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync q += 3;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync else
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /* sector 0 */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U32(q, 0);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync q += 4;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* lead out track */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *q++ = 0; /* reserved */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *q++ = 0x14; /* ADR, control */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *q++ = 0xaa; /* track number */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *q++ = 0; /* reserved */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (fMSF)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *q++ = 0; /* reserved */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataLBA2MSF(q, pAhciPort->cTotalSectors);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync q += 3;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync else
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U32(q, pAhciPort->cTotalSectors);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync q += 4;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync cbSize = q - aBuf;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataH2BE_U16(aBuf, cbSize - 2);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* Copy the buffer in to the scatter gather list. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync RT_MIN(cbData, cbSize));
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync atapiCmdOK(pAhciPort, pAhciReq);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync return VINF_SUCCESS;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync}
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsyncstatic int atapiReadTOCMultiSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
e4bf6817370e1a71833a02285515694afcda7599vboxsync{
e4bf6817370e1a71833a02285515694afcda7599vboxsync uint8_t aBuf[12];
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync bool fMSF;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync fMSF = (pAhciReq->aATAPICmd[1] >> 1) & 1;
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* multi session: only a single session defined */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync/** @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. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync memset(aBuf, 0, 12);
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[1] = 0x0a;
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync aBuf[2] = 0x01;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[3] = 0x01;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[5] = 0x14; /* ADR, control */
e4bf6817370e1a71833a02285515694afcda7599vboxsync aBuf[6] = 1; /* first track in last complete session */
e4bf6817370e1a71833a02285515694afcda7599vboxsync if (fMSF)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync aBuf[8] = 0; /* reserved */
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataLBA2MSF(&aBuf[9], 0);
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
e4bf6817370e1a71833a02285515694afcda7599vboxsync else
e4bf6817370e1a71833a02285515694afcda7599vboxsync {
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* sector 0 */
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U32(aBuf + 8, 0);
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
e4bf6817370e1a71833a02285515694afcda7599vboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* Copy the buffer in to the scatter gather list. */
e4bf6817370e1a71833a02285515694afcda7599vboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
e4bf6817370e1a71833a02285515694afcda7599vboxsync RT_MIN(cbData, sizeof(aBuf)));
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync atapiCmdOK(pAhciPort, pAhciReq);
e4bf6817370e1a71833a02285515694afcda7599vboxsync return VINF_SUCCESS;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync}
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsyncstatic int atapiReadTOCRawSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync{
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync uint8_t aBuf[50]; /* Counted a maximum of 45 bytes but better be on the safe side. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync uint8_t *q, iStartTrack;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync bool fMSF;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync uint32_t cbSize;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync fMSF = (pAhciReq->aATAPICmd[1] >> 1) & 1;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync iStartTrack = pAhciReq->aATAPICmd[6];
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync q = aBuf + 2;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 1; /* first session */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 1; /* last session */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 1; /* session number */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0x14; /* data track */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* track number */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0xa0; /* first track in program area */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* min */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* sec */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* frame */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 1; /* first track */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0x00; /* disk type CD-DA or CD data */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 1; /* session number */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0x14; /* data track */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* track number */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0xa1; /* last track in program area */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* min */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* sec */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* frame */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 1; /* last track */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 1; /* session number */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0x14; /* data track */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* track number */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0xa2; /* lead-out */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* min */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* sec */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* frame */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (fMSF)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* reserved */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync ataLBA2MSF(q, pAhciPort->cTotalSectors);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync q += 3;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync }
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync else
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync ataH2BE_U32(q, pAhciPort->cTotalSectors);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync q += 4;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync }
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 1; /* session number */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0x14; /* ADR, control */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* track number */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 1; /* point */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* min */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* sec */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* frame */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (fMSF)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *q++ = 0; /* reserved */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync ataLBA2MSF(q, 0);
70aa086e9e9d2f85d2e997d0e69169018a001e54vboxsync q += 3;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync }
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync else
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /* sector 0 */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync ataH2BE_U32(q, 0);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync q += 4;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync }
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync cbSize = q - aBuf;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync ataH2BE_U16(aBuf, cbSize - 2);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /* Copy the buffer in to the scatter gather list. */
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync RT_MIN(cbData, cbSize));
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync atapiCmdOK(pAhciPort, pAhciReq);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync return VINF_SUCCESS;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync}
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync/**
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * Sets the given media track type.
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync */
70aa086e9e9d2f85d2e997d0e69169018a001e54vboxsyncstatic uint32_t ahciMediumTypeSet(PAHCIPort pAhciPort, uint32_t MediaTrackType)
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync{
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync return ASMAtomicXchgU32(&pAhciPort->MediaTrackType, MediaTrackType);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync}
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsyncstatic int atapiPassthroughSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync{
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync int rc = VINF_SUCCESS;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint8_t abATAPISense[ATAPI_SENSE_SIZE];
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint32_t cbTransfer;
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync void *pvBuf = NULL;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync cbTransfer = pAhciReq->cbTransfer;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (cbTransfer)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync pvBuf = (uint8_t *)RTMemAlloc(cbTransfer);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (!pvBuf)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync return VERR_NO_MEMORY;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (pAhciReq->enmTxDir == AHCITXDIR_WRITE)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ahciCopyFromPrdtl(pAhciPort->pDevInsR3, pAhciReq, pvBuf, cbTransfer);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (pAhciReq->fFlags & AHCI_REQ_OVERFLOW)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync return VINF_SUCCESS;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* Simple heuristics: if there is at least one sector of data
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync * to transfer, it's worth updating the LEDs. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (cbTransfer >= 2048)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (pAhciReq->enmTxDir != AHCITXDIR_WRITE)
ed521f15dde29451cbdb1df104f666204e04ba65vboxsync pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync else
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (cbTransfer > SCSI_MAX_BUFFER_SIZE)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync /* Linux accepts commands with up to 100KB of data, but expects
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync * us to handle commands with up to 128KB of data. The usual
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync * imbalance of powers. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint8_t aATAPICmd[ATAPI_PACKET_SIZE];
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint32_t iATAPILBA, cSectors, cReqSectors, cbCurrTX;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint8_t *pbBuf = (uint8_t *)pvBuf;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync switch (pAhciReq->aATAPICmd[0])
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case SCSI_READ_10:
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync case SCSI_WRITE_10:
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync case SCSI_WRITE_AND_VERIFY_10:
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync iATAPILBA = ataBE2H_U32(pAhciReq->aATAPICmd + 2);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync cSectors = ataBE2H_U16(pAhciReq->aATAPICmd + 7);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync break;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync case SCSI_READ_12:
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync case SCSI_WRITE_12:
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync iATAPILBA = ataBE2H_U32(pAhciReq->aATAPICmd + 2);
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync cSectors = ataBE2H_U32(pAhciReq->aATAPICmd + 6);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync break;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync case SCSI_READ_CD:
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync iATAPILBA = ataBE2H_U32(pAhciReq->aATAPICmd + 2);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync cSectors = ataBE2H_U24(pAhciReq->aATAPICmd + 6);
015d66d7947b8b6a38619a0c205842f86c832fe3vboxsync break;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync case SCSI_READ_CD_MSF:
015d66d7947b8b6a38619a0c205842f86c832fe3vboxsync iATAPILBA = ataMSF2LBA(pAhciReq->aATAPICmd + 3);
015d66d7947b8b6a38619a0c205842f86c832fe3vboxsync cSectors = ataMSF2LBA(pAhciReq->aATAPICmd + 6) - iATAPILBA;
015d66d7947b8b6a38619a0c205842f86c832fe3vboxsync break;
015d66d7947b8b6a38619a0c205842f86c832fe3vboxsync default:
015d66d7947b8b6a38619a0c205842f86c832fe3vboxsync AssertMsgFailed(("Don't know how to split command %#04x\n", pAhciReq->aATAPICmd[0]));
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync if (pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync LogRel(("AHCI: LUN#%d: CD-ROM passthrough split error\n", pAhciPort->iLUN));
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync RTMemFree(pvBuf);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync return VINF_SUCCESS;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync memcpy(aATAPICmd, pAhciReq->aATAPICmd, ATAPI_PACKET_SIZE);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync cReqSectors = 0;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync for (uint32_t i = cSectors; i > 0; i -= cReqSectors)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (i * pAhciReq->cbATAPISector > SCSI_MAX_BUFFER_SIZE)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync cReqSectors = SCSI_MAX_BUFFER_SIZE / pAhciReq->cbATAPISector;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync else
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync cReqSectors = i;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync cbCurrTX = pAhciReq->cbATAPISector * cReqSectors;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync switch (pAhciReq->aATAPICmd[0])
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync {
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync case SCSI_READ_10:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case SCSI_WRITE_10:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case SCSI_WRITE_AND_VERIFY_10:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ataH2BE_U32(aATAPICmd + 2, iATAPILBA);
e4bf6817370e1a71833a02285515694afcda7599vboxsync ataH2BE_U16(aATAPICmd + 7, cReqSectors);
e4bf6817370e1a71833a02285515694afcda7599vboxsync break;
e4bf6817370e1a71833a02285515694afcda7599vboxsync case SCSI_READ_12:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case SCSI_WRITE_12:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ataH2BE_U32(aATAPICmd + 2, iATAPILBA);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ataH2BE_U32(aATAPICmd + 6, cReqSectors);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync break;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case SCSI_READ_CD:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ataH2BE_U32(aATAPICmd + 2, iATAPILBA);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ataH2BE_U24(aATAPICmd + 6, cReqSectors);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync break;
1025ef7261a6961d07d390e85a095d806ccb88d9vboxsync case SCSI_READ_CD_MSF:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ataLBA2MSF(aATAPICmd + 3, iATAPILBA);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ataLBA2MSF(aATAPICmd + 6, iATAPILBA + cReqSectors);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync break;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync rc = pAhciPort->pDrvBlock->pfnSendCmd(pAhciPort->pDrvBlock,
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync aATAPICmd,
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciReq->enmTxDir == AHCITXDIR_READ
ad66a27959d7085aa31760f63ce082943be60e89vboxsync ? PDMBLOCKTXDIR_FROM_DEVICE
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync : PDMBLOCKTXDIR_TO_DEVICE,
ed521f15dde29451cbdb1df104f666204e04ba65vboxsync pbBuf,
ad66a27959d7085aa31760f63ce082943be60e89vboxsync &cbCurrTX,
d3dea25ec07f6546715fe3af943ea863294b392evboxsync abATAPISense,
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync sizeof(abATAPISense),
e4bf6817370e1a71833a02285515694afcda7599vboxsync 30000 /**< @todo timeout */);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync if (rc != VINF_SUCCESS)
e4bf6817370e1a71833a02285515694afcda7599vboxsync break;
e4bf6817370e1a71833a02285515694afcda7599vboxsync iATAPILBA += cReqSectors;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pbBuf += pAhciReq->cbATAPISector * cReqSectors;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync else
e4bf6817370e1a71833a02285515694afcda7599vboxsync {
e4bf6817370e1a71833a02285515694afcda7599vboxsync PDMBLOCKTXDIR enmBlockTxDir = PDMBLOCKTXDIR_NONE;
e4bf6817370e1a71833a02285515694afcda7599vboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync if (pAhciReq->enmTxDir == AHCITXDIR_READ)
e4bf6817370e1a71833a02285515694afcda7599vboxsync enmBlockTxDir = PDMBLOCKTXDIR_FROM_DEVICE;
e4bf6817370e1a71833a02285515694afcda7599vboxsync else if (pAhciReq->enmTxDir == AHCITXDIR_WRITE)
e4bf6817370e1a71833a02285515694afcda7599vboxsync enmBlockTxDir = PDMBLOCKTXDIR_TO_DEVICE;
e4bf6817370e1a71833a02285515694afcda7599vboxsync else if (pAhciReq->enmTxDir == AHCITXDIR_NONE)
e4bf6817370e1a71833a02285515694afcda7599vboxsync enmBlockTxDir = PDMBLOCKTXDIR_NONE;
e4bf6817370e1a71833a02285515694afcda7599vboxsync else
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync AssertMsgFailed(("Invalid transfer direction %d\n", pAhciReq->enmTxDir));
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync rc = pAhciPort->pDrvBlock->pfnSendCmd(pAhciPort->pDrvBlock,
e4bf6817370e1a71833a02285515694afcda7599vboxsync pAhciReq->aATAPICmd,
e4bf6817370e1a71833a02285515694afcda7599vboxsync enmBlockTxDir,
e4bf6817370e1a71833a02285515694afcda7599vboxsync pvBuf,
e4bf6817370e1a71833a02285515694afcda7599vboxsync &cbTransfer,
e4bf6817370e1a71833a02285515694afcda7599vboxsync abATAPISense,
ed521f15dde29451cbdb1df104f666204e04ba65vboxsync sizeof(abATAPISense),
e4bf6817370e1a71833a02285515694afcda7599vboxsync 30000 /**< @todo timeout */);
e4bf6817370e1a71833a02285515694afcda7599vboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
e4bf6817370e1a71833a02285515694afcda7599vboxsync /* Update the LEDs and the read/write statistics. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (cbTransfer >= 2048)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync {
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pAhciReq->enmTxDir != AHCITXDIR_WRITE)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync {
ad66a27959d7085aa31760f63ce082943be60e89vboxsync pAhciPort->Led.Actual.s.fReading = 0;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync STAM_REL_COUNTER_ADD(&pAhciPort->StatBytesRead, cbTransfer);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync else
d3dea25ec07f6546715fe3af943ea863294b392evboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync pAhciPort->Led.Actual.s.fWriting = 0;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync STAM_REL_COUNTER_ADD(&pAhciPort->StatBytesWritten, cbTransfer);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync if (RT_SUCCESS(rc))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* Do post processing for certain commands. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync switch (pAhciReq->aATAPICmd[0])
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case SCSI_SEND_CUE_SHEET:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync case SCSI_READ_TOC_PMA_ATIP:
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync {
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (!pAhciPort->pTrackList)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync rc = ATAPIPassthroughTrackListCreateEmpty(&pAhciPort->pTrackList);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if (RT_SUCCESS(rc))
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync rc = ATAPIPassthroughTrackListUpdate(pAhciPort->pTrackList, pAhciReq->aATAPICmd, pvBuf);
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync if ( RT_FAILURE(rc)
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync && pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync LogRel(("AHCI: Error (%Rrc) while updating the tracklist during %s, burning the disc might fail\n",
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync rc, pAhciReq->aATAPICmd[0] == SCSI_SEND_CUE_SHEET ? "SEND CUE SHEET" : "READ TOC/PMA/ATIP"));
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync break;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync case SCSI_SYNCHRONIZE_CACHE:
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (pAhciPort->pTrackList)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ATAPIPassthroughTrackListClear(pAhciPort->pTrackList);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync break;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (pAhciReq->enmTxDir == AHCITXDIR_READ)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync Assert(cbTransfer <= pAhciReq->cbTransfer);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync if (pAhciReq->aATAPICmd[0] == SCSI_INQUIRY)
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* Make sure that the real drive cannot be identified.
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync * Motivation: changing the VM configuration should be as
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync * invisible as possible to the guest. */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync if (cbTransfer >= 8 + 8)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataSCSIPadStr((uint8_t *)pvBuf + 8, "VBOX", 8);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (cbTransfer >= 16 + 16)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataSCSIPadStr((uint8_t *)pvBuf + 16, "CD-ROM", 16);
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (cbTransfer >= 32 + 4)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync ataSCSIPadStr((uint8_t *)pvBuf + 32, "1.0", 4);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (cbTransfer)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync Log3(("ATAPI PT data read (%d): %.*Rhxs\n", cbTransfer, cbTransfer, (uint8_t *)pvBuf));
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync /* Reply with the same amount of data as the real drive. */
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, pvBuf,
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync cbTransfer);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync }
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync else
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *pcbData = 0;
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync }
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync else
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync *pcbData = cbTransfer;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync atapiCmdOK(pAhciPort, pAhciReq);
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync }
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync else
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync if (pAhciPort->cErrors < MAX_LOG_REL_ERRORS)
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync uint8_t u8Cmd = pAhciReq->aATAPICmd[0];
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync do
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync /* don't log superfluous errors */
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync if ( rc == VERR_DEV_IO_ERROR
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync && ( u8Cmd == SCSI_TEST_UNIT_READY
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync || u8Cmd == SCSI_READ_CAPACITY
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync || u8Cmd == SCSI_READ_DVD_STRUCTURE
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync || u8Cmd == SCSI_READ_TOC_PMA_ATIP))
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync break;
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync pAhciPort->cErrors++;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync LogRel(("PIIX3 ATA: LUN#%d: CD-ROM passthrough cmd=%#04x sense=%d ASC=%#02x ASCQ=%#02x %Rrc\n",
c553cea6bfe2ce63ff5517ca4ec288502a890e99vboxsync pAhciPort->iLUN, u8Cmd, abATAPISense[2] & 0x0f, abATAPISense[12], abATAPISense[13], rc));
d3dea25ec07f6546715fe3af943ea863294b392evboxsync } while (0);
d3dea25ec07f6546715fe3af943ea863294b392evboxsync }
d3dea25ec07f6546715fe3af943ea863294b392evboxsync atapiCmdError(pAhciPort, pAhciReq, abATAPISense, sizeof(abATAPISense));
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pvBuf)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync RTMemFree(pvBuf);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync return VINF_SUCCESS;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync}
ad66a27959d7085aa31760f63ce082943be60e89vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync/** @todo: Revise ASAP. */
ad66a27959d7085aa31760f63ce082943be60e89vboxsync/* Keep in sync with DevATA.cpp! */
ad66a27959d7085aa31760f63ce082943be60e89vboxsyncstatic int atapiReadDVDStructureSS(PAHCIREQ pAhciReq, PAHCIPort pAhciPort, size_t cbData, size_t *pcbData)
ad66a27959d7085aa31760f63ce082943be60e89vboxsync{
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint8_t aBuf[25]; /* Counted a maximum of 20 bytes but better be on the safe side. */
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uint8_t *buf = aBuf;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync int media = pAhciReq->aATAPICmd[1];
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync int format = pAhciReq->aATAPICmd[7];
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync uint16_t max_len = ataBE2H_U16(&pAhciReq->aATAPICmd[8]);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync memset(buf, 0, max_len);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync
ad66a27959d7085aa31760f63ce082943be60e89vboxsync switch (format) {
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case 0x00:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case 0x01:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case 0x02:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case 0x03:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x04:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x05:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x06:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x07:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x08:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x09:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x0a:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x0b:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x0c:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x0d:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x0e:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x0f:
d3dea25ec07f6546715fe3af943ea863294b392evboxsync case 0x10:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x11:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x30:
d3dea25ec07f6546715fe3af943ea863294b392evboxsync case 0x31:
d3dea25ec07f6546715fe3af943ea863294b392evboxsync case 0xff:
d3dea25ec07f6546715fe3af943ea863294b392evboxsync if (media == 0)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync int uASC = SCSI_ASC_NONE;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync switch (format)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x0: /* Physical format information */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync int layer = pAhciReq->aATAPICmd[6];
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uint64_t total_sectors;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if (layer != 0)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync break;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync total_sectors = pAhciPort->cTotalSectors;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync total_sectors >>= 2;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if (total_sectors == 0)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uASC = -SCSI_ASC_MEDIUM_NOT_PRESENT;
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync break;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[4] = 1; /* DVD-ROM, part version 1 */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */
2d8870843ff566fee9bd3a6a5942414254106479vboxsync buf[7] = 0; /* default densities */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* FIXME: 0x30000 per spec? */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ataH2BE_U32(buf + 8, 0); /* start sector */
2d8870843ff566fee9bd3a6a5942414254106479vboxsync ataH2BE_U32(buf + 12, total_sectors - 1); /* end sector */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ataH2BE_U32(buf + 16, total_sectors - 1); /* l0 end sector */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* Size of buffer, not including 2 byte size field */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ataH2BE_U32(&buf[0], 2048 + 2);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* 2k data + 4 byte header */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uASC = (2048 + 4);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync break;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x01: /* DVD copyright information */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[4] = 0; /* no copyright data */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[5] = 0; /* no region restrictions */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* Size of buffer, not including 2 byte size field */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ataH2BE_U16(buf, 4 + 2);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* 4 byte header + 4 byte data */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uASC = (4 + 4);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x03: /* BCA information - invalid field for no BCA info */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync break;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x04: /* DVD disc manufacturing information */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* Size of buffer, not including 2 byte size field */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ataH2BE_U16(buf, 2048 + 2);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* 2k data + 4 byte header */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uASC = (2048 + 4);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync break;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0xff:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /*
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * This lists all the command capabilities above. Add new ones
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * in order and update the length and buffer return values.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync */
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[4] = 0x00; /* Physical format */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[5] = 0x40; /* Not writable, is readable */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ataH2BE_U16((buf + 6), 2048 + 4);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[8] = 0x01; /* Copyright info */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[9] = 0x40; /* Not writable, is readable */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ataH2BE_U16((buf + 10), 4 + 4);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[12] = 0x03; /* BCA info */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[13] = 0x40; /* Not writable, is readable */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ataH2BE_U16((buf + 14), 188 + 4);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[16] = 0x04; /* Manufacturing info */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync buf[17] = 0x40; /* Not writable, is readable */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ataH2BE_U16((buf + 18), 2048 + 4);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* Size of buffer, not including 2 byte size field */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync ataH2BE_U16(buf, 16 + 2);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* data written + 4 byte header */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uASC = (16 + 4);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync break;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync default: /* TODO: formats beyond DVD-ROM requires */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uASC = -SCSI_ASC_INV_FIELD_IN_CMD_PACKET;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if (uASC < 0)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, -uASC);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return false;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync break;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* TODO: BD support, fall through for now */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* Generic disk structures */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x80: /* TODO: AACS volume identifier */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x81: /* TODO: AACS media serial number */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x82: /* TODO: AACS media identifier */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x83: /* TODO: AACS media key block */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0x90: /* TODO: List of recognized format layers */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync case 0xc0: /* TODO: Write protection status */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync default:
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST,
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return false;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync }
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* Copy the buffer into the scatter gather list. */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync *pcbData = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq, (void *)&aBuf[0],
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync RT_MIN(cbData, max_len));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync atapiCmdOK(pAhciPort, pAhciReq);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync return false;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync}
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsyncstatic int atapiDoTransfer(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, size_t cbMax, ATAPIFN iSourceSink)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync{
ad66a27959d7085aa31760f63ce082943be60e89vboxsync size_t cbTransfered = 0;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync int rcSourceSink;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync rcSourceSink = g_apfnAtapiFuncs[iSourceSink](pAhciReq, pAhciPort, cbMax,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync &cbTransfered);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pAhciReq->cmdHdr.u32PRDBC = cbTransfered;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync pAhciReq->cbTransfer = cbTransfered;
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync LogFlow(("cbTransfered=%d\n", cbTransfered));
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync /* Write updated command header into memory of the guest. */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync PDMDevHlpPCIPhysWrite(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, &pAhciReq->cmdHdr, sizeof(CmdHdr));
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync return rcSourceSink;
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync}
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsyncstatic int atapiReadSectors2352PostProcess(PAHCIREQ pAhciReq, void **ppvProc, size_t *pcbProc)
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync{
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync uint8_t *pbBuf = NULL;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync uint32_t cSectors = pAhciReq->cbTransfer / 2048;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync uint32_t iATAPILBA = pAhciReq->uOffset / 2048;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync uint8_t *pbBufDst;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync uint8_t *pbBufSrc = (uint8_t *)pAhciReq->u.Io.DataSeg.pvSeg;
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync size_t cbAlloc = pAhciReq->cbTransfer + cSectors * (1 + 11 + 3 + 1 + 288); /* Per sector data like ECC. */
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync pbBuf = (uint8_t *)RTMemAlloc(cbAlloc);
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync if (RT_UNLIKELY(!pbBuf))
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync return VERR_NO_MEMORY;
113a43aca041000560f93f9a8ee23dfcf01a9e7cvboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pbBufDst = pbBuf;
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync for (uint32_t i = iATAPILBA; i < iATAPILBA + cSectors; i++)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync {
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /* sync bytes */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync *pbBufDst++ = 0x00;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync memset(pbBufDst, 0xff, 11);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pbBufDst += 11;
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync /* MSF */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync ataLBA2MSF(pbBufDst, i);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pbBufDst += 3;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync *pbBufDst++ = 0x01; /* mode 1 data */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /* data */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync memcpy(pbBufDst, pbBufSrc, 2048);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pbBufDst += 2048;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pbBufSrc += 2048;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /* ECC */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync memset(pbBufDst, 0, 288);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pbBufDst += 288;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync }
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync *ppvProc = pbBuf;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync *pcbProc = cbAlloc;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync return VINF_SUCCESS;
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync}
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsyncstatic int atapiReadSectors(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint32_t iATAPILBA, uint32_t cSectors, uint32_t cbSector)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync{
5b0a093ca572a855886faa6747ad46df859dd041vboxsync Log(("%s: %d sectors at LBA %d\n", __FUNCTION__, cSectors, iATAPILBA));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
5b0a093ca572a855886faa6747ad46df859dd041vboxsync switch (cbSector)
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync {
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync case 2048:
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pAhciReq->uOffset = (uint64_t)iATAPILBA * cbSector;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pAhciReq->cbTransfer = cSectors * cbSector;
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync break;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync case 2352:
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync {
3bb3e26b3306b9f62b18c17380bdf2ca3a98ca49vboxsync pAhciReq->u.Io.pfnPostProcess = atapiReadSectors2352PostProcess;
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync pAhciReq->uOffset = (uint64_t)iATAPILBA * 2048;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pAhciReq->cbTransfer = cSectors * 2048;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync break;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync }
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync default:
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync AssertMsgFailed(("Unsupported sectors size\n"));
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync break;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync }
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync return VINF_SUCCESS;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync}
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsyncstatic AHCITXDIR atapiParseCmdVirtualATAPI(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync{
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync AHCITXDIR enmTxDir = AHCITXDIR_NONE;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync const uint8_t *pbPacket;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync uint32_t cbMax;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync pbPacket = pAhciReq->aATAPICmd;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync ahciLog(("%s: ATAPI CMD=%#04x \"%s\"\n", __FUNCTION__, pbPacket[0], SCSICmdText(pbPacket[0])));
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync switch (pbPacket[0])
725fba5c64717677ac66072bae37e5b3686f3e6dvboxsync {
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync case SCSI_TEST_UNIT_READY:
5b0a093ca572a855886faa6747ad46df859dd041vboxsync if (pAhciPort->cNotifiedMediaChange > 0)
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync {
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync if (pAhciPort->cNotifiedMediaChange-- > 2)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync else
5b0a093ca572a855886faa6747ad46df859dd041vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync }
5b0a093ca572a855886faa6747ad46df859dd041vboxsync else if (pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
5b0a093ca572a855886faa6747ad46df859dd041vboxsync atapiCmdOK(pAhciPort, pAhciReq);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync else
5b0a093ca572a855886faa6747ad46df859dd041vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync break;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync case SCSI_GET_EVENT_STATUS_NOTIFICATION:
d3dea25ec07f6546715fe3af943ea863294b392evboxsync cbMax = ataBE2H_U16(pbPacket + 7);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION);
d3dea25ec07f6546715fe3af943ea863294b392evboxsync break;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case SCSI_MODE_SENSE_10:
5b0a093ca572a855886faa6747ad46df859dd041vboxsync {
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync uint8_t uPageControl, uPageCode;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync cbMax = ataBE2H_U16(pbPacket + 7);
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uPageControl = pbPacket[2] >> 6;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync uPageCode = pbPacket[2] & 0x3f;
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync switch (uPageControl)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync case SCSI_PAGECONTROL_CURRENT:
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync switch (uPageCode)
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync {
4b8c66482cbc69a01ac399b8588b8bc80b310523vboxsync case SCSI_MODEPAGE_ERROR_RECOVERY:
5b0a093ca572a855886faa6747ad46df859dd041vboxsync atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MODE_SENSE_ERROR_RECOVERY);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync break;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case SCSI_MODEPAGE_CD_STATUS:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MODE_SENSE_CD_STATUS);
ad66a27959d7085aa31760f63ce082943be60e89vboxsync break;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync default:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync goto error_cmd;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync }
ad66a27959d7085aa31760f63ce082943be60e89vboxsync break;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case SCSI_PAGECONTROL_CHANGEABLE:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync goto error_cmd;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync case SCSI_PAGECONTROL_DEFAULT:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync goto error_cmd;
ad66a27959d7085aa31760f63ce082943be60e89vboxsync default:
5b0a093ca572a855886faa6747ad46df859dd041vboxsync case SCSI_PAGECONTROL_SAVED:
5b0a093ca572a855886faa6747ad46df859dd041vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync break;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync }
5b0a093ca572a855886faa6747ad46df859dd041vboxsync break;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync }
5b0a093ca572a855886faa6747ad46df859dd041vboxsync case SCSI_REQUEST_SENSE:
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync cbMax = pbPacket[4];
5b0a093ca572a855886faa6747ad46df859dd041vboxsync atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_REQUEST_SENSE);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync break;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
ad66a27959d7085aa31760f63ce082943be60e89vboxsync if (pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync {
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync if (pbPacket[4] & 1)
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync pAhciPort->pDrvMount->pfnLock(pAhciPort->pDrvMount);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync else
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync pAhciPort->pDrvMount->pfnUnlock(pAhciPort->pDrvMount);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync atapiCmdOK(pAhciPort, pAhciReq);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync }
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync else
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync break;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync case SCSI_READ_10:
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync case SCSI_READ_12:
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync {
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync uint32_t cSectors, iATAPILBA;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync if (pAhciPort->cNotifiedMediaChange > 0)
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync {
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync pAhciPort->cNotifiedMediaChange-- ;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync break;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync }
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync {
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync break;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync }
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync if (pbPacket[0] == SCSI_READ_10)
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync cSectors = ataBE2H_U16(pbPacket + 7);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync else
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync cSectors = ataBE2H_U32(pbPacket + 6);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync iATAPILBA = ataBE2H_U32(pbPacket + 2);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync if (cSectors == 0)
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync {
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync atapiCmdOK(pAhciPort, pAhciReq);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync break;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync }
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync if ((uint64_t)iATAPILBA + cSectors > pAhciPort->cTotalSectors)
{
/* Rate limited logging, one log line per second. For
* guests that insist on reading from places outside the
* valid area this often generates too many release log
* entries otherwise. */
static uint64_t s_uLastLogTS = 0;
if (RTTimeMilliTS() >= s_uLastLogTS + 1000)
{
LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (READ)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors));
s_uLastLogTS = RTTimeMilliTS();
}
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
break;
}
atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2048);
enmTxDir = AHCITXDIR_READ;
break;
}
case SCSI_READ_CD:
{
uint32_t cSectors, iATAPILBA;
if (pAhciPort->cNotifiedMediaChange > 0)
{
pAhciPort->cNotifiedMediaChange-- ;
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
break;
}
else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
{
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
break;
}
cSectors = (pbPacket[6] << 16) | (pbPacket[7] << 8) | pbPacket[8];
iATAPILBA = ataBE2H_U32(pbPacket + 2);
if (cSectors == 0)
{
atapiCmdOK(pAhciPort, pAhciReq);
break;
}
if ((uint64_t)iATAPILBA + cSectors > pAhciPort->cTotalSectors)
{
/* Rate limited logging, one log line per second. For
* guests that insist on reading from places outside the
* valid area this often generates too many release log
* entries otherwise. */
static uint64_t s_uLastLogTS = 0;
if (RTTimeMilliTS() >= s_uLastLogTS + 1000)
{
LogRel(("AHCI ATA: LUN#%d: CD-ROM block number %Ld invalid (READ CD)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA + cSectors));
s_uLastLogTS = RTTimeMilliTS();
}
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
break;
}
switch (pbPacket[9] & 0xf8)
{
case 0x00:
/* nothing */
atapiCmdOK(pAhciPort, pAhciReq);
break;
case 0x10:
/* normal read */
atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2048);
enmTxDir = AHCITXDIR_READ;
break;
case 0xf8:
/* read all data */
atapiReadSectors(pAhciPort, pAhciReq, iATAPILBA, cSectors, 2352);
enmTxDir = AHCITXDIR_READ;
break;
default:
LogRel(("AHCI ATAPI: LUN#%d: CD-ROM sector format not supported\n", pAhciPort->iLUN));
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
break;
}
break;
}
case SCSI_SEEK_10:
{
uint32_t iATAPILBA;
if (pAhciPort->cNotifiedMediaChange > 0)
{
pAhciPort->cNotifiedMediaChange-- ;
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
break;
}
else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
{
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
break;
}
iATAPILBA = ataBE2H_U32(pbPacket + 2);
if (iATAPILBA > pAhciPort->cTotalSectors)
{
/* Rate limited logging, one log line per second. For
* guests that insist on seeking to places outside the
* valid area this often generates too many release log
* entries otherwise. */
static uint64_t s_uLastLogTS = 0;
if (RTTimeMilliTS() >= s_uLastLogTS + 1000)
{
LogRel(("AHCI ATAPI: LUN#%d: CD-ROM block number %Ld invalid (SEEK)\n", pAhciPort->iLUN, (uint64_t)iATAPILBA));
s_uLastLogTS = RTTimeMilliTS();
}
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
break;
}
atapiCmdOK(pAhciPort, pAhciReq);
pAhciReq->uATARegStatus |= ATA_STAT_SEEK; /* Linux expects this. */
break;
}
case SCSI_START_STOP_UNIT:
{
int rc = VINF_SUCCESS;
switch (pbPacket[4] & 3)
{
case 0: /* 00 - Stop motor */
case 1: /* 01 - Start motor */
break;
case 2: /* 10 - Eject media */
{
/* This must be done from EMT. */
PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci);
PPDMDEVINS pDevIns = pAhci->CTX_SUFF(pDevIns);
rc = VMR3ReqPriorityCallWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
(PFNRT)pAhciPort->pDrvMount->pfnUnmount, 3,
pAhciPort->pDrvMount, false/*=fForce*/, true/*=fEject*/);
Assert(RT_SUCCESS(rc) || rc == VERR_PDM_MEDIA_LOCKED || rc == VERR_PDM_MEDIA_NOT_MOUNTED);
if (RT_SUCCESS(rc) && pAhci->pMediaNotify)
{
rc = VMR3ReqCallNoWait(PDMDevHlpGetVM(pDevIns), VMCPUID_ANY,
(PFNRT)pAhci->pMediaNotify->pfnEjected, 2,
pAhci->pMediaNotify, pAhciPort->iLUN);
AssertRC(rc);
}
break;
}
case 3: /* 11 - Load media */
/** @todo rc = s->pDrvMount->pfnLoadMedia(s->pDrvMount) */
break;
}
if (RT_SUCCESS(rc))
atapiCmdOK(pAhciPort, pAhciReq);
else
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED);
break;
}
case SCSI_MECHANISM_STATUS:
{
cbMax = ataBE2H_U16(pbPacket + 8);
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_MECHANISM_STATUS);
break;
}
case SCSI_READ_TOC_PMA_ATIP:
{
uint8_t format;
if (pAhciPort->cNotifiedMediaChange > 0)
{
pAhciPort->cNotifiedMediaChange-- ;
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
break;
}
else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
{
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
break;
}
cbMax = ataBE2H_U16(pbPacket + 7);
/* SCSI MMC-3 spec says format is at offset 2 (lower 4 bits),
* but Linux kernel uses offset 9 (topmost 2 bits). Hope that
* the other field is clear... */
format = (pbPacket[2] & 0xf) | (pbPacket[9] >> 6);
switch (format)
{
case 0:
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_NORMAL);
break;
case 1:
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_MULTI);
break;
case 2:
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TOC_RAW);
break;
default:
error_cmd:
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
break;
}
break;
}
case SCSI_READ_CAPACITY:
if (pAhciPort->cNotifiedMediaChange > 0)
{
pAhciPort->cNotifiedMediaChange-- ;
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
break;
}
else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
{
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
break;
}
atapiDoTransfer(pAhciPort, pAhciReq, 8 /* cbMax */, ATAFN_SS_ATAPI_READ_CAPACITY);
break;
case SCSI_READ_DISC_INFORMATION:
if (pAhciPort->cNotifiedMediaChange > 0)
{
pAhciPort->cNotifiedMediaChange-- ;
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
break;
}
else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
{
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
break;
}
cbMax = ataBE2H_U16(pbPacket + 7);
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_DISC_INFORMATION);
break;
case SCSI_READ_TRACK_INFORMATION:
if (pAhciPort->cNotifiedMediaChange > 0)
{
pAhciPort->cNotifiedMediaChange-- ;
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_UNIT_ATTENTION, SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED); /* media changed */
break;
}
else if (!pAhciPort->pDrvMount->pfnIsMounted(pAhciPort->pDrvMount))
{
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT);
break;
}
cbMax = ataBE2H_U16(pbPacket + 7);
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_TRACK_INFORMATION);
break;
case SCSI_GET_CONFIGURATION:
/* No media change stuff here, it can confuse Linux guests. */
cbMax = ataBE2H_U16(pbPacket + 7);
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_GET_CONFIGURATION);
break;
case SCSI_INQUIRY:
cbMax = pbPacket[4];
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_INQUIRY);
break;
case SCSI_READ_DVD_STRUCTURE:
cbMax = ataBE2H_U16(pbPacket + 8);
atapiDoTransfer(pAhciPort, pAhciReq, cbMax, ATAFN_SS_ATAPI_READ_DVD_STRUCTURE);
break;
default:
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
break;
}
return enmTxDir;
}
/*
* Parse ATAPI commands, passing them directly to the CD/DVD drive.
*/
static AHCITXDIR atapiParseCmdPassthrough(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
{
const uint8_t *pbPacket;
uint32_t cSectors, iATAPILBA;
uint32_t cbTransfer = 0;
AHCITXDIR enmTxDir = AHCITXDIR_NONE;
pbPacket = pAhciReq->aATAPICmd;
switch (pbPacket[0])
{
case SCSI_BLANK:
goto sendcmd;
case SCSI_CLOSE_TRACK_SESSION:
goto sendcmd;
case SCSI_ERASE_10:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cbTransfer = ataBE2H_U16(pbPacket + 7);
Log2(("ATAPI PT: lba %d\n", iATAPILBA));
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
case SCSI_FORMAT_UNIT:
cbTransfer = pAhciReq->cmdFis[AHCI_CMDFIS_CYLL] | (pAhciReq->cmdFis[AHCI_CMDFIS_CYLH] << 8); /* use ATAPI transfer length */
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
case SCSI_GET_CONFIGURATION:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_GET_EVENT_STATUS_NOTIFICATION:
cbTransfer = ataBE2H_U16(pbPacket + 7);
if (ASMAtomicReadU32(&pAhciPort->MediaEventStatus) != ATA_EVENT_STATUS_UNCHANGED)
{
pAhciReq->cbTransfer = RT_MIN(cbTransfer, 8);
atapiDoTransfer(pAhciPort, pAhciReq, pAhciReq->cbTransfer, ATAFN_SS_ATAPI_GET_EVENT_STATUS_NOTIFICATION);
break;
}
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_GET_PERFORMANCE:
cbTransfer = pAhciReq->cmdFis[AHCI_CMDFIS_CYLL] | (pAhciReq->cmdFis[AHCI_CMDFIS_CYLH] << 8); /* use ATAPI transfer length */
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_INQUIRY:
cbTransfer = ataBE2H_U16(pbPacket + 3);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_LOAD_UNLOAD_MEDIUM:
goto sendcmd;
case SCSI_MECHANISM_STATUS:
cbTransfer = ataBE2H_U16(pbPacket + 8);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_MODE_SELECT_10:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
case SCSI_MODE_SENSE_10:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_PAUSE_RESUME:
goto sendcmd;
case SCSI_PLAY_AUDIO_10:
goto sendcmd;
case SCSI_PLAY_AUDIO_12:
goto sendcmd;
case SCSI_PLAY_AUDIO_MSF:
goto sendcmd;
case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
/** @todo do not forget to unlock when a VM is shut down */
goto sendcmd;
case SCSI_READ_10:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U16(pbPacket + 7);
Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
pAhciReq->cbATAPISector = 2048; /**< @todo this size is not always correct */
cbTransfer = cSectors * pAhciReq->cbATAPISector;
enmTxDir = AHCITXDIR_READ;
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));
pAhciReq->cbATAPISector = 2048; /**< @todo this size is not always correct */
cbTransfer = cSectors * pAhciReq->cbATAPISector;
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_READ_BUFFER:
cbTransfer = ataBE2H_U24(pbPacket + 6);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_READ_BUFFER_CAPACITY:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_READ_CAPACITY:
cbTransfer = 8;
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_READ_CD:
case SCSI_READ_CD_MSF:
{
/* Get sector size based on the expected sector type field. */
switch ((pbPacket[1] >> 2) & 0x7)
{
case 0x0: /* All types. */
{
uint32_t iLbaStart;
if (pbPacket[0] == SCSI_READ_CD)
iLbaStart = ataBE2H_U32(&pbPacket[2]);
else
iLbaStart = ataMSF2LBA(&pbPacket[3]);
if (pAhciPort->pTrackList)
pAhciReq->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pAhciPort->pTrackList, iLbaStart);
else
pAhciReq->cbATAPISector = 2048; /* Might be incorrect if we couldn't determine the type. */
break;
}
case 0x1: /* CD-DA */
pAhciReq->cbATAPISector = 2352;
break;
case 0x2: /* Mode 1 */
pAhciReq->cbATAPISector = 2048;
break;
case 0x3: /* Mode 2 formless */
pAhciReq->cbATAPISector = 2336;
break;
case 0x4: /* Mode 2 form 1 */
pAhciReq->cbATAPISector = 2048;
break;
case 0x5: /* Mode 2 form 2 */
pAhciReq->cbATAPISector = 2324;
break;
default: /* Reserved */
AssertMsgFailed(("Unknown sector type\n"));
pAhciReq->cbATAPISector = 0; /** @todo we should probably fail the command here already. */
}
if (pbPacket[0] == SCSI_READ_CD)
cbTransfer = ataBE2H_U24(pbPacket + 6) * pAhciReq->cbATAPISector;
else /* SCSI_READ_MSF */
{
cSectors = ataMSF2LBA(pbPacket + 6) - ataMSF2LBA(pbPacket + 3);
if (cSectors > 32)
cSectors = 32; /* Limit transfer size to 64~74K. Safety first. In any case this can only harm software doing CDDA extraction. */
cbTransfer = cSectors * pAhciReq->cbATAPISector;
}
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
}
case SCSI_READ_DISC_INFORMATION:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_READ_DVD_STRUCTURE:
cbTransfer = ataBE2H_U16(pbPacket + 8);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_READ_FORMAT_CAPACITIES:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_READ_SUBCHANNEL:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_READ_TOC_PMA_ATIP:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_READ_TRACK_INFORMATION:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_REPAIR_TRACK:
goto sendcmd;
case SCSI_REPORT_KEY:
cbTransfer = ataBE2H_U16(pbPacket + 8);
enmTxDir = AHCITXDIR_READ;
goto sendcmd;
case SCSI_REQUEST_SENSE:
cbTransfer = pbPacket[4];
if ((pAhciPort->abATAPISense[2] & 0x0f) != SCSI_SENSE_NONE)
{
pAhciReq->cbTransfer = cbTransfer;
pAhciReq->enmTxDir = AHCITXDIR_READ;
atapiDoTransfer(pAhciPort, pAhciReq, cbTransfer, ATAFN_SS_ATAPI_REQUEST_SENSE);
break;
}
enmTxDir = AHCITXDIR_READ;
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);
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
case SCSI_SEND_DVD_STRUCTURE:
cbTransfer = ataBE2H_U16(pbPacket + 8);
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
case SCSI_SEND_EVENT:
cbTransfer = ataBE2H_U16(pbPacket + 8);
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
case SCSI_SEND_KEY:
cbTransfer = ataBE2H_U16(pbPacket + 8);
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
case SCSI_SEND_OPC_INFORMATION:
cbTransfer = ataBE2H_U16(pbPacket + 7);
enmTxDir = AHCITXDIR_WRITE;
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);
enmTxDir = AHCITXDIR_WRITE;
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:
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));
if (pAhciPort->pTrackList)
pAhciReq->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pAhciPort->pTrackList, iATAPILBA);
else
pAhciReq->cbATAPISector = 2048;
cbTransfer = cSectors * pAhciReq->cbATAPISector;
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
case SCSI_WRITE_12:
iATAPILBA = ataBE2H_U32(pbPacket + 2);
cSectors = ataBE2H_U32(pbPacket + 6);
Log2(("ATAPI PT: lba %d sectors %d\n", iATAPILBA, cSectors));
if (pAhciPort->pTrackList)
pAhciReq->cbATAPISector = ATAPIPassthroughTrackListGetSectorSizeFromLba(pAhciPort->pTrackList, iATAPILBA);
else
pAhciReq->cbATAPISector = 2048;
cbTransfer = cSectors * pAhciReq->cbATAPISector;
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
case SCSI_WRITE_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", pAhciPort->iLUN));
atapiCmdErrorSimple(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
break;
default:
cbTransfer = ataBE2H_U16(pbPacket + 6);
enmTxDir = AHCITXDIR_WRITE;
goto sendcmd;
}
break;
case SCSI_REPORT_LUNS: /* Not part of MMC-3, but used by Windows. */
cbTransfer = ataBE2H_U32(pbPacket + 6);
enmTxDir = AHCITXDIR_READ;
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(pAhciPort, pAhciReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
break;
default:
LogRel(("AHCI: LUN#%d: passthrough unimplemented for command %#x\n", pAhciPort->iLUN, pbPacket[0]));
atapiCmdErrorSimple(pAhciPort, pAhciReq, 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));
if (cbTransfer == 0)
enmTxDir = AHCITXDIR_NONE;
pAhciReq->enmTxDir = enmTxDir;
pAhciReq->cbTransfer = cbTransfer;
atapiDoTransfer(pAhciPort, pAhciReq, cbTransfer, ATAFN_SS_ATAPI_PASSTHROUGH);
}
return AHCITXDIR_NONE;
}
static AHCITXDIR atapiParseCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
{
AHCITXDIR enmTxDir = AHCITXDIR_NONE;
const uint8_t *pbPacket;
pbPacket = pAhciReq->aATAPICmd;
Log(("%s: LUN#%d CMD=%#04x \"%s\"\n", __FUNCTION__, pAhciPort->iLUN, pbPacket[0], SCSICmdText(pbPacket[0])));
Log2(("%s: limit=%#x packet: %.*Rhxs\n", __FUNCTION__, pAhciReq->cmdFis[AHCI_CMDFIS_CYLL] | (pAhciReq->cmdFis[AHCI_CMDFIS_CYLH] << 8), ATAPI_PACKET_SIZE, pbPacket));
if (pAhciPort->fATAPIPassthrough)
enmTxDir = atapiParseCmdPassthrough(pAhciPort, pAhciReq);
else
enmTxDir = atapiParseCmdVirtualATAPI(pAhciPort, pAhciReq);
return enmTxDir;
}
/**
* Reset all values after a reset of the attached storage device.
*
* @returns nothing
* @param pAhciPort The port the device is attached to.
* @param pAhciReq The state to get the tag number from.
*/
static void ahciFinishStorageDeviceReset(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
{
int rc;
/* Send a status good D2H FIS. */
ASMAtomicWriteU32(&pAhciPort->MediaEventStatus, ATA_EVENT_STATUS_UNCHANGED);
pAhciPort->fResetDevice = false;
if (pAhciPort->regCMD & AHCI_PORT_CMD_FRE)
ahciPostFirstD2HFisIntoMemory(pAhciPort);
/* As this is the first D2H FIS after the reset update the signature in the SIG register of the port. */
if (pAhciPort->fATAPI)
pAhciPort->regSIG = AHCI_PORT_SIG_ATAPI;
else
pAhciPort->regSIG = AHCI_PORT_SIG_DISK;
ASMAtomicOrU32(&pAhciPort->u32TasksFinished, (1 << pAhciReq->uTag));
rc = ahciHbaSetInterrupt(pAhciPort->CTX_SUFF(pAhci), pAhciPort->iLUN, VERR_IGNORED);
AssertRC(rc);
}
/**
* Initiates a device reset caused by ATA_DEVICE_RESET (ATAPI only).
*
* @returns nothing.
* @param pAhciPort The device to reset.
* @param pAhciReq The task state.
*/
static void ahciDeviceReset(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
{
ASMAtomicWriteBool(&pAhciPort->fResetDevice, true);
/*
* Because this ATAPI only and ATAPI can't have
* more than one command active at a time the task counter should be 0
* and it is possible to finish the reset now.
*/
Assert(ASMAtomicReadU32(&pAhciPort->cTasksActive) == 0);
ahciFinishStorageDeviceReset(pAhciPort, pAhciReq);
}
/**
* Create a PIO setup FIS and post it into the memory area of the guest.
*
* @returns nothing.
* @param pAhciPort The port of the SATA controller.
* @param pAhciReq The state of the task.
* @param pCmdFis Pointer to the command FIS from the guest.
* @param fInterrupt If an interrupt should be send to the guest.
*/
static void ahciSendPioSetupFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t *pCmdFis,
bool fInterrupt)
{
uint8_t abPioSetupFis[20];
bool fAssertIntr = false;
PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci);
ahciLog(("%s: building PIO setup Fis\n", __FUNCTION__));
AssertMsg( pAhciReq->cbTransfer > 0
&& pAhciReq->cbTransfer <= 65534,
("Can't send PIO setup FIS for requests with 0 bytes to transfer or greater than 65534\n"));
if (pAhciPort->regCMD & AHCI_PORT_CMD_FRE)
{
memset(&abPioSetupFis[0], 0, sizeof(abPioSetupFis));
abPioSetupFis[AHCI_CMDFIS_TYPE] = AHCI_CMDFIS_TYPE_PIOSETUP;
abPioSetupFis[AHCI_CMDFIS_BITS] = (fInterrupt ? AHCI_CMDFIS_I : 0);
if (pAhciReq->enmTxDir == AHCITXDIR_READ)
abPioSetupFis[AHCI_CMDFIS_BITS] |= AHCI_CMDFIS_D;
abPioSetupFis[AHCI_CMDFIS_STS] = pAhciReq->uATARegStatus;
abPioSetupFis[AHCI_CMDFIS_ERR] = pAhciReq->uATARegError;
abPioSetupFis[AHCI_CMDFIS_SECTN] = pCmdFis[AHCI_CMDFIS_SECTN];
abPioSetupFis[AHCI_CMDFIS_CYLL] = pCmdFis[AHCI_CMDFIS_CYLL];
abPioSetupFis[AHCI_CMDFIS_CYLH] = pCmdFis[AHCI_CMDFIS_CYLH];
abPioSetupFis[AHCI_CMDFIS_HEAD] = pCmdFis[AHCI_CMDFIS_HEAD];
abPioSetupFis[AHCI_CMDFIS_SECTNEXP] = pCmdFis[AHCI_CMDFIS_SECTNEXP];
abPioSetupFis[AHCI_CMDFIS_CYLLEXP] = pCmdFis[AHCI_CMDFIS_CYLLEXP];
abPioSetupFis[AHCI_CMDFIS_CYLHEXP] = pCmdFis[AHCI_CMDFIS_CYLHEXP];
abPioSetupFis[AHCI_CMDFIS_SECTC] = pCmdFis[AHCI_CMDFIS_SECTC];
abPioSetupFis[AHCI_CMDFIS_SECTCEXP] = pCmdFis[AHCI_CMDFIS_SECTCEXP];
/* Set transfer count. */
abPioSetupFis[16] = (pAhciReq->cbTransfer >> 8) & 0xff;
abPioSetupFis[17] = pAhciReq->cbTransfer & 0xff;
/* Update registers. */
pAhciPort->regTFD = (pAhciReq->uATARegError << 8) | pAhciReq->uATARegStatus;
ahciPostFisIntoMemory(pAhciPort, AHCI_CMDFIS_TYPE_PIOSETUP, abPioSetupFis);
if (fInterrupt)
{
ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_PSS);
/* Check if we should assert an interrupt */
if (pAhciPort->regIE & AHCI_PORT_IE_PSE)
fAssertIntr = true;
}
if (fAssertIntr)
{
int rc = ahciHbaSetInterrupt(pAhci, pAhciPort->iLUN, VERR_IGNORED);
AssertRC(rc);
}
}
}
/**
* Build a D2H FIS and post into the memory area of the guest.
*
* @returns Nothing
* @param pAhciPort The port of the SATA controller.
* @param pAhciReq The state of the task.
* @param pCmdFis Pointer to the command FIS from the guest.
* @param fInterrupt If an interrupt should be send to the guest.
*/
static void ahciSendD2HFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t *pCmdFis, bool fInterrupt)
{
uint8_t d2hFis[20];
bool fAssertIntr = false;
PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci);
ahciLog(("%s: building D2H Fis\n", __FUNCTION__));
if (pAhciPort->regCMD & AHCI_PORT_CMD_FRE)
{
memset(&d2hFis[0], 0, sizeof(d2hFis));
d2hFis[AHCI_CMDFIS_TYPE] = AHCI_CMDFIS_TYPE_D2H;
d2hFis[AHCI_CMDFIS_BITS] = (fInterrupt ? AHCI_CMDFIS_I : 0);
d2hFis[AHCI_CMDFIS_STS] = pAhciReq->uATARegStatus;
d2hFis[AHCI_CMDFIS_ERR] = pAhciReq->uATARegError;
d2hFis[AHCI_CMDFIS_SECTN] = pCmdFis[AHCI_CMDFIS_SECTN];
d2hFis[AHCI_CMDFIS_CYLL] = pCmdFis[AHCI_CMDFIS_CYLL];
d2hFis[AHCI_CMDFIS_CYLH] = pCmdFis[AHCI_CMDFIS_CYLH];
d2hFis[AHCI_CMDFIS_HEAD] = pCmdFis[AHCI_CMDFIS_HEAD];
d2hFis[AHCI_CMDFIS_SECTNEXP] = pCmdFis[AHCI_CMDFIS_SECTNEXP];
d2hFis[AHCI_CMDFIS_CYLLEXP] = pCmdFis[AHCI_CMDFIS_CYLLEXP];
d2hFis[AHCI_CMDFIS_CYLHEXP] = pCmdFis[AHCI_CMDFIS_CYLHEXP];
d2hFis[AHCI_CMDFIS_SECTC] = pCmdFis[AHCI_CMDFIS_SECTC];
d2hFis[AHCI_CMDFIS_SECTCEXP] = pCmdFis[AHCI_CMDFIS_SECTCEXP];
/* Update registers. */
pAhciPort->regTFD = (pAhciReq->uATARegError << 8) | pAhciReq->uATARegStatus;
ahciPostFisIntoMemory(pAhciPort, AHCI_CMDFIS_TYPE_D2H, d2hFis);
if (pAhciReq->uATARegStatus & ATA_STAT_ERR)
{
/* Error bit is set. */
ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_TFES);
if (pAhciPort->regIE & AHCI_PORT_IE_TFEE)
fAssertIntr = true;
/*
* Don't mark the command slot as completed because the guest
* needs it to identify the failed command.
*/
}
else if (fInterrupt)
{
ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_DHRS);
/* Check if we should assert an interrupt */
if (pAhciPort->regIE & AHCI_PORT_IE_DHRE)
fAssertIntr = true;
/* Mark command as completed. */
ASMAtomicOrU32(&pAhciPort->u32TasksFinished, (1 << pAhciReq->uTag));
}
if (fAssertIntr)
{
int rc = ahciHbaSetInterrupt(pAhci, pAhciPort->iLUN, VERR_IGNORED);
AssertRC(rc);
}
}
}
/**
* Build a SDB Fis and post it into the memory area of the guest.
*
* @returns Nothing
* @param pAhciPort The port for which the SDB Fis is send.
* @param uFinishedTasks Bitmask of finished tasks.
* @param fInterrupt If an interrupt should be asserted.
*/
static void ahciSendSDBFis(PAHCIPort pAhciPort, uint32_t uFinishedTasks, bool fInterrupt)
{
uint32_t sdbFis[2];
bool fAssertIntr = false;
PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci);
PAHCIREQ pTaskErr = ASMAtomicReadPtrT(&pAhciPort->pTaskErr, PAHCIREQ);
ahciLog(("%s: Building SDB FIS\n", __FUNCTION__));
if (pAhciPort->regCMD & AHCI_PORT_CMD_FRE)
{
memset(&sdbFis[0], 0, sizeof(sdbFis));
sdbFis[0] = AHCI_CMDFIS_TYPE_SETDEVBITS;
sdbFis[0] |= (fInterrupt ? (1 << 14) : 0);
if (RT_UNLIKELY(pTaskErr))
{
sdbFis[0] = pTaskErr->uATARegError;
sdbFis[0] |= (pTaskErr->uATARegStatus & 0x77) << 16; /* Some bits are marked as reserved and thus are masked out. */
/* Update registers. */
pAhciPort->regTFD = (pTaskErr->uATARegError << 8) | pTaskErr->uATARegStatus;
}
else
{
sdbFis[0] = 0;
sdbFis[0] |= (ATA_STAT_READY | ATA_STAT_SEEK) << 16;
pAhciPort->regTFD = ATA_STAT_READY | ATA_STAT_SEEK;
}
sdbFis[1] = pAhciPort->u32QueuedTasksFinished | uFinishedTasks;
ahciPostFisIntoMemory(pAhciPort, AHCI_CMDFIS_TYPE_SETDEVBITS, (uint8_t *)sdbFis);
if (RT_UNLIKELY(pTaskErr))
{
/* Error bit is set. */
ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_TFES);
if (pAhciPort->regIE & AHCI_PORT_IE_TFEE)
fAssertIntr = true;
}
if (fInterrupt)
{
ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_SDBS);
/* Check if we should assert an interrupt */
if (pAhciPort->regIE & AHCI_PORT_IE_SDBE)
fAssertIntr = true;
}
ASMAtomicOrU32(&pAhciPort->u32QueuedTasksFinished, uFinishedTasks);
if (fAssertIntr)
{
int rc = ahciHbaSetInterrupt(pAhci, pAhciPort->iLUN, VERR_IGNORED);
AssertRC(rc);
}
}
}
static uint32_t ahciGetNSectors(uint8_t *pCmdFis, bool fLBA48)
{
/* 0 means either 256 (LBA28) or 65536 (LBA48) sectors. */
if (fLBA48)
{
if (!pCmdFis[AHCI_CMDFIS_SECTC] && !pCmdFis[AHCI_CMDFIS_SECTCEXP])
return 65536;
else
return pCmdFis[AHCI_CMDFIS_SECTCEXP] << 8 | pCmdFis[AHCI_CMDFIS_SECTC];
}
else
{
if (!pCmdFis[AHCI_CMDFIS_SECTC])
return 256;
else
return pCmdFis[AHCI_CMDFIS_SECTC];
}
}
static uint64_t ahciGetSector(PAHCIPort pAhciPort, uint8_t *pCmdFis, bool fLBA48)
{
uint64_t iLBA;
if (pCmdFis[AHCI_CMDFIS_HEAD] & 0x40)
{
/* any LBA variant */
if (fLBA48)
{
/* LBA48 */
iLBA = ((uint64_t)pCmdFis[AHCI_CMDFIS_CYLHEXP] << 40) |
((uint64_t)pCmdFis[AHCI_CMDFIS_CYLLEXP] << 32) |
((uint64_t)pCmdFis[AHCI_CMDFIS_SECTNEXP] << 24) |
((uint64_t)pCmdFis[AHCI_CMDFIS_CYLH] << 16) |
((uint64_t)pCmdFis[AHCI_CMDFIS_CYLL] << 8) |
pCmdFis[AHCI_CMDFIS_SECTN];
}
else
{
/* LBA */
iLBA = ((pCmdFis[AHCI_CMDFIS_HEAD] & 0x0f) << 24) | (pCmdFis[AHCI_CMDFIS_CYLH] << 16) |
(pCmdFis[AHCI_CMDFIS_CYLL] << 8) | pCmdFis[AHCI_CMDFIS_SECTN];
}
}
else
{
/* CHS */
iLBA = ((pCmdFis[AHCI_CMDFIS_CYLH] << 8) | pCmdFis[AHCI_CMDFIS_CYLL]) * pAhciPort->PCHSGeometry.cHeads * pAhciPort->PCHSGeometry.cSectors +
(pCmdFis[AHCI_CMDFIS_HEAD] & 0x0f) * pAhciPort->PCHSGeometry.cSectors +
(pCmdFis[AHCI_CMDFIS_SECTN] - 1);
}
return iLBA;
}
static uint64_t ahciGetSectorQueued(uint8_t *pCmdFis)
{
uint64_t uLBA;
uLBA = ((uint64_t)pCmdFis[AHCI_CMDFIS_CYLHEXP] << 40) |
((uint64_t)pCmdFis[AHCI_CMDFIS_CYLLEXP] << 32) |
((uint64_t)pCmdFis[AHCI_CMDFIS_SECTNEXP] << 24) |
((uint64_t)pCmdFis[AHCI_CMDFIS_CYLH] << 16) |
((uint64_t)pCmdFis[AHCI_CMDFIS_CYLL] << 8) |
pCmdFis[AHCI_CMDFIS_SECTN];
return uLBA;
}
DECLINLINE(uint32_t) ahciGetNSectorsQueued(uint8_t *pCmdFis)
{
if (!pCmdFis[AHCI_CMDFIS_FETEXP] && !pCmdFis[AHCI_CMDFIS_FET])
return 65536;
else
return pCmdFis[AHCI_CMDFIS_FETEXP] << 8 | pCmdFis[AHCI_CMDFIS_FET];
}
DECLINLINE(uint8_t) ahciGetTagQueued(uint8_t *pCmdFis)
{
return pCmdFis[AHCI_CMDFIS_SECTC] >> 3;
}
/**
* Allocates memory for the given request using already allocated memory if possible.
*
* @returns Pointer to the memory or NULL on failure
* @param pAhciReq The request to allocate memory for.
* @param cb The amount of memory to allocate.
*/
static void *ahciReqMemAlloc(PAHCIREQ pAhciReq, size_t cb)
{
if (pAhciReq->cbAlloc > cb)
{
pAhciReq->cAllocTooMuch++;
}
else if (pAhciReq->cbAlloc < cb)
{
if (pAhciReq->cbAlloc)
RTMemPageFree(pAhciReq->pvAlloc, pAhciReq->cbAlloc);
pAhciReq->cbAlloc = RT_ALIGN_Z(cb, _4K);
pAhciReq->pvAlloc = RTMemPageAlloc(pAhciReq->cbAlloc);
pAhciReq->cAllocTooMuch = 0;
if (RT_UNLIKELY(!pAhciReq->pvAlloc))
pAhciReq->cbAlloc = 0;
}
return pAhciReq->pvAlloc;
}
/**
* Frees memory allocated for the given request.
*
* @returns nothing.
* @param pAhciReq The request.
*/
static void ahciReqMemFree(PAHCIREQ pAhciReq)
{
if (pAhciReq->cAllocTooMuch >= AHCI_MAX_ALLOC_TOO_MUCH)
{
RTMemPageFree(pAhciReq->pvAlloc, pAhciReq->cbAlloc);
pAhciReq->cbAlloc = 0;
pAhciReq->cAllocTooMuch = 0;
}
}
/**
* Copies a data buffer into the S/G buffer set up by the guest.
*
* @returns Amount of bytes copied to the PRDTL.
* @param pDevIns Pointer to the device instance data.
* @param pAhciReq AHCI request structure.
* @param pvBuf The buffer to copy from.
* @param cbBuf The size of the buffer.
*/
static size_t ahciCopyToPrdtl(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq,
void *pvBuf, size_t cbBuf)
{
uint8_t *pbBuf = (uint8_t *)pvBuf;
SGLEntry aPrdtlEntries[32];
RTGCPHYS GCPhysPrdtl = pAhciReq->GCPhysPrdtl;
unsigned cPrdtlEntries = pAhciReq->cPrdtlEntries;
size_t cbCopied = 0;
AssertMsgReturn(cPrdtlEntries > 0, ("Copying 0 bytes is not possible\n"), 0);
do
{
uint32_t cPrdtlEntriesRead = cPrdtlEntries < RT_ELEMENTS(aPrdtlEntries)
? cPrdtlEntries
: RT_ELEMENTS(aPrdtlEntries);
PDMDevHlpPhysRead(pDevIns, GCPhysPrdtl, &aPrdtlEntries[0], cPrdtlEntriesRead * sizeof(SGLEntry));
for (uint32_t i = 0; (i < cPrdtlEntriesRead) && cbBuf; i++)
{
RTGCPHYS GCPhysAddrDataBase = AHCI_RTGCPHYS_FROM_U32(aPrdtlEntries[i].u32DBAUp, aPrdtlEntries[i].u32DBA);
uint32_t cbThisCopy = (aPrdtlEntries[i].u32DescInf & SGLENTRY_DESCINF_DBC) + 1;
cbThisCopy = RT_MIN(cbThisCopy, cbBuf);
/* Copy into SG entry. */
PDMDevHlpPCIPhysWrite(pDevIns, GCPhysAddrDataBase, pbBuf, cbThisCopy);
pbBuf += cbThisCopy;
cbBuf -= cbThisCopy;
cbCopied += cbThisCopy;
}
GCPhysPrdtl += cPrdtlEntriesRead * sizeof(SGLEntry);
cPrdtlEntries -= cPrdtlEntriesRead;
} while (cPrdtlEntries && cbBuf);
if (cbCopied < cbBuf)
pAhciReq->fFlags |= AHCI_REQ_OVERFLOW;
return cbCopied;
}
/**
* Copies the S/G buffer into a data buffer.
*
* @returns Amount of bytes copied to the PRDTL.
* @param pDevIns Pointer to the device instance data.
* @param pAhciReq AHCI request structure.
* @param pvBuf The buffer to copy to.
* @param cbBuf The size of the buffer.
*/
static size_t ahciCopyFromPrdtl(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq,
void *pvBuf, size_t cbBuf)
{
uint8_t *pbBuf = (uint8_t *)pvBuf;
SGLEntry aPrdtlEntries[32];
RTGCPHYS GCPhysPrdtl = pAhciReq->GCPhysPrdtl;
unsigned cPrdtlEntries = pAhciReq->cPrdtlEntries;
size_t cbCopied = 0;
AssertMsgReturn(cPrdtlEntries > 0, ("Copying 0 bytes is not possible\n"), 0);
do
{
uint32_t cPrdtlEntriesRead = (cPrdtlEntries < RT_ELEMENTS(aPrdtlEntries))
? cPrdtlEntries
: RT_ELEMENTS(aPrdtlEntries);
PDMDevHlpPhysRead(pDevIns, GCPhysPrdtl, &aPrdtlEntries[0], cPrdtlEntriesRead * sizeof(SGLEntry));
for (uint32_t i = 0; (i < cPrdtlEntriesRead) && cbBuf; i++)
{
RTGCPHYS GCPhysAddrDataBase = AHCI_RTGCPHYS_FROM_U32(aPrdtlEntries[i].u32DBAUp, aPrdtlEntries[i].u32DBA);
uint32_t cbThisCopy = (aPrdtlEntries[i].u32DescInf & SGLENTRY_DESCINF_DBC) + 1;
cbThisCopy = RT_MIN(cbThisCopy, cbBuf);
/* Copy into buffer. */
PDMDevHlpPhysRead(pDevIns, GCPhysAddrDataBase, pbBuf, cbThisCopy);
pbBuf += cbThisCopy;
cbBuf -= cbThisCopy;
cbCopied += cbThisCopy;
}
GCPhysPrdtl += cPrdtlEntriesRead * sizeof(SGLEntry);
cPrdtlEntries -= cPrdtlEntriesRead;
} while (cPrdtlEntries && cbBuf);
if (cbCopied < cbBuf)
pAhciReq->fFlags |= AHCI_REQ_OVERFLOW;
return cbCopied;
}
/**
* Allocate I/O memory and copies the guest buffer for writes.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pAhciReq The request state.
* @param cbTransfer Amount of bytes to allocate.
*/
static int ahciIoBufAllocate(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq, size_t cbTransfer)
{
AssertMsg( pAhciReq->enmTxDir == AHCITXDIR_READ
|| pAhciReq->enmTxDir == AHCITXDIR_WRITE,
("Allocating I/O memory for a non I/O request is not allowed\n"));
pAhciReq->u.Io.DataSeg.pvSeg = ahciReqMemAlloc(pAhciReq, cbTransfer);
if (!pAhciReq->u.Io.DataSeg.pvSeg)
return VERR_NO_MEMORY;
pAhciReq->u.Io.DataSeg.cbSeg = cbTransfer;
if (pAhciReq->enmTxDir == AHCITXDIR_WRITE)
{
ahciCopyFromPrdtl(pDevIns, pAhciReq,
pAhciReq->u.Io.DataSeg.pvSeg,
cbTransfer);
}
return VINF_SUCCESS;
}
/**
* Frees the I/O memory of the given request and updates the guest buffer if necessary.
*
* @returns nothing.
* @param pDevIns The device instance.
* @param pAhciReq The request state.
* @param fCopyToGuest Flag whether to update the guest buffer if necessary.
* Nothing is copied if false even if the request was a read.
*/
static void ahciIoBufFree(PPDMDEVINS pDevIns, PAHCIREQ pAhciReq,
bool fCopyToGuest)
{
AssertMsg( pAhciReq->enmTxDir == AHCITXDIR_READ
|| pAhciReq->enmTxDir == AHCITXDIR_WRITE,
("Freeing I/O memory for a non I/O request is not allowed\n"));
if ( pAhciReq->enmTxDir == AHCITXDIR_READ
&& fCopyToGuest)
{
if (pAhciReq->u.Io.pfnPostProcess)
{
void *pv = NULL;
size_t cb = 0;
int rc = pAhciReq->u.Io.pfnPostProcess(pAhciReq, &pv, &cb);
if (RT_SUCCESS(rc))
{
pAhciReq->cbTransfer = ahciCopyToPrdtl(pDevIns, pAhciReq, pv, cb);
RTMemFree(pv);
}
}
else
ahciCopyToPrdtl(pDevIns, pAhciReq,
pAhciReq->u.Io.DataSeg.pvSeg,
pAhciReq->u.Io.DataSeg.cbSeg);
}
ahciReqMemFree(pAhciReq);
pAhciReq->u.Io.DataSeg.pvSeg = NULL;
pAhciReq->u.Io.DataSeg.cbSeg = 0;
}
/**
* Cancels all active tasks on the port.
*
* @returns Whether all active tasks were canceled.
* @param pAhciPort The ahci port.
*/
static bool ahciCancelActiveTasks(PAHCIPort pAhciPort)
{
for (unsigned i = 0; i < RT_ELEMENTS(pAhciPort->aCachedTasks); i++)
{
PAHCIREQ pAhciReq = pAhciPort->aCachedTasks[i];
if (VALID_PTR(pAhciReq))
{
bool fXchg = false;
ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_CANCELED, AHCITXSTATE_ACTIVE, fXchg);
if (fXchg)
{
/* Task is active and was canceled. */
AssertReleaseMsg(ASMAtomicReadU32(&pAhciPort->cTasksActive) > 0,
("Task was canceled but none is active\n"));
ASMAtomicDecU32(&pAhciPort->cTasksActive);
/*
* Clear the pointer in the cached array. The controller will allocate a
* a new task structure for this tag.
*/
ASMAtomicWriteNullPtr(&pAhciPort->aCachedTasks[i]);
LogRel(("AHCI#%uP%u: Cancelled task %u\n", pAhciPort->CTX_SUFF(pDevIns)->iInstance,
pAhciPort->iLUN, pAhciReq->uTag));
}
else
AssertMsg(pAhciReq->enmTxState == AHCITXSTATE_FREE,
("Invalid task state, must be free!\n"));
}
}
AssertRelease(!ASMAtomicReadU32(&pAhciPort->cTasksActive));
return true; /* always true for now because tasks don't use guest memory as the buffer which makes canceling a task impossible. */
}
/* -=-=-=-=- IBlockAsyncPort -=-=-=-=- */
/** Makes a PAHCIPort out of a PPDMIBLOCKASYNCPORT. */
#define PDMIBLOCKASYNCPORT_2_PAHCIPORT(pInterface) ( (PAHCIPort)((uintptr_t)pInterface - RT_OFFSETOF(AHCIPort, IPortAsync)) )
static void ahciWarningDiskFull(PPDMDEVINS pDevIns)
{
int rc;
LogRel(("AHCI: Host disk full\n"));
rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevAHCI_DISKFULL",
N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
AssertRC(rc);
}
static void ahciWarningFileTooBig(PPDMDEVINS pDevIns)
{
int rc;
LogRel(("AHCI: File too big\n"));
rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevAHCI_FILETOOBIG",
N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files"));
AssertRC(rc);
}
static void ahciWarningISCSI(PPDMDEVINS pDevIns)
{
int rc;
LogRel(("AHCI: iSCSI target unavailable\n"));
rc = PDMDevHlpVMSetRuntimeError(pDevIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DevAHCI_ISCSIDOWN",
N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
AssertRC(rc);
}
bool ahciIsRedoSetWarning(PAHCIPort pAhciPort, int rc)
{
if (rc == VERR_DISK_FULL)
{
if (ASMAtomicCmpXchgBool(&pAhciPort->fRedo, true, false))
ahciWarningDiskFull(pAhciPort->CTX_SUFF(pDevIns));
return true;
}
if (rc == VERR_FILE_TOO_BIG)
{
if (ASMAtomicCmpXchgBool(&pAhciPort->fRedo, true, false))
ahciWarningFileTooBig(pAhciPort->CTX_SUFF(pDevIns));
return true;
}
if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
{
/* iSCSI connection abort (first error) or failure to reestablish
* connection (second error). Pause VM. On resume we'll retry. */
if (ASMAtomicCmpXchgBool(&pAhciPort->fRedo, true, false))
ahciWarningISCSI(pAhciPort->CTX_SUFF(pDevIns));
return true;
}
if (rc == VERR_VD_DEK_MISSING)
{
/* Error message already set. */
ASMAtomicCmpXchgBool(&pAhciPort->fRedo, true, false);
return true;
}
return false;
}
/**
* Creates the array of ranges to trim.
*
* @returns VBox status code.
* @param pAhciPort AHCI port state.
* @param pAhciReq The request handling the TRIM request.
*/
static int ahciTrimRangesCreate(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
{
SGLEntry aPrdtlEntries[32];
uint64_t aRanges[64];
unsigned cRangesMax;
unsigned cRanges = 0;
uint32_t cPrdtlEntries = pAhciReq->cPrdtlEntries;
RTGCPHYS GCPhysPrdtl = pAhciReq->GCPhysPrdtl;
PPDMDEVINS pDevIns = pAhciPort->CTX_SUFF(pDevIns);
int rc = VINF_SUCCESS;
LogFlowFunc(("pAhciPort=%#p pAhciReq=%#p\n", pAhciPort, pAhciReq));
AssertMsgReturn(pAhciReq->enmTxDir == AHCITXDIR_TRIM, ("This is not a trim request\n"), VERR_INVALID_PARAMETER);
/* The data buffer contains LBA range entries. Each range is 8 bytes big. */
if (!pAhciReq->cmdFis[AHCI_CMDFIS_SECTC] && !pAhciReq->cmdFis[AHCI_CMDFIS_SECTCEXP])
cRangesMax = 65536 * 512 / 8;
else
cRangesMax = pAhciReq->cmdFis[AHCI_CMDFIS_SECTC] * 512 / 8;
if (!cPrdtlEntries)
{
pAhciReq->fFlags |= AHCI_REQ_OVERFLOW;
return VINF_SUCCESS;
}
do
{
uint32_t cPrdtlEntriesRead = (cPrdtlEntries < RT_ELEMENTS(aPrdtlEntries))
? cPrdtlEntries
: RT_ELEMENTS(aPrdtlEntries);
PDMDevHlpPhysRead(pDevIns, GCPhysPrdtl, &aPrdtlEntries[0], cPrdtlEntriesRead * sizeof(SGLEntry));
for (uint32_t i = 0; i < cPrdtlEntriesRead; i++)
{
RTGCPHYS GCPhysAddrDataBase = AHCI_RTGCPHYS_FROM_U32(aPrdtlEntries[i].u32DBAUp, aPrdtlEntries[i].u32DBA);
uint32_t cbThisCopy = (aPrdtlEntries[i].u32DescInf & SGLENTRY_DESCINF_DBC) + 1;
cbThisCopy = RT_MIN(cbThisCopy, sizeof(aRanges));
/* Copy into buffer. */
PDMDevHlpPhysRead(pDevIns, GCPhysAddrDataBase, aRanges, cbThisCopy);
for (unsigned idxRange = 0; idxRange < RT_ELEMENTS(aRanges); idxRange++)
{
aRanges[idxRange] = RT_H2LE_U64(aRanges[idxRange]);
if (AHCI_RANGE_LENGTH_GET(aRanges[idxRange]) != 0)
cRanges++;
else
break;
}
}
GCPhysPrdtl += cPrdtlEntriesRead * sizeof(SGLEntry);
cPrdtlEntries -= cPrdtlEntriesRead;
} while (cPrdtlEntries);
if (RT_UNLIKELY(!cRanges))
{
return VERR_BUFFER_OVERFLOW;
}
pAhciReq->u.Trim.cRanges = cRanges;
pAhciReq->u.Trim.paRanges = (PRTRANGE)RTMemAllocZ(sizeof(RTRANGE) * cRanges);
if (pAhciReq->u.Trim.paRanges)
{
uint32_t idxRange = 0;
cPrdtlEntries = pAhciReq->cPrdtlEntries;
GCPhysPrdtl = pAhciReq->GCPhysPrdtl;
/* Convert the ranges from the guest to our format. */
do
{
uint32_t cPrdtlEntriesRead = (cPrdtlEntries < RT_ELEMENTS(aPrdtlEntries))
? cPrdtlEntries
: RT_ELEMENTS(aPrdtlEntries);
PDMDevHlpPhysRead(pDevIns, GCPhysPrdtl, &aPrdtlEntries[0], cPrdtlEntriesRead * sizeof(SGLEntry));
for (uint32_t i = 0; i < cPrdtlEntriesRead; i++)
{
RTGCPHYS GCPhysAddrDataBase = AHCI_RTGCPHYS_FROM_U32(aPrdtlEntries[i].u32DBAUp, aPrdtlEntries[i].u32DBA);
uint32_t cbThisCopy = (aPrdtlEntries[i].u32DescInf & SGLENTRY_DESCINF_DBC) + 1;
cbThisCopy = RT_MIN(cbThisCopy, sizeof(aRanges));
/* Copy into buffer. */
PDMDevHlpPhysRead(pDevIns, GCPhysAddrDataBase, aRanges, cbThisCopy);
for (unsigned idxRangeSrc = 0; idxRangeSrc < RT_ELEMENTS(aRanges); idxRangeSrc++)
{
aRanges[idxRangeSrc] = RT_H2LE_U64(aRanges[idxRangeSrc]);
if (AHCI_RANGE_LENGTH_GET(aRanges[idxRangeSrc]) != 0)
{
pAhciReq->u.Trim.paRanges[idxRange].offStart = (aRanges[idxRangeSrc] & AHCI_RANGE_LBA_MASK) * pAhciPort->cbSector;
pAhciReq->u.Trim.paRanges[idxRange].cbRange = AHCI_RANGE_LENGTH_GET(aRanges[idxRangeSrc]) * pAhciPort->cbSector;
idxRange++;
}
else
break;
}
}
GCPhysPrdtl += cPrdtlEntriesRead * sizeof(SGLEntry);
cPrdtlEntries -= cPrdtlEntriesRead;
} while (idxRange < cRanges);
}
else
rc = VERR_NO_MEMORY;
LogFlowFunc(("returns rc=%Rrc\n", rc));
return rc;
}
/**
* Destroy the trim range list.
*
* @returns nothing.
* @param pAhciReq The task state.
*/
static void ahciTrimRangesDestroy(PAHCIREQ pAhciReq)
{
AssertReturnVoid(pAhciReq->enmTxDir == AHCITXDIR_TRIM);
RTMemFree(pAhciReq->u.Trim.paRanges);
}
/**
* Complete a data transfer task by freeing all occupied resources
* and notifying the guest.
*
* @returns Flag whether the given request was canceled inbetween;
*
* @param pAhciPort Pointer to the port where to request completed.
* @param pAhciReq Pointer to the task which finished.
* @param rcReq IPRT status code of the completed request.
* @param fFreeReq Flag whether to free the request if it was canceled.
*/
static bool ahciTransferComplete(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, int rcReq, bool fFreeReq)
{
bool fXchg = false;
bool fRedo = false;
bool fCanceled = false;
uint64_t tsNow = RTTimeMilliTS();
AHCITXSTATE enmTxState = AHCITXSTATE_INVALID;
LogFlowFunc(("pAhciPort=%p pAhciReq=%p rcReq=%d fFreeReq=%RTbool\n",
pAhciPort, pAhciReq, rcReq, fFreeReq));
ASMAtomicReadSize(&pAhciReq->enmTxState, &enmTxState);
VBOXDD_AHCI_REQ_COMPLETED(pAhciReq, rcReq, enmTxState, pAhciReq->uOffset, pAhciReq->cbTransfer);
VBOXDD_AHCI_REQ_COMPLETED_TIMESTAMP(pAhciReq, tsNow);
/*
* Leave a release log entry if the request was active for more than 25 seconds
* (30 seconds is the timeout of the guest).
*/
if (tsNow - pAhciReq->tsStart >= 25 * 1000)
{
const char *pcszReq = NULL;
switch (pAhciReq->enmTxDir)
{
case AHCITXDIR_READ:
pcszReq = "Read";
break;
case AHCITXDIR_WRITE:
pcszReq = "Write";
break;
case AHCITXDIR_FLUSH:
pcszReq = "Flush";
break;
case AHCITXDIR_TRIM:
pcszReq = "Trim";
break;
default:
pcszReq = "<Invalid>";
}
LogRel(("AHCI#%uP%u: %s request was active for %llu seconds\n",
pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, pcszReq, (tsNow - pAhciReq->tsStart) / 1000));
}
ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg);
if ( fXchg
&& !ASMAtomicReadBool(&pAhciPort->fPortReset))
{
if (pAhciReq->enmTxDir == AHCITXDIR_READ)
{
ahciIoBufFree(pAhciPort->pDevInsR3, pAhciReq, true /* fCopyToGuest */);
STAM_REL_COUNTER_ADD(&pAhciPort->StatBytesRead, pAhciReq->cbTransfer);
pAhciPort->Led.Actual.s.fReading = 0;
}
else if (pAhciReq->enmTxDir == AHCITXDIR_WRITE)
{
ahciIoBufFree(pAhciPort->pDevInsR3, pAhciReq, false /* fCopyToGuest */);
STAM_REL_COUNTER_ADD(&pAhciPort->StatBytesWritten, pAhciReq->cbTransfer);
pAhciPort->Led.Actual.s.fWriting = 0;
}
else if (pAhciReq->enmTxDir == AHCITXDIR_TRIM)
{
ahciTrimRangesDestroy(pAhciReq);
pAhciPort->Led.Actual.s.fWriting = 0;
}
if (RT_FAILURE(rcReq))
{
/* Log the error. */
if (pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS)
{
if (pAhciReq->enmTxDir == AHCITXDIR_FLUSH)
LogRel(("AHCI#%uP%u: Flush returned rc=%Rrc\n",
pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, rcReq));
else if (pAhciReq->enmTxDir == AHCITXDIR_TRIM)
LogRel(("AHCI#%uP%u: Trim returned rc=%Rrc\n",
pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, rcReq));
else
LogRel(("AHCI#%uP%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN,
pAhciReq->enmTxDir == AHCITXDIR_READ
? "Read"
: "Write",
pAhciReq->uOffset,
pAhciReq->cbTransfer, rcReq));
}
fRedo = ahciIsRedoSetWarning(pAhciPort, rcReq);
if (!fRedo)
{
pAhciReq->cmdHdr.u32PRDBC = 0;
pAhciReq->uATARegError = ID_ERR;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
ASMAtomicCmpXchgPtr(&pAhciPort->pTaskErr, pAhciReq, NULL);
}
else
ASMAtomicOrU32(&pAhciPort->u32TasksRedo, RT_BIT_32(pAhciReq->uTag));
}
else
{
pAhciReq->cmdHdr.u32PRDBC = pAhciReq->cbTransfer;
/* Status will be set by already for non I/O requests. */
if (pAhciReq->enmTxDir != AHCITXDIR_NONE)
{
pAhciReq->uATARegError = 0;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK;
}
/* Write updated command header into memory of the guest. */
PDMDevHlpPCIPhysWrite(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, &pAhciReq->cmdHdr, sizeof(CmdHdr));
if (pAhciReq->fFlags & AHCI_REQ_OVERFLOW)
{
/*
* The guest tried to transfer more data than there is space in the buffer.
* Terminate task and set the overflow bit.
*/
/* Notify the guest. */
ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_OFS);
if (pAhciPort->regIE & AHCI_PORT_IE_OFE)
ahciHbaSetInterrupt(pAhciPort->CTX_SUFF(pAhci), pAhciPort->iLUN, VERR_IGNORED);
}
}
AssertReleaseMsg(ASMAtomicReadU32(&pAhciPort->cTasksActive) > 0 ,
("Inconsistent request counter\n"));
ASMAtomicDecU32(&pAhciPort->cTasksActive);
if (!fRedo)
{
/* Post a PIO setup FIS first if this is a PIO command which transfers data. */
if (pAhciReq->fFlags & AHCI_REQ_PIO_DATA)
ahciSendPioSetupFis(pAhciPort, pAhciReq, pAhciReq->cmdFis, false /* fInterrupt */);
if (pAhciReq->fFlags & AHCI_REQ_CLEAR_SACT)
{
if (RT_SUCCESS(rcReq) && !ASMAtomicReadPtrT(&pAhciPort->pTaskErr, PAHCIREQ))
ASMAtomicOrU32(&pAhciPort->u32QueuedTasksFinished, RT_BIT_32(pAhciReq->uTag));
}
if (pAhciReq->fFlags & AHCI_REQ_IS_QUEUED)
{
/*
* Always raise an interrupt after task completion; delaying
* this (interrupt coalescing) increases latency and has a significant
* impact on performance (see @bugref{5071})
*/
ahciSendSDBFis(pAhciPort, 0, true);
}
else
ahciSendD2HFis(pAhciPort, pAhciReq, pAhciReq->cmdFis, true);
}
}
else
{
/*
* Task was canceled, do the cleanup but DO NOT access the guest memory!
* The guest might use it for other things now because it doesn't know about that task anymore.
*/
AssertMsg( pAhciReq->enmTxState == AHCITXSTATE_CANCELED
|| pAhciPort->fPortReset,
("Task is not active but wasn't canceled!\n"));
fCanceled = true;
ASMAtomicXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE);
if (pAhciReq->enmTxDir == AHCITXDIR_TRIM)
ahciTrimRangesDestroy(pAhciReq);
else if (pAhciReq->enmTxDir != AHCITXDIR_FLUSH)
ahciIoBufFree(pAhciPort->pDevInsR3, pAhciReq, false /* fCopyToGuest */);
/* Leave a log message about the canceled request. */
if (pAhciPort->cErrors++ < MAX_LOG_REL_ERRORS)
{
if (pAhciReq->enmTxDir == AHCITXDIR_FLUSH)
LogRel(("AHCI#%uP%u: Canceled flush returned rc=%Rrc\n",
pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN, rcReq));
else if (pAhciReq->enmTxDir == AHCITXDIR_TRIM)
LogRel(("AHCI#%uP%u: Canceled trim returned rc=%Rrc\n",
pAhciPort->CTX_SUFF(pDevIns)->iInstance,pAhciPort->iLUN, rcReq));
else
LogRel(("AHCI#%uP%u: Canceled %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
pAhciPort->CTX_SUFF(pDevIns)->iInstance, pAhciPort->iLUN,
pAhciReq->enmTxDir == AHCITXDIR_READ
? "read"
: "write",
pAhciReq->uOffset,
pAhciReq->cbTransfer, rcReq));
}
/* Finally free the task state structure because it is completely unused now. */
if (fFreeReq)
RTMemFree(pAhciReq);
}
if (pAhciPort->cTasksActive == 0 && pAhciPort->pAhciR3->fSignalIdle)
PDMDevHlpAsyncNotificationCompleted(pAhciPort->pDevInsR3);
return fCanceled;
}
/**
* Notification callback for a completed transfer.
*
* @returns VBox status code.
* @param pInterface Pointer to the interface.
* @param pvUser User data.
* @param rcReq IPRT Status code of the completed request.
*/
static DECLCALLBACK(int) ahciR3TransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rcReq)
{
PAHCIPort pAhciPort = PDMIBLOCKASYNCPORT_2_PAHCIPORT(pInterface);
PAHCIREQ pAhciReq = (PAHCIREQ)pvUser;
ahciLog(("%s: pInterface=%p pvUser=%p uTag=%u\n",
__FUNCTION__, pInterface, pvUser, pAhciReq->uTag));
ahciTransferComplete(pAhciPort, pAhciReq, rcReq, true);
return VINF_SUCCESS;
}
/**
* Process an non read/write ATA command.
*
* @returns The direction of the data transfer
* @param pCmdHdr Pointer to the command header.
*/
static AHCITXDIR ahciProcessCmd(PAHCIPort pAhciPort, PAHCIREQ pAhciReq, uint8_t *pCmdFis)
{
AHCITXDIR enmTxDir = AHCITXDIR_NONE;
bool fLBA48 = false;
CmdHdr *pCmdHdr = &pAhciReq->cmdHdr;
AssertMsg(pCmdFis[AHCI_CMDFIS_TYPE] == AHCI_CMDFIS_TYPE_H2D, ("FIS is not a host to device Fis!!\n"));
pAhciReq->cbTransfer = 0;
switch (pCmdFis[AHCI_CMDFIS_CMD])
{
case ATA_IDENTIFY_DEVICE:
{
if (pAhciPort->pDrvBlock && !pAhciPort->fATAPI)
{
uint16_t u16Temp[256];
size_t cbCopied;
/* Fill the buffer. */
ahciIdentifySS(pAhciPort, u16Temp);
/* Copy the buffer. */
cbCopied = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq,
&u16Temp[0], sizeof(u16Temp));
pAhciReq->fFlags |= AHCI_REQ_PIO_DATA;
pAhciReq->cbTransfer = cbCopied;
}
else
{
pAhciReq->uATARegError = ABRT_ERR;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK | ATA_STAT_ERR;
}
break;
}
case ATA_READ_NATIVE_MAX_ADDRESS_EXT:
case ATA_READ_NATIVE_MAX_ADDRESS:
break;
case ATA_SET_FEATURES:
{
switch (pCmdFis[AHCI_CMDFIS_FET])
{
case 0x02: /* write cache enable */
case 0xaa: /* read look-ahead enable */
case 0x55: /* read look-ahead disable */
case 0xcc: /* reverting to power-on defaults enable */
case 0x66: /* reverting to power-on defaults disable */
pAhciReq->uATARegError = 0;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK;
break;
case 0x82: /* write cache disable */
enmTxDir = AHCITXDIR_FLUSH;
break;
case 0x03:
{
/* set transfer mode */
Log2(("%s: transfer mode %#04x\n", __FUNCTION__, pCmdFis[AHCI_CMDFIS_SECTC]));
switch (pCmdFis[AHCI_CMDFIS_SECTC] & 0xf8)
{
case 0x00: /* PIO default */
case 0x08: /* PIO mode */
break;
case ATA_MODE_MDMA: /* MDMA mode */
pAhciPort->uATATransferMode = (pCmdFis[AHCI_CMDFIS_SECTC] & 0xf8) | RT_MIN(pCmdFis[AHCI_CMDFIS_SECTC] & 0x07, ATA_MDMA_MODE_MAX);
break;
case ATA_MODE_UDMA: /* UDMA mode */
pAhciPort->uATATransferMode = (pCmdFis[AHCI_CMDFIS_SECTC] & 0xf8) | RT_MIN(pCmdFis[AHCI_CMDFIS_SECTC] & 0x07, ATA_UDMA_MODE_MAX);
break;
}
break;
}
default:
pAhciReq->uATARegError = ABRT_ERR;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
}
break;
}
case ATA_DEVICE_RESET:
{
if (!pAhciPort->fATAPI)
{
pAhciReq->uATARegError = ABRT_ERR;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
}
else
{
/* Reset the device. */
ahciDeviceReset(pAhciPort, pAhciReq);
}
break;
}
case ATA_FLUSH_CACHE_EXT:
case ATA_FLUSH_CACHE:
enmTxDir = AHCITXDIR_FLUSH;
break;
case ATA_PACKET:
if (!pAhciPort->fATAPI)
{
pAhciReq->uATARegError = ABRT_ERR;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
}
else
enmTxDir = atapiParseCmd(pAhciPort, pAhciReq);
break;
case ATA_IDENTIFY_PACKET_DEVICE:
if (!pAhciPort->fATAPI)
{
pAhciReq->uATARegError = ABRT_ERR;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
}
else
{
atapiDoTransfer(pAhciPort, pAhciReq, 512, ATAFN_SS_ATAPI_IDENTIFY);
pAhciReq->fFlags |= AHCI_REQ_PIO_DATA;
pAhciReq->uATARegError = 0;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK;
}
break;
case ATA_SET_MULTIPLE_MODE:
if ( pCmdFis[AHCI_CMDFIS_SECTC] != 0
&& ( pCmdFis[AHCI_CMDFIS_SECTC] > ATA_MAX_MULT_SECTORS
|| (pCmdFis[AHCI_CMDFIS_SECTC] & (pCmdFis[AHCI_CMDFIS_SECTC] - 1)) != 0))
{
pAhciReq->uATARegError = ABRT_ERR;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
}
else
{
Log2(("%s: set multi sector count to %d\n", __FUNCTION__, pCmdFis[AHCI_CMDFIS_SECTC]));
pAhciPort->cMultSectors = pCmdFis[AHCI_CMDFIS_SECTC];
pAhciReq->uATARegError = 0;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK;
}
break;
case ATA_STANDBY_IMMEDIATE:
break; /* Do nothing. */
case ATA_CHECK_POWER_MODE:
pAhciReq->cmdFis[AHCI_CMDFIS_SECTC] = 0xff; /* drive active or idle */
/* fall through */
case ATA_INITIALIZE_DEVICE_PARAMETERS:
case ATA_IDLE_IMMEDIATE:
case ATA_RECALIBRATE:
case ATA_NOP:
case ATA_READ_VERIFY_SECTORS_EXT:
case ATA_READ_VERIFY_SECTORS:
case ATA_READ_VERIFY_SECTORS_WITHOUT_RETRIES:
case ATA_SLEEP:
pAhciReq->uATARegError = 0;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK;
break;
case ATA_READ_DMA_EXT:
fLBA48 = true;
case ATA_READ_DMA:
{
pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * pAhciPort->cbSector;
pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * pAhciPort->cbSector;
enmTxDir = AHCITXDIR_READ;
break;
}
case ATA_WRITE_DMA_EXT:
fLBA48 = true;
case ATA_WRITE_DMA:
{
pAhciReq->cbTransfer = ahciGetNSectors(pCmdFis, fLBA48) * pAhciPort->cbSector;
pAhciReq->uOffset = ahciGetSector(pAhciPort, pCmdFis, fLBA48) * pAhciPort->cbSector;
enmTxDir = AHCITXDIR_WRITE;
break;
}
case ATA_READ_FPDMA_QUEUED:
{
pAhciReq->cbTransfer = ahciGetNSectorsQueued(pCmdFis) * pAhciPort->cbSector;
pAhciReq->uOffset = ahciGetSectorQueued(pCmdFis) * pAhciPort->cbSector;
pAhciReq->fFlags |= AHCI_REQ_IS_QUEUED;
enmTxDir = AHCITXDIR_READ;
break;
}
case ATA_WRITE_FPDMA_QUEUED:
{
pAhciReq->cbTransfer = ahciGetNSectorsQueued(pCmdFis) * pAhciPort->cbSector;
pAhciReq->uOffset = ahciGetSectorQueued(pCmdFis) * pAhciPort->cbSector;
pAhciReq->fFlags |= AHCI_REQ_IS_QUEUED;
enmTxDir = AHCITXDIR_WRITE;
break;
}
case ATA_READ_LOG_EXT:
{
size_t cbLogRead = ((pCmdFis[AHCI_CMDFIS_SECTCEXP] << 8) | pCmdFis[AHCI_CMDFIS_SECTC]) * 512;
unsigned offLogRead = ((pCmdFis[AHCI_CMDFIS_CYLLEXP] << 8) | pCmdFis[AHCI_CMDFIS_CYLL]) * 512;
unsigned iPage = pCmdFis[AHCI_CMDFIS_SECTN];
size_t cbCopied;
LogFlow(("Trying to read %zu bytes starting at offset %u from page %u\n", cbLogRead, offLogRead, iPage));
uint8_t aBuf[512];
memset(aBuf, 0, sizeof(aBuf));
if (offLogRead + cbLogRead <= sizeof(aBuf))
{
switch (iPage)
{
case 0x10:
{
LogFlow(("Reading error page\n"));
PAHCIREQ pTaskErr = ASMAtomicXchgPtrT(&pAhciPort->pTaskErr, NULL, PAHCIREQ);
if (pTaskErr)
{
aBuf[0] = (pTaskErr->fFlags & AHCI_REQ_IS_QUEUED) ? pTaskErr->uTag : (1 << 7);
aBuf[2] = pTaskErr->uATARegStatus;
aBuf[3] = pTaskErr->uATARegError;
aBuf[4] = pTaskErr->cmdFis[AHCI_CMDFIS_SECTN];
aBuf[5] = pTaskErr->cmdFis[AHCI_CMDFIS_CYLL];
aBuf[6] = pTaskErr->cmdFis[AHCI_CMDFIS_CYLH];
aBuf[7] = pTaskErr->cmdFis[AHCI_CMDFIS_HEAD];
aBuf[8] = pTaskErr->cmdFis[AHCI_CMDFIS_SECTNEXP];
aBuf[9] = pTaskErr->cmdFis[AHCI_CMDFIS_CYLLEXP];
aBuf[10] = pTaskErr->cmdFis[AHCI_CMDFIS_CYLHEXP];
aBuf[12] = pTaskErr->cmdFis[AHCI_CMDFIS_SECTC];
aBuf[13] = pTaskErr->cmdFis[AHCI_CMDFIS_SECTCEXP];
/* Calculate checksum */
uint8_t uChkSum = 0;
for (unsigned i = 0; i < RT_ELEMENTS(aBuf)-1; i++)
uChkSum += aBuf[i];
aBuf[511] = (uint8_t)-(int8_t)uChkSum;
/*
* Reading this log page results in an abort of all outstanding commands
* and clearing the SActive register and TaskFile register.
*/
ahciSendSDBFis(pAhciPort, 0xffffffff, true);
}
break;
}
}
/* Copy the buffer. */
cbCopied = ahciCopyToPrdtl(pAhciPort->pDevInsR3, pAhciReq,
&aBuf[offLogRead], cbLogRead);
pAhciReq->fFlags |= AHCI_REQ_PIO_DATA;
pAhciReq->cbTransfer = cbCopied;
}
break;
}
case ATA_DATA_SET_MANAGEMENT:
{
if ( ( !pAhciPort->fAsyncInterface
&& pAhciPort->pDrvBlock->pfnDiscard)
|| ( pAhciPort->fAsyncInterface
&& pAhciPort->pDrvBlockAsync->pfnStartDiscard))
{
/* Check that the trim bit is set and all other bits are 0. */
if ( !(pAhciReq->cmdFis[AHCI_CMDFIS_FET] & UINT16_C(0x01))
|| (pAhciReq->cmdFis[AHCI_CMDFIS_FET] & ~UINT16_C(0x1)))
{
pAhciReq->uATARegError = ABRT_ERR;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_SEEK;
}
else
enmTxDir = AHCITXDIR_TRIM;
break;
}
/* else: fall through and report error to the guest. */
}
/* All not implemented commands go below. */
case ATA_SECURITY_FREEZE_LOCK:
case ATA_SMART:
case ATA_NV_CACHE:
case ATA_IDLE:
pAhciReq->uATARegError = ABRT_ERR;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
break;
default: /* For debugging purposes. */
AssertMsgFailed(("Unknown command issued\n"));
pAhciReq->uATARegError = ABRT_ERR;
pAhciReq->uATARegStatus = ATA_STAT_READY | ATA_STAT_ERR;
}
return enmTxDir;
}
/**
* Retrieve a command FIS from guest memory.
*
* @returns whether the H2D FIS was successfully read from the guest memory.
* @param pAhciReq The state of the actual task.
*/
static bool ahciPortTaskGetCommandFis(PAHCIPort pAhciPort, PAHCIREQ pAhciReq)
{
RTGCPHYS GCPhysAddrCmdTbl;
AssertMsgReturn(pAhciPort->GCPhysAddrClb && pAhciPort->GCPhysAddrFb,
("%s: GCPhysAddrClb and/or GCPhysAddrFb are 0\n", __FUNCTION__),
false);
/*
* First we are reading the command header pointed to by regCLB.
* From this we get the address of the command table which we are reading too.
* We can process the Command FIS afterwards.
*/
pAhciReq->GCPhysCmdHdrAddr = pAhciPort->GCPhysAddrClb + pAhciReq->uTag * sizeof(CmdHdr);
LogFlow(("%s: PDMDevHlpPhysRead GCPhysAddrCmdLst=%RGp cbCmdHdr=%u\n", __FUNCTION__,
pAhciReq->GCPhysCmdHdrAddr, sizeof(CmdHdr)));
PDMDevHlpPhysRead(pAhciPort->CTX_SUFF(pDevIns), pAhciReq->GCPhysCmdHdrAddr, &pAhciReq->cmdHdr, sizeof(CmdHdr));
#ifdef LOG_ENABLED
/* Print some infos about the command header. */
ahciDumpCmdHdrInfo(pAhciPort, &pAhciReq->cmdHdr);
#endif
GCPhysAddrCmdTbl = AHCI_RTGCPHYS_FROM_U32(pAhciReq->cmdHdr.u32CmdTblAddrUp, pAhciReq->cmdHdr.u32CmdTblAddr);
AssertMsgReturn((pAhciReq->cmdHdr.u32DescInf & AHCI_CMDHDR_CFL_MASK) * sizeof(uint32_t) == AHCI_CMDFIS_TYPE_H2D_SIZE,
("This is not a command FIS!!\n"),
false);
/* Read the command Fis. */
LogFlow(("%s: PDMDevHlpPhysRead GCPhysAddrCmdTbl=%RGp cbCmdFis=%u\n", __FUNCTION__, GCPhysAddrCmdTbl, AHCI_CMDFIS_TYPE_H2D_SIZE));
PDMDevHlpPhysRead(pAhciPort->CTX_SUFF(pDevIns), GCPhysAddrCmdTbl, &pAhciReq->cmdFis[0], AHCI_CMDFIS_TYPE_H2D_SIZE);
AssertMsgReturn(pAhciReq->cmdFis[AHCI_CMDFIS_TYPE] == AHCI_CMDFIS_TYPE_H2D,
("This is not a command FIS\n"),
false);
/* Set transfer direction. */
pAhciReq->enmTxDir = (pAhciReq->cmdHdr.u32DescInf & AHCI_CMDHDR_W) ? AHCITXDIR_WRITE : AHCITXDIR_READ;
/* If this is an ATAPI command read the atapi command. */
if (pAhciReq->cmdHdr.u32DescInf & AHCI_CMDHDR_A)
{
GCPhysAddrCmdTbl += AHCI_CMDHDR_ACMD_OFFSET;
PDMDevHlpPhysRead(pAhciPort->CTX_SUFF(pDevIns), GCPhysAddrCmdTbl, &pAhciReq->aATAPICmd[0], ATAPI_PACKET_SIZE);
}
/* We "received" the FIS. Clear the BSY bit in regTFD. */
if ((pAhciReq->cmdHdr.u32DescInf & AHCI_CMDHDR_C) && (pAhciReq->fFlags & AHCI_REQ_CLEAR_SACT))
{
/*
* We need to send a FIS which clears the busy bit if this is a queued command so that the guest can queue other commands.
* but this FIS does not assert an interrupt
*/
ahciSendD2HFis(pAhciPort, pAhciReq, pAhciReq->cmdFis, false);
pAhciPort->regTFD &= ~AHCI_PORT_TFD_BSY;
}
pAhciReq->GCPhysPrdtl = AHCI_RTGCPHYS_FROM_U32(pAhciReq->cmdHdr.u32CmdTblAddrUp, pAhciReq->cmdHdr.u32CmdTblAddr) + AHCI_CMDHDR_PRDT_OFFSET;
pAhciReq->cPrdtlEntries = AHCI_CMDHDR_PRDTL_ENTRIES(pAhciReq->cmdHdr.u32DescInf);
#ifdef LOG_ENABLED
/* Print some infos about the FIS. */
ahciDumpFisInfo(pAhciPort, &pAhciReq->cmdFis[0]);
/* Print the PRDT */
ahciLog(("PRDT address %RGp number of entries %u\n", pAhciReq->GCPhysPrdtl, pAhciReq->cPrdtlEntries));
RTGCPHYS GCPhysPrdtl = pAhciReq->GCPhysPrdtl;
for (unsigned i = 0; i < pAhciReq->cPrdtlEntries; i++)
{
SGLEntry SGEntry;
ahciLog(("Entry %u at address %RGp\n", i, GCPhysPrdtl));
PDMDevHlpPhysRead(pAhciPort->CTX_SUFF(pDevIns), GCPhysPrdtl, &SGEntry, sizeof(SGLEntry));
RTGCPHYS GCPhysDataAddr = AHCI_RTGCPHYS_FROM_U32(SGEntry.u32DBAUp, SGEntry.u32DBA);
ahciLog(("GCPhysAddr=%RGp Size=%u\n", GCPhysDataAddr, SGEntry.u32DescInf & SGLENTRY_DESCINF_DBC));
GCPhysPrdtl += sizeof(SGLEntry);
}
#endif
return true;
}
/**
* Transmit queue consumer
* Queue a new async task.
*
* @returns Success indicator.
* If false the item will not be removed and the flushing will stop.
* @param pDevIns The device instance.
* @param pItem The item to consume. Upon return this item will be freed.
*/
static DECLCALLBACK(bool) ahciNotifyQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
{
PDEVPORTNOTIFIERQUEUEITEM pNotifierItem = (PDEVPORTNOTIFIERQUEUEITEM)pItem;
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
PAHCIPort pAhciPort = &pThis->ahciPort[pNotifierItem->iPort];
int rc = VINF_SUCCESS;
ahciLog(("%s: Got notification from GC\n", __FUNCTION__));
/* Notify the async IO thread. */
rc = SUPSemEventSignal(pThis->pSupDrvSession, pAhciPort->hEvtProcess);
AssertRC(rc);
return true;
}
/* The async IO thread for one port. */
static DECLCALLBACK(int) ahciAsyncIOLoop(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
{
PAHCIPort pAhciPort = (PAHCIPort)pThread->pvUser;
PAHCI pAhci = pAhciPort->CTX_SUFF(pAhci);
int rc = VINF_SUCCESS;
uint64_t u64StartTime = 0;
uint64_t u64StopTime = 0;
uint32_t uIORequestsProcessed = 0;
uint32_t uIOsPerSec = 0;
uint32_t fTasksToProcess = 0;
ahciLog(("%s: Port %d entering async IO loop.\n", __FUNCTION__, pAhciPort->iLUN));
if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
return VINF_SUCCESS;
while (pThread->enmState == PDMTHREADSTATE_RUNNING)
{
unsigned idx = 0;
uint32_t u32Tasks = 0;
uint32_t u32RegHbaCtrl = 0;
ASMAtomicWriteBool(&pAhciPort->fWrkThreadSleeping, true);
u32Tasks = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0);
if (!u32Tasks)
{
Assert(ASMAtomicReadBool(&pAhciPort->fWrkThreadSleeping));
rc = SUPSemEventWaitNoResume(pAhci->pSupDrvSession, pAhciPort->hEvtProcess, RT_INDEFINITE_WAIT);
AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
break;
LogFlowFunc(("Woken up with rc=%Rrc\n", rc));
u32Tasks = ASMAtomicXchgU32(&pAhciPort->u32TasksNew, 0);
}
ASMAtomicWriteBool(&pAhciPort->fWrkThreadSleeping, false);
ASMAtomicIncU32(&pAhci->cThreadsActive);
/*
* Check whether the global host controller bit is set and go to sleep immediately again
* if it is set.
*/
u32RegHbaCtrl = ASMAtomicReadU32(&pAhci->regHbaCtrl);
if ( u32RegHbaCtrl & AHCI_HBA_CTRL_HR
&& !ASMAtomicDecU32(&pAhci->cThreadsActive))
{
ahciHBAReset(pAhci);
continue;
}
idx = ASMBitFirstSetU32(u32Tasks);
while ( idx
&& !pAhciPort->fPortReset)
{
bool fReqCanceled = false;
AHCITXDIR enmTxDir;
PAHCIREQ pAhciReq;
/* Decrement to get the slot number. */
idx--;
ahciLog(("%s: Processing command at slot %d\n", __FUNCTION__, idx));
/*
* Check if there is already an allocated task struct in the cache.
* Allocate a new task otherwise.
*/
if (!pAhciPort->aCachedTasks[idx])
{
pAhciReq = (PAHCIREQ)RTMemAllocZ(sizeof(AHCIREQ));
AssertMsg(pAhciReq, ("%s: Cannot allocate task state memory!\n"));
pAhciReq->enmTxState = AHCITXSTATE_FREE;
pAhciPort->aCachedTasks[idx] = pAhciReq;
}
else
pAhciReq = pAhciPort->aCachedTasks[idx];
bool fXchg;
ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_ACTIVE, AHCITXSTATE_FREE, fXchg);
AssertMsg(fXchg, ("Task is already active\n"));
pAhciReq->tsStart = RTTimeMilliTS();
pAhciReq->uATARegStatus = 0;
pAhciReq->uATARegError = 0;
pAhciReq->fFlags = 0;
/* Set current command slot */
pAhciReq->uTag = idx;
ASMAtomicWriteU32(&pAhciPort->u32CurrentCommandSlot, pAhciReq->uTag);
bool fFisRead = ahciPortTaskGetCommandFis(pAhciPort, pAhciReq);
if (RT_UNLIKELY(!fFisRead))
{
/*
* Couldn't find anything in either the AHCI or SATA spec which
* indicates what should be done if the FIS is not read successfully.
* The closest thing is in the state machine, stating that the device
* should go into idle state again (SATA spec 1.0 chapter 8.7.1).
* Do the same here and ignore any corrupt FIS types, after all
* the guest messed up everything and this behavior is undefined.
*/
ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg);
Assert(fXchg);
u32Tasks &= ~RT_BIT_32(idx); /* Clear task bit. */
idx = ASMBitFirstSetU32(u32Tasks);
continue;
}
/* Mark the task as processed by the HBA if this is a queued task so that it doesn't occur in the CI register anymore. */
if (pAhciPort->regSACT & (1 << idx))
{
pAhciReq->fFlags |= AHCI_REQ_CLEAR_SACT;
ASMAtomicOrU32(&pAhciPort->u32TasksFinished, (1 << pAhciReq->uTag));
}
if (!(pAhciReq->cmdFis[AHCI_CMDFIS_BITS] & AHCI_CMDFIS_C))
{
/* If the reset bit is set put the device into reset state. */
if (pAhciReq->cmdFis[AHCI_CMDFIS_CTL] & AHCI_CMDFIS_CTL_SRST)
{
ahciLog(("%s: Setting device into reset state\n", __FUNCTION__));
pAhciPort->fResetDevice = true;
ahciSendD2HFis(pAhciPort, pAhciReq, pAhciReq->cmdFis, true);
}
else if (pAhciPort->fResetDevice) /* The bit is not set and we are in a reset state. */
ahciFinishStorageDeviceReset(pAhciPort, pAhciReq);
else /* We are not in a reset state update the control registers. */
AssertMsgFailed(("%s: Update the control register\n", __FUNCTION__));
ASMAtomicCmpXchgSize(&pAhciReq->enmTxState, AHCITXSTATE_FREE, AHCITXSTATE_ACTIVE, fXchg);
AssertMsg(fXchg, ("Task is not active\n"));
break;
}
else
{
AssertReleaseMsg(ASMAtomicReadU32(&pAhciPort->cTasksActive) < AHCI_NR_COMMAND_SLOTS,
("There are more than 32 requests active"));
ASMAtomicIncU32(&pAhciPort->cTasksActive);
enmTxDir = ahciProcessCmd(pAhciPort, pAhciReq, pAhciReq->cmdFis);
pAhciReq->enmTxDir = enmTxDir;
if (enmTxDir != AHCITXDIR_NONE)
{
if ( enmTxDir != AHCITXDIR_FLUSH
&& enmTxDir != AHCITXDIR_TRIM)
{
STAM_REL_COUNTER_INC(&pAhciPort->StatDMA);
rc = ahciIoBufAllocate(pAhciPort->pDevInsR3, pAhciReq, pAhciReq->cbTransfer);
if (RT_FAILURE(rc))
AssertMsgFailed(("%s: Failed to process command %Rrc\n", __FUNCTION__, rc));
}
if (!(pAhciReq->fFlags & AHCI_REQ_OVERFLOW))
{
if (pAhciPort->fAsyncInterface)
{
VBOXDD_AHCI_REQ_SUBMIT(pAhciReq, enmTxDir, pAhciReq->uOffset, pAhciReq->cbTransfer);
VBOXDD_AHCI_REQ_SUBMIT_TIMESTAMP(pAhciReq, pAhciReq->tsStart);
if (enmTxDir == AHCITXDIR_FLUSH)
{
rc = pAhciPort->pDrvBlockAsync->pfnStartFlush(pAhciPort->pDrvBlockAsync,
pAhciReq);
}
else if (enmTxDir == AHCITXDIR_TRIM)
{
rc = ahciTrimRangesCreate(pAhciPort, pAhciReq);
if (RT_SUCCESS(rc))
{
pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
rc = pAhciPort->pDrvBlockAsync->pfnStartDiscard(pAhciPort->pDrvBlockAsync, pAhciReq->u.Trim.paRanges,
pAhciReq->u.Trim.cRanges, pAhciReq);
}
}
else if (enmTxDir == AHCITXDIR_READ)
{
pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
rc = pAhciPort->pDrvBlockAsync->pfnStartRead(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset,
&pAhciReq->u.Io.DataSeg, 1,
pAhciReq->cbTransfer,
pAhciReq);
}
else
{
pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
rc = pAhciPort->pDrvBlockAsync->pfnStartWrite(pAhciPort->pDrvBlockAsync, pAhciReq->uOffset,
&pAhciReq->u.Io.DataSeg, 1,
pAhciReq->cbTransfer,
pAhciReq);
}
if (rc == VINF_VD_ASYNC_IO_FINISHED)
fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true);
else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, true);
}
else
{
if (enmTxDir == AHCITXDIR_FLUSH)
rc = pAhciPort->pDrvBlock->pfnFlush(pAhciPort->pDrvBlock);
else if (enmTxDir == AHCITXDIR_TRIM)
{
rc = ahciTrimRangesCreate(pAhciPort, pAhciReq);
if (RT_SUCCESS(rc))
{
pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
rc = pAhciPort->pDrvBlock->pfnDiscard(pAhciPort->pDrvBlock, pAhciReq->u.Trim.paRanges,
pAhciReq->u.Trim.cRanges);
pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0;
}
}
else if (enmTxDir == AHCITXDIR_READ)
{
pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 1;
rc = pAhciPort->pDrvBlock->pfnRead(pAhciPort->pDrvBlock, pAhciReq->uOffset,
pAhciReq->u.Io.DataSeg.pvSeg,
pAhciReq->cbTransfer);
pAhciPort->Led.Asserted.s.fReading = pAhciPort->Led.Actual.s.fReading = 0;
}
else
{
pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 1;
rc = pAhciPort->pDrvBlock->pfnWrite(pAhciPort->pDrvBlock, pAhciReq->uOffset,
pAhciReq->u.Io.DataSeg.pvSeg,
pAhciReq->cbTransfer);
pAhciPort->Led.Asserted.s.fWriting = pAhciPort->Led.Actual.s.fWriting = 0;
}
fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, rc, true);
}
}
}
else
fReqCanceled = ahciTransferComplete(pAhciPort, pAhciReq, VINF_SUCCESS, true);
} /* Command */
/*
* Don't process other requests if the last one was canceled,
* the others are not valid anymore.
*/
if (fReqCanceled)
break;
u32Tasks &= ~RT_BIT_32(idx); /* Clear task bit. */
idx = ASMBitFirstSetU32(u32Tasks);
} /* while tasks available */
/* Check whether a port reset was active. */
if ( ASMAtomicReadBool(&pAhciPort->fPortReset)
&& (pAhciPort->regSCTL & AHCI_PORT_SCTL_DET) == AHCI_PORT_SCTL_DET_NINIT)
ahciPortResetFinish(pAhciPort);
/*
* Check whether a host controller reset is pending and execute the reset
* if this is the last active thread.
*/
u32RegHbaCtrl = ASMAtomicReadU32(&pAhci->regHbaCtrl);
uint32_t cThreadsActive = ASMAtomicDecU32(&pAhci->cThreadsActive);
if ( (u32RegHbaCtrl & AHCI_HBA_CTRL_HR)
&& !cThreadsActive)
ahciHBAReset(pAhci);
} /* While running */
ahciLog(("%s: Port %d async IO thread exiting\n", __FUNCTION__, pAhciPort->iLUN));
return VINF_SUCCESS;
}
/**
* Unblock the async I/O thread so it can respond to a state change.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pThread The send thread.
*/
static DECLCALLBACK(int) ahciAsyncIOLoopWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
PAHCIPort pAhciPort = (PAHCIPort)pThread->pvUser;
return SUPSemEventSignal(pThis->pSupDrvSession, pAhciPort->hEvtProcess);
}
/* -=-=-=-=- DBGF -=-=-=-=- */
/**
* AHCI status info callback.
*
* @param pDevIns The device instance.
* @param pHlp The output helpers.
* @param pszArgs The arguments.
*/
static DECLCALLBACK(void) ahciR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
/*
* Show info.
*/
pHlp->pfnPrintf(pHlp,
"%s#%d: mmio=%RGp ports=%u GC=%RTbool R0=%RTbool\n",
pDevIns->pReg->szName,
pDevIns->iInstance,
pThis->MMIOBase,
pThis->cPortsImpl,
pThis->fGCEnabled ? true : false,
pThis->fR0Enabled ? true : false);
/*
* Show global registers.
*/
pHlp->pfnPrintf(pHlp, "HbaCap=%#x\n", pThis->regHbaCap);
pHlp->pfnPrintf(pHlp, "HbaCtrl=%#x\n", pThis->regHbaCtrl);
pHlp->pfnPrintf(pHlp, "HbaIs=%#x\n", pThis->regHbaIs);
pHlp->pfnPrintf(pHlp, "HbaPi=%#x", pThis->regHbaPi);
pHlp->pfnPrintf(pHlp, "HbaVs=%#x\n", pThis->regHbaVs);
pHlp->pfnPrintf(pHlp, "HbaCccCtl=%#x\n", pThis->regHbaCccCtl);
pHlp->pfnPrintf(pHlp, "HbaCccPorts=%#x\n", pThis->regHbaCccPorts);
pHlp->pfnPrintf(pHlp, "PortsInterrupted=%#x\n", pThis->u32PortsInterrupted);
/*
* Per port data.
*/
for (unsigned i = 0; i < pThis->cPortsImpl; i++)
{
PAHCIPort pThisPort = &pThis->ahciPort[i];
pHlp->pfnPrintf(pHlp, "Port %d: async=%RTbool device-attached=%RTbool\n",
pThisPort->iLUN, pThisPort->fAsyncInterface, pThisPort->pDrvBase != NULL);
pHlp->pfnPrintf(pHlp, "PortClb=%#x\n", pThisPort->regCLB);
pHlp->pfnPrintf(pHlp, "PortClbU=%#x\n", pThisPort->regCLBU);
pHlp->pfnPrintf(pHlp, "PortFb=%#x\n", pThisPort->regFB);
pHlp->pfnPrintf(pHlp, "PortFbU=%#x\n", pThisPort->regFBU);
pHlp->pfnPrintf(pHlp, "PortIs=%#x\n", pThisPort->regIS);
pHlp->pfnPrintf(pHlp, "PortIe=%#x\n", pThisPort->regIE);
pHlp->pfnPrintf(pHlp, "PortCmd=%#x\n", pThisPort->regCMD);
pHlp->pfnPrintf(pHlp, "PortTfd=%#x\n", pThisPort->regTFD);
pHlp->pfnPrintf(pHlp, "PortSig=%#x\n", pThisPort->regSIG);
pHlp->pfnPrintf(pHlp, "PortSSts=%#x\n", pThisPort->regSSTS);
pHlp->pfnPrintf(pHlp, "PortSCtl=%#x\n", pThisPort->regSCTL);
pHlp->pfnPrintf(pHlp, "PortSErr=%#x\n", pThisPort->regSERR);
pHlp->pfnPrintf(pHlp, "PortSAct=%#x\n", pThisPort->regSACT);
pHlp->pfnPrintf(pHlp, "PortCi=%#x\n", pThisPort->regCI);
pHlp->pfnPrintf(pHlp, "PortPhysClb=%RGp\n", pThisPort->GCPhysAddrClb);
pHlp->pfnPrintf(pHlp, "PortPhysFb=%RGp\n", pThisPort->GCPhysAddrFb);
pHlp->pfnPrintf(pHlp, "PortActTasksActive=%u\n", pThisPort->cTasksActive);
pHlp->pfnPrintf(pHlp, "PortPoweredOn=%RTbool\n", pThisPort->fPoweredOn);
pHlp->pfnPrintf(pHlp, "PortSpunUp=%RTbool\n", pThisPort->fSpunUp);
pHlp->pfnPrintf(pHlp, "PortFirstD2HFisSend=%RTbool\n", pThisPort->fFirstD2HFisSend);
pHlp->pfnPrintf(pHlp, "PortATAPI=%RTbool\n", pThisPort->fATAPI);
pHlp->pfnPrintf(pHlp, "PortTasksFinished=%#x\n", pThisPort->u32TasksFinished);
pHlp->pfnPrintf(pHlp, "PortQueuedTasksFinished=%#x\n", pThisPort->u32QueuedTasksFinished);
pHlp->pfnPrintf(pHlp, "\n");
}
}
/* -=-=-=-=- Helper -=-=-=-=- */
/**
* Checks if all asynchronous I/O is finished, both AHCI and IDE.
*
* Used by ahciR3Reset, ahciR3Suspend and ahciR3PowerOff. ahciR3SavePrep makes
* use of it in strict builds (which is why it's up here).
*
* @returns true if quiesced, false if busy.
* @param pDevIns The device instance.
*/
static bool ahciR3AllAsyncIOIsFinished(PPDMDEVINS pDevIns)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
if (pThis->cThreadsActive)
return false;
for (uint32_t i = 0; i < RT_ELEMENTS(pThis->ahciPort); i++)
{
PAHCIPort pThisPort = &pThis->ahciPort[i];
if (pThisPort->pDrvBase)
{
if ( (pThisPort->cTasksActive != 0)
|| (pThisPort->u32TasksNew != 0))
return false;
}
}
return true;
}
/* -=-=-=-=- Saved State -=-=-=-=- */
/**
* @copydoc FNDEVSSMSAVEPREP
*/
static DECLCALLBACK(int) ahciR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
Assert(ahciR3AllAsyncIOIsFinished(pDevIns));
return VINF_SUCCESS;
}
/**
* @copydoc FNDEVSSMLOADPREP
*/
static DECLCALLBACK(int) ahciR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
Assert(ahciR3AllAsyncIOIsFinished(pDevIns));
return VINF_SUCCESS;
}
/**
* @copydoc FNDEVSSMLIVEEXEC
*/
static DECLCALLBACK(int) ahciR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
/* config. */
SSMR3PutU32(pSSM, pThis->cPortsImpl);
for (uint32_t i = 0; i < AHCI_MAX_NR_PORTS_IMPL; i++)
{
SSMR3PutBool(pSSM, pThis->ahciPort[i].pDrvBase != NULL);
SSMR3PutBool(pSSM, pThis->ahciPort[i].fHotpluggable);
SSMR3PutStrZ(pSSM, pThis->ahciPort[i].szSerialNumber);
SSMR3PutStrZ(pSSM, pThis->ahciPort[i].szFirmwareRevision);
SSMR3PutStrZ(pSSM, pThis->ahciPort[i].szModelNumber);
}
static const char *s_apszIdeEmuPortNames[4] = { "PrimaryMaster", "PrimarySlave", "SecondaryMaster", "SecondarySlave" };
for (size_t i = 0; i < RT_ELEMENTS(s_apszIdeEmuPortNames); i++)
{
uint32_t iPort;
int rc = CFGMR3QueryU32Def(pDevIns->pCfg, s_apszIdeEmuPortNames[i], &iPort, i);
AssertRCReturn(rc, rc);
SSMR3PutU32(pSSM, iPort);
}
return VINF_SSM_DONT_CALL_AGAIN;
}
/**
* @copydoc FNDEVSSMSAVEEXEC
*/
static DECLCALLBACK(int) ahciR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
uint32_t i;
int rc;
Assert(!pThis->f8ByteMMIO4BytesWrittenSuccessfully);
/* The config */
rc = ahciR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
AssertRCReturn(rc, rc);
/* The main device structure. */
SSMR3PutU32(pSSM, pThis->regHbaCap);
SSMR3PutU32(pSSM, pThis->regHbaCtrl);
SSMR3PutU32(pSSM, pThis->regHbaIs);
SSMR3PutU32(pSSM, pThis->regHbaPi);
SSMR3PutU32(pSSM, pThis->regHbaVs);
SSMR3PutU32(pSSM, pThis->regHbaCccCtl);
SSMR3PutU32(pSSM, pThis->regHbaCccPorts);
SSMR3PutU8(pSSM, pThis->uCccPortNr);
SSMR3PutU64(pSSM, pThis->uCccTimeout);
SSMR3PutU32(pSSM, pThis->uCccNr);
SSMR3PutU32(pSSM, pThis->uCccCurrentNr);
SSMR3PutU32(pSSM, pThis->u32PortsInterrupted);
SSMR3PutBool(pSSM, pThis->fReset);
SSMR3PutBool(pSSM, pThis->f64BitAddr);
SSMR3PutBool(pSSM, pThis->fR0Enabled);
SSMR3PutBool(pSSM, pThis->fGCEnabled);
SSMR3PutBool(pSSM, pThis->fLegacyPortResetMethod);
/* Now every port. */
for (i = 0; i < AHCI_MAX_NR_PORTS_IMPL; i++)
{
Assert(pThis->ahciPort[i].cTasksActive == 0);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regCLB);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regCLBU);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regFB);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regFBU);
SSMR3PutGCPhys(pSSM, pThis->ahciPort[i].GCPhysAddrClb);
SSMR3PutGCPhys(pSSM, pThis->ahciPort[i].GCPhysAddrFb);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regIS);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regIE);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regCMD);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regTFD);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regSIG);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regSSTS);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regSCTL);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regSERR);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regSACT);
SSMR3PutU32(pSSM, pThis->ahciPort[i].regCI);
SSMR3PutU32(pSSM, pThis->ahciPort[i].PCHSGeometry.cCylinders);
SSMR3PutU32(pSSM, pThis->ahciPort[i].PCHSGeometry.cHeads);
SSMR3PutU32(pSSM, pThis->ahciPort[i].PCHSGeometry.cSectors);
SSMR3PutU64(pSSM, pThis->ahciPort[i].cTotalSectors);
SSMR3PutU32(pSSM, pThis->ahciPort[i].cMultSectors);
SSMR3PutU8(pSSM, pThis->ahciPort[i].uATATransferMode);
SSMR3PutBool(pSSM, pThis->ahciPort[i].fResetDevice);
SSMR3PutBool(pSSM, pThis->ahciPort[i].fPoweredOn);
SSMR3PutBool(pSSM, pThis->ahciPort[i].fSpunUp);
SSMR3PutU32(pSSM, pThis->ahciPort[i].u32TasksFinished);
SSMR3PutU32(pSSM, pThis->ahciPort[i].u32QueuedTasksFinished);
SSMR3PutU32(pSSM, pThis->ahciPort[i].u32CurrentCommandSlot);
/* ATAPI saved state. */
SSMR3PutBool(pSSM, pThis->ahciPort[i].fATAPI);
SSMR3PutMem(pSSM, &pThis->ahciPort[i].abATAPISense[0], sizeof(pThis->ahciPort[i].abATAPISense));
SSMR3PutU8(pSSM, pThis->ahciPort[i].cNotifiedMediaChange);
SSMR3PutU32(pSSM, pThis->ahciPort[i].MediaEventStatus);
}
return SSMR3PutU32(pSSM, UINT32_MAX); /* sanity/terminator */
}
/**
* Loads a saved legacy ATA emulated device state.
*
* @returns VBox status code.
* @param pSSM The handle to the saved state.
*/
static int ahciR3LoadLegacyEmulationState(PSSMHANDLE pSSM)
{
int rc;
uint32_t u32Version;
uint32_t u32;
uint32_t u32IOBuffer;
/* Test for correct version. */
rc = SSMR3GetU32(pSSM, &u32Version);
AssertRCReturn(rc, rc);
LogFlow(("LoadOldSavedStates u32Version = %d\n", u32Version));
if ( u32Version != ATA_CTL_SAVED_STATE_VERSION
&& u32Version != ATA_CTL_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE
&& u32Version != ATA_CTL_SAVED_STATE_VERSION_WITHOUT_EVENT_STATUS)
{
AssertMsgFailed(("u32Version=%d\n", u32Version));
return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
}
SSMR3Skip(pSSM, 19 + 5 * sizeof(bool) + sizeof(BMDMAState));
for (uint32_t j = 0; j < 2; j++)
{
SSMR3Skip(pSSM, 88 + 5 * sizeof(bool) );
if (u32Version > ATA_CTL_SAVED_STATE_VERSION_WITHOUT_FULL_SENSE)
SSMR3Skip(pSSM, 64);
else
SSMR3Skip(pSSM, 2);
/** @todo triple-check this hack after passthrough is working */
SSMR3Skip(pSSM, 1);
if (u32Version > ATA_CTL_SAVED_STATE_VERSION_WITHOUT_EVENT_STATUS)
SSMR3Skip(pSSM, 4);
SSMR3Skip(pSSM, sizeof(PDMLED));
SSMR3GetU32(pSSM, &u32IOBuffer);
if (u32IOBuffer)
SSMR3Skip(pSSM, u32IOBuffer);
}
rc = SSMR3GetU32(pSSM, &u32);
if (RT_FAILURE(rc))
return rc;
if (u32 != ~0U)
{
AssertMsgFailed(("u32=%#x expected ~0\n", u32));
rc = VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
return rc;
}
return VINF_SUCCESS;
}
/**
* Loads a saved AHCI device state.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to the saved state.
* @param uVersion The data unit version number.
* @param uPass The data pass.
*/
static DECLCALLBACK(int) ahciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
uint32_t u32;
int rc;
if ( uVersion > AHCI_SAVED_STATE_VERSION
|| uVersion < AHCI_SAVED_STATE_VERSION_VBOX_30)
return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
/* Deal with the priod after removing the saved IDE bits where the saved
state version remained unchanged. */
if ( uVersion == AHCI_SAVED_STATE_VERSION_IDE_EMULATION
&& SSMR3HandleRevision(pSSM) >= 79045
&& SSMR3HandleRevision(pSSM) < 79201)
uVersion++;
/*
* Check whether we have to resort to the legacy port reset method to
* prevent older BIOS versions from failing after a reset.
*/
if (uVersion <= AHCI_SAVED_STATE_VERSION_PRE_PORT_RESET_CHANGES)
pThis->fLegacyPortResetMethod = true;
/* Verify config. */
if (uVersion > AHCI_SAVED_STATE_VERSION_VBOX_30)
{
rc = SSMR3GetU32(pSSM, &u32);
AssertRCReturn(rc, rc);
if (u32 != pThis->cPortsImpl)
{
LogRel(("AHCI: Config mismatch: cPortsImpl - saved=%u config=%u\n", u32, pThis->cPortsImpl));
if ( u32 < pThis->cPortsImpl
|| u32 > AHCI_MAX_NR_PORTS_IMPL)
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch: cPortsImpl - saved=%u config=%u"),
u32, pThis->cPortsImpl);
}
for (uint32_t i = 0; i < AHCI_MAX_NR_PORTS_IMPL; i++)
{
bool fInUse;
rc = SSMR3GetBool(pSSM, &fInUse);
AssertRCReturn(rc, rc);
if (fInUse != (pThis->ahciPort[i].pDrvBase != NULL))
return SSMR3SetCfgError(pSSM, RT_SRC_POS,
N_("The %s VM is missing a device on port %u. Please make sure the source and target VMs have compatible storage configurations"),
fInUse ? "target" : "source", i );
if (uVersion > AHCI_SAVED_STATE_VERSION_PRE_HOTPLUG_FLAG)
{
bool fHotpluggable;
rc = SSMR3GetBool(pSSM, &fHotpluggable);
AssertRCReturn(rc, rc);
if (fHotpluggable != pThis->ahciPort[i].fHotpluggable)
return SSMR3SetCfgError(pSSM, RT_SRC_POS,
N_("AHCI: Port %u config mismatch: Hotplug flag - saved=%RTbool config=%RTbool\n"),
i, fHotpluggable, pThis->ahciPort[i].fHotpluggable);
}
else
Assert(pThis->ahciPort[i].fHotpluggable);
char szSerialNumber[AHCI_SERIAL_NUMBER_LENGTH+1];
rc = SSMR3GetStrZ(pSSM, szSerialNumber, sizeof(szSerialNumber));
AssertRCReturn(rc, rc);
if (strcmp(szSerialNumber, pThis->ahciPort[i].szSerialNumber))
LogRel(("AHCI: Port %u config mismatch: Serial number - saved='%s' config='%s'\n",
i, szSerialNumber, pThis->ahciPort[i].szSerialNumber));
char szFirmwareRevision[AHCI_FIRMWARE_REVISION_LENGTH+1];
rc = SSMR3GetStrZ(pSSM, szFirmwareRevision, sizeof(szFirmwareRevision));
AssertRCReturn(rc, rc);
if (strcmp(szFirmwareRevision, pThis->ahciPort[i].szFirmwareRevision))
LogRel(("AHCI: Port %u config mismatch: Firmware revision - saved='%s' config='%s'\n",
i, szFirmwareRevision, pThis->ahciPort[i].szFirmwareRevision));
char szModelNumber[AHCI_MODEL_NUMBER_LENGTH+1];
rc = SSMR3GetStrZ(pSSM, szModelNumber, sizeof(szModelNumber));
AssertRCReturn(rc, rc);
if (strcmp(szModelNumber, pThis->ahciPort[i].szModelNumber))
LogRel(("AHCI: Port %u config mismatch: Model number - saved='%s' config='%s'\n",
i, szModelNumber, pThis->ahciPort[i].szModelNumber));
}
static const char *s_apszIdeEmuPortNames[4] = { "PrimaryMaster", "PrimarySlave", "SecondaryMaster", "SecondarySlave" };
for (size_t i = 0; i < RT_ELEMENTS(s_apszIdeEmuPortNames); i++)
{
uint32_t iPort;
rc = CFGMR3QueryU32Def(pDevIns->pCfg, s_apszIdeEmuPortNames[i], &iPort, i);
AssertRCReturn(rc, rc);
uint32_t iPortSaved;
rc = SSMR3GetU32(pSSM, &iPortSaved);
AssertRCReturn(rc, rc);
if (iPortSaved != iPort)
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("IDE %s config mismatch: saved=%u config=%u"),
s_apszIdeEmuPortNames[i], iPortSaved, iPort);
}
}
if (uPass == SSM_PASS_FINAL)
{
/* Restore data. */
/* The main device structure. */
SSMR3GetU32(pSSM, &pThis->regHbaCap);
SSMR3GetU32(pSSM, &pThis->regHbaCtrl);
SSMR3GetU32(pSSM, &pThis->regHbaIs);
SSMR3GetU32(pSSM, &pThis->regHbaPi);
SSMR3GetU32(pSSM, &pThis->regHbaVs);
SSMR3GetU32(pSSM, &pThis->regHbaCccCtl);
SSMR3GetU32(pSSM, &pThis->regHbaCccPorts);
SSMR3GetU8(pSSM, &pThis->uCccPortNr);
SSMR3GetU64(pSSM, &pThis->uCccTimeout);
SSMR3GetU32(pSSM, &pThis->uCccNr);
SSMR3GetU32(pSSM, &pThis->uCccCurrentNr);
SSMR3GetU32(pSSM, (uint32_t *)&pThis->u32PortsInterrupted);
SSMR3GetBool(pSSM, &pThis->fReset);
SSMR3GetBool(pSSM, &pThis->f64BitAddr);
SSMR3GetBool(pSSM, &pThis->fR0Enabled);
SSMR3GetBool(pSSM, &pThis->fGCEnabled);
if (uVersion > AHCI_SAVED_STATE_VERSION_PRE_PORT_RESET_CHANGES)
SSMR3GetBool(pSSM, &pThis->fLegacyPortResetMethod);
/* Now every port. */
for (uint32_t i = 0; i < AHCI_MAX_NR_PORTS_IMPL; i++)
{
PAHCIPort pAhciPort = &pThis->ahciPort[i];
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regCLB);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regCLBU);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regFB);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regFBU);
SSMR3GetGCPhys(pSSM, (RTGCPHYS *)&pThis->ahciPort[i].GCPhysAddrClb);
SSMR3GetGCPhys(pSSM, (RTGCPHYS *)&pThis->ahciPort[i].GCPhysAddrFb);
SSMR3GetU32(pSSM, (uint32_t *)&pThis->ahciPort[i].regIS);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regIE);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regCMD);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regTFD);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regSIG);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regSSTS);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regSCTL);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].regSERR);
SSMR3GetU32(pSSM, (uint32_t *)&pThis->ahciPort[i].regSACT);
SSMR3GetU32(pSSM, (uint32_t *)&pThis->ahciPort[i].regCI);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].PCHSGeometry.cCylinders);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].PCHSGeometry.cHeads);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].PCHSGeometry.cSectors);
SSMR3GetU64(pSSM, &pThis->ahciPort[i].cTotalSectors);
SSMR3GetU32(pSSM, &pThis->ahciPort[i].cMultSectors);
SSMR3GetU8(pSSM, &pThis->ahciPort[i].uATATransferMode);
SSMR3GetBool(pSSM, &pThis->ahciPort[i].fResetDevice);
if (uVersion <= AHCI_SAVED_STATE_VERSION_VBOX_30)
SSMR3Skip(pSSM, AHCI_NR_COMMAND_SLOTS * sizeof(uint8_t)); /* no active data here */
if (uVersion < AHCI_SAVED_STATE_VERSION_IDE_EMULATION)
{
/* The old positions in the FIFO, not required. */
SSMR3Skip(pSSM, 2*sizeof(uint8_t));
}
SSMR3GetBool(pSSM, &pThis->ahciPort[i].fPoweredOn);
SSMR3GetBool(pSSM, &pThis->ahciPort[i].fSpunUp);
SSMR3GetU32(pSSM, (uint32_t *)&pThis->ahciPort[i].u32TasksFinished);
SSMR3GetU32(pSSM, (uint32_t *)&pThis->ahciPort[i].u32QueuedTasksFinished);
if (uVersion >= AHCI_SAVED_STATE_VERSION_IDE_EMULATION)
SSMR3GetU32(pSSM, (uint32_t *)&pThis->ahciPort[i].u32CurrentCommandSlot);
if (uVersion > AHCI_SAVED_STATE_VERSION_PRE_ATAPI)
{
SSMR3GetBool(pSSM, &pThis->ahciPort[i].fATAPI);
SSMR3GetMem(pSSM, pThis->ahciPort[i].abATAPISense, sizeof(pThis->ahciPort[i].abATAPISense));
SSMR3GetU8(pSSM, &pThis->ahciPort[i].cNotifiedMediaChange);
SSMR3GetU32(pSSM, (uint32_t*)&pThis->ahciPort[i].MediaEventStatus);
}
else if (pThis->ahciPort[i].fATAPI)
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch: atapi - saved=%false config=true"));
/* Check if we have tasks pending. */
uint32_t fTasksOutstanding = pAhciPort->regCI & ~pAhciPort->u32TasksFinished;
uint32_t fQueuedTasksOutstanding = pAhciPort->regSACT & ~pAhciPort->u32QueuedTasksFinished;
pAhciPort->u32TasksNew = fTasksOutstanding | fQueuedTasksOutstanding;
if (pAhciPort->u32TasksNew)
{
/*
* There are tasks pending. The VM was saved after a task failed
* because of non-fatal error. Set the redo flag.
*/
pAhciPort->fRedo = true;
}
}
if (uVersion <= AHCI_SAVED_STATE_VERSION_IDE_EMULATION)
{
for (uint32_t i = 0; i < 2; i++)
{
rc = ahciR3LoadLegacyEmulationState(pSSM);
if(RT_FAILURE(rc))
return rc;
}
}
rc = SSMR3GetU32(pSSM, &u32);
if (RT_FAILURE(rc))
return rc;
AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
}
return VINF_SUCCESS;
}
/* -=-=-=-=- device PDM interface -=-=-=-=- */
static DECLCALLBACK(void) ahciR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
{
uint32_t i;
PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
pAhci->pDevInsRC += offDelta;
pAhci->pHbaCccTimerRC = TMTimerRCPtr(pAhci->pHbaCccTimerR3);
pAhci->pNotifierQueueRC = PDMQueueRCPtr(pAhci->pNotifierQueueR3);
/* Relocate every port. */
for (i = 0; i < RT_ELEMENTS(pAhci->ahciPort); i++)
{
PAHCIPort pAhciPort = &pAhci->ahciPort[i];
pAhciPort->pAhciRC += offDelta;
pAhciPort->pDevInsRC += offDelta;
}
}
/**
* SCSI_GET_EVENT_STATUS_NOTIFICATION should return "medium removed" event
* from now on, regardless if there was a medium inserted or not.
*/
static void ahciMediumRemoved(PAHCIPort pAhciPort)
{
ASMAtomicWriteU32(&pAhciPort->MediaEventStatus, ATA_EVENT_STATUS_MEDIA_REMOVED);
}
/**
* SCSI_GET_EVENT_STATUS_NOTIFICATION should return "medium inserted". If
* there was already a medium inserted, don't forget to send the "medium
* removed" event first.
*/
static void ahciMediumInserted(PAHCIPort pAhciPort)
{
uint32_t OldStatus, NewStatus;
do
{
OldStatus = ASMAtomicReadU32(&pAhciPort->MediaEventStatus);
switch (OldStatus)
{
case ATA_EVENT_STATUS_MEDIA_CHANGED:
case ATA_EVENT_STATUS_MEDIA_REMOVED:
/* no change, we will send "medium removed" + "medium inserted" */
NewStatus = ATA_EVENT_STATUS_MEDIA_CHANGED;
break;
default:
NewStatus = ATA_EVENT_STATUS_MEDIA_NEW;
break;
}
} while (!ASMAtomicCmpXchgU32(&pAhciPort->MediaEventStatus, NewStatus, OldStatus));
}
/**
* Called when a media is mounted.
*
* @param pInterface Pointer to the interface structure containing the called function pointer.
*/
static DECLCALLBACK(void) ahciR3MountNotify(PPDMIMOUNTNOTIFY pInterface)
{
PAHCIPort pAhciPort = PDMIMOUNTNOTIFY_2_PAHCIPORT(pInterface);
Log(("%s: changing LUN#%d\n", __FUNCTION__, pAhciPort->iLUN));
/* Ignore the call if we're called while being attached. */
if (!pAhciPort->pDrvBlock)
return;
if (pAhciPort->fATAPI)
{
pAhciPort->cTotalSectors = pAhciPort->pDrvBlock->pfnGetSize(pAhciPort->pDrvBlock) / 2048;
LogRel(("AHCI: LUN#%d: CD/DVD, total number of sectors %Ld, passthrough unchanged\n", pAhciPort->iLUN, pAhciPort->cTotalSectors));
/* Report media changed in TEST UNIT and other (probably incorrect) places. */
if (pAhciPort->cNotifiedMediaChange < 2)
pAhciPort->cNotifiedMediaChange = 2;
ahciMediumInserted(pAhciPort);
ahciMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN);
}
else
AssertMsgFailed(("Hard disks don't have a mount interface!\n"));
}
/**
* Called when a media is unmounted
* @param pInterface Pointer to the interface structure containing the called function pointer.
*/
static DECLCALLBACK(void) ahciR3UnmountNotify(PPDMIMOUNTNOTIFY pInterface)
{
PAHCIPort pAhciPort = PDMIMOUNTNOTIFY_2_PAHCIPORT(pInterface);
Log(("%s:\n", __FUNCTION__));
pAhciPort->cTotalSectors = 0;
if (pAhciPort->fATAPI)
{
/*
* 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.
*/
pAhciPort->cNotifiedMediaChange = 4;
ahciMediumRemoved(pAhciPort);
ahciMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN);
}
else
AssertMsgFailed(("Hard disks don't have a mount interface!\n"));
}
/**
* Configure the attached device for a port.
*
* Used by ahciR3Construct and ahciR3Attach.
*
* @returns VBox status code
* @param pDevIns The device instance data.
* @param pAhciPort The port for which the device is to be configured.
*/
static int ahciR3ConfigureLUN(PPDMDEVINS pDevIns, PAHCIPort pAhciPort)
{
int rc = VINF_SUCCESS;
PDMBLOCKTYPE enmType;
/*
* Query the block and blockbios interfaces.
*/
pAhciPort->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pAhciPort->pDrvBase, PDMIBLOCK);
if (!pAhciPort->pDrvBlock)
{
AssertMsgFailed(("Configuration error: LUN#%d hasn't a block interface!\n", pAhciPort->iLUN));
return VERR_PDM_MISSING_INTERFACE;
}
pAhciPort->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pAhciPort->pDrvBase, PDMIBLOCKBIOS);
if (!pAhciPort->pDrvBlockBios)
{
AssertMsgFailed(("Configuration error: LUN#%d hasn't a block BIOS interface!\n", pAhciPort->iLUN));
return VERR_PDM_MISSING_INTERFACE;
}
pAhciPort->pDrvMount = PDMIBASE_QUERY_INTERFACE(pAhciPort->pDrvBase, PDMIMOUNT);
/* Try to get the optional async block interface. */
pAhciPort->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pAhciPort->pDrvBase, PDMIBLOCKASYNC);
/*
* Validate type.
*/
enmType = pAhciPort->pDrvBlock->pfnGetType(pAhciPort->pDrvBlock);
if ( enmType != PDMBLOCKTYPE_HARD_DISK
&& enmType != PDMBLOCKTYPE_CDROM
&& enmType != PDMBLOCKTYPE_DVD)
{
AssertMsgFailed(("Configuration error: LUN#%d isn't a disk or cd/dvd. enmType=%d\n", pAhciPort->iLUN, enmType));
return VERR_PDM_UNSUPPORTED_BLOCK_TYPE;
}
if ( (enmType == PDMBLOCKTYPE_CDROM || enmType == PDMBLOCKTYPE_DVD)
&& !pAhciPort->pDrvMount)
{
AssertMsgFailed(("Internal error: CD/DVD-ROM without a mountable interface\n"));
return VERR_INTERNAL_ERROR;
}
pAhciPort->fATAPI = (enmType == PDMBLOCKTYPE_CDROM || enmType == PDMBLOCKTYPE_DVD);
pAhciPort->fATAPIPassthrough = pAhciPort->fATAPI ? (pAhciPort->pDrvBlock->pfnSendCmd != NULL) : false;
if (pAhciPort->fATAPI)
{
pAhciPort->cTotalSectors = pAhciPort->pDrvBlock->pfnGetSize(pAhciPort->pDrvBlock) / 2048;
pAhciPort->PCHSGeometry.cCylinders = 0;
pAhciPort->PCHSGeometry.cHeads = 0;
pAhciPort->PCHSGeometry.cSectors = 0;
LogRel(("AHCI LUN#%d: CD/DVD, total number of sectors %Ld, passthrough %s\n", pAhciPort->iLUN, pAhciPort->cTotalSectors, (pAhciPort->fATAPIPassthrough ? "enabled" : "disabled")));
}
else
{
pAhciPort->cbSector = pAhciPort->pDrvBlock->pfnGetSectorSize(pAhciPort->pDrvBlock);
pAhciPort->cTotalSectors = pAhciPort->pDrvBlock->pfnGetSize(pAhciPort->pDrvBlock) / pAhciPort->cbSector;
rc = pAhciPort->pDrvBlockBios->pfnGetPCHSGeometry(pAhciPort->pDrvBlockBios,
&pAhciPort->PCHSGeometry);
if (rc == VERR_PDM_MEDIA_NOT_MOUNTED)
{
pAhciPort->PCHSGeometry.cCylinders = 0;
pAhciPort->PCHSGeometry.cHeads = 16; /*??*/
pAhciPort->PCHSGeometry.cSectors = 63; /*??*/
}
else if (rc == VERR_PDM_GEOMETRY_NOT_SET)
{
pAhciPort->PCHSGeometry.cCylinders = 0; /* autodetect marker */
rc = VINF_SUCCESS;
}
AssertRC(rc);
if ( pAhciPort->PCHSGeometry.cCylinders == 0
|| pAhciPort->PCHSGeometry.cHeads == 0
|| pAhciPort->PCHSGeometry.cSectors == 0)
{
uint64_t cCylinders = pAhciPort->cTotalSectors / (16 * 63);
pAhciPort->PCHSGeometry.cCylinders = RT_MAX(RT_MIN(cCylinders, 16383), 1);
pAhciPort->PCHSGeometry.cHeads = 16;
pAhciPort->PCHSGeometry.cSectors = 63;
/* Set the disk geometry information. Ignore errors. */
pAhciPort->pDrvBlockBios->pfnSetPCHSGeometry(pAhciPort->pDrvBlockBios,
&pAhciPort->PCHSGeometry);
rc = VINF_SUCCESS;
}
LogRel(("AHCI: LUN#%d: disk, PCHS=%u/%u/%u, total number of sectors %Ld\n",
pAhciPort->iLUN, pAhciPort->PCHSGeometry.cCylinders,
pAhciPort->PCHSGeometry.cHeads, pAhciPort->PCHSGeometry.cSectors,
pAhciPort->cTotalSectors));
if (pAhciPort->pDrvBlock->pfnDiscard)
LogRel(("AHCI: LUN#%d: Enabled TRIM support\n", pAhciPort->iLUN));
}
return rc;
}
/**
* Callback employed by ahciR3Suspend and ahciR3PowerOff..
*
* @returns true if we've quiesced, false if we're still working.
* @param pDevIns The device instance.
*/
static DECLCALLBACK(bool) ahciR3IsAsyncSuspendOrPowerOffDone(PPDMDEVINS pDevIns)
{
if (!ahciR3AllAsyncIOIsFinished(pDevIns))
return false;
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
ASMAtomicWriteBool(&pThis->fSignalIdle, false);
return true;
}
/**
* Common worker for ahciR3Suspend and ahciR3PowerOff.
*/
static void ahciR3SuspendOrPowerOff(PPDMDEVINS pDevIns)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
ASMAtomicWriteBool(&pThis->fSignalIdle, true);
if (!ahciR3AllAsyncIOIsFinished(pDevIns))
PDMDevHlpSetAsyncNotification(pDevIns, ahciR3IsAsyncSuspendOrPowerOffDone);
else
ASMAtomicWriteBool(&pThis->fSignalIdle, false);
}
/**
* Suspend notification.
*
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(void) ahciR3Suspend(PPDMDEVINS pDevIns)
{
Log(("ahciR3Suspend\n"));
ahciR3SuspendOrPowerOff(pDevIns);
}
/**
* Resume notification.
*
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(void) ahciR3Resume(PPDMDEVINS pDevIns)
{
PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
/*
* Check if one of the ports has pending tasks.
* Queue a notification item again in this case.
*/
for (unsigned i = 0; i < RT_ELEMENTS(pAhci->ahciPort); i++)
{
PAHCIPort pAhciPort = &pAhci->ahciPort[i];
if (pAhciPort->u32TasksRedo)
{
PDEVPORTNOTIFIERQUEUEITEM pItem = (PDEVPORTNOTIFIERQUEUEITEM)PDMQueueAlloc(pAhci->CTX_SUFF(pNotifierQueue));
AssertMsg(pItem, ("Allocating item for queue failed\n"));
pAhciPort->u32TasksNew |= pAhciPort->u32TasksRedo;
pAhciPort->u32TasksRedo = 0;
Assert(pAhciPort->fRedo);
pAhciPort->fRedo = false;
pItem->iPort = pAhci->ahciPort[i].iLUN;
PDMQueueInsert(pAhci->CTX_SUFF(pNotifierQueue), (PPDMQUEUEITEMCORE)pItem);
}
}
Log(("%s:\n", __FUNCTION__));
}
/**
* Initializes the VPD data of a attached device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pAhciPort The attached device.
* @param szName Name of the port to get the CFGM node.
*/
static int ahciR3VpdInit(PPDMDEVINS pDevIns, PAHCIPort pAhciPort, const char *pszName)
{
int rc = VINF_SUCCESS;
PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
/* Generate a default serial number. */
char szSerial[AHCI_SERIAL_NUMBER_LENGTH+1];
RTUUID Uuid;
if (pAhciPort->pDrvBlock)
rc = pAhciPort->pDrvBlock->pfnGetUuid(pAhciPort->pDrvBlock, &Uuid);
else
RTUuidClear(&Uuid);
if (RT_FAILURE(rc) || RTUuidIsNull(&Uuid))
{
/* Generate a predictable serial for drives which don't have a UUID. */
RTStrPrintf(szSerial, sizeof(szSerial), "VB%x-1a2b3c4d",
pAhciPort->iLUN);
}
else
RTStrPrintf(szSerial, sizeof(szSerial), "VB%08x-%08x", Uuid.au32[0], Uuid.au32[3]);
/* Get user config if present using defaults otherwise. */
PCFGMNODE pCfgNode = CFGMR3GetChild(pDevIns->pCfg, pszName);
rc = CFGMR3QueryStringDef(pCfgNode, "SerialNumber", pAhciPort->szSerialNumber, sizeof(pAhciPort->szSerialNumber),
szSerial);
if (RT_FAILURE(rc))
{
if (rc == VERR_CFGM_NOT_ENOUGH_SPACE)
return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER,
N_("AHCI configuration error: \"SerialNumber\" is longer than 20 bytes"));
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read \"SerialNumber\" as string"));
}
rc = CFGMR3QueryStringDef(pCfgNode, "FirmwareRevision", pAhciPort->szFirmwareRevision, sizeof(pAhciPort->szFirmwareRevision),
"1.0");
if (RT_FAILURE(rc))
{
if (rc == VERR_CFGM_NOT_ENOUGH_SPACE)
return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER,
N_("AHCI configuration error: \"FirmwareRevision\" is longer than 8 bytes"));
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read \"FirmwareRevision\" as string"));
}
rc = CFGMR3QueryStringDef(pCfgNode, "ModelNumber", pAhciPort->szModelNumber, sizeof(pAhciPort->szModelNumber),
pAhciPort->fATAPI ? "VBOX CD-ROM" : "VBOX HARDDISK");
if (RT_FAILURE(rc))
{
if (rc == VERR_CFGM_NOT_ENOUGH_SPACE)
return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER,
N_("AHCI configuration error: \"ModelNumber\" is longer than 40 bytes"));
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read \"ModelNumber\" as string"));
}
rc = CFGMR3QueryBoolDef(pCfgNode, "NonRotationalMedium", &pAhciPort->fNonRotational, false);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read \"NonRotationalMedium\" as boolean"));
rc = CFGMR3QueryU8Def(pCfgNode, "LogicalSectorsPerPhysical", &pAhciPort->cLogSectorsPerPhysicalExp, 0);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read \"LogicalSectorsPerPhysical\" as integer"));
if (pAhciPort->cLogSectorsPerPhysicalExp >= 16)
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: \"LogicalSectorsPerPhysical\" must be between 0 and 15"));
/* There are three other identification strings for CD drives used for INQUIRY */
if (pAhciPort->fATAPI)
{
rc = CFGMR3QueryStringDef(pCfgNode, "ATAPIVendorId", pAhciPort->szInquiryVendorId, sizeof(pAhciPort->szInquiryVendorId),
"VBOX");
if (RT_FAILURE(rc))
{
if (rc == VERR_CFGM_NOT_ENOUGH_SPACE)
return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER,
N_("AHCI configuration error: \"ATAPIVendorId\" is longer than 16 bytes"));
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read \"ATAPIVendorId\" as string"));
}
rc = CFGMR3QueryStringDef(pCfgNode, "ATAPIProductId", pAhciPort->szInquiryProductId, sizeof(pAhciPort->szInquiryProductId),
"CD-ROM");
if (RT_FAILURE(rc))
{
if (rc == VERR_CFGM_NOT_ENOUGH_SPACE)
return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER,
N_("AHCI configuration error: \"ATAPIProductId\" is longer than 16 bytes"));
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read \"ATAPIProductId\" as string"));
}
rc = CFGMR3QueryStringDef(pCfgNode, "ATAPIRevision", pAhciPort->szInquiryRevision, sizeof(pAhciPort->szInquiryRevision),
"1.0");
if (RT_FAILURE(rc))
{
if (rc == VERR_CFGM_NOT_ENOUGH_SPACE)
return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER,
N_("AHCI configuration error: \"ATAPIRevision\" is longer than 4 bytes"));
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read \"ATAPIRevision\" as string"));
}
}
return rc;
}
/**
* Detach notification.
*
* One harddisk at one port has been unplugged.
* The VM is suspended at this point.
*
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
static DECLCALLBACK(void) ahciR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
PAHCIPort pAhciPort = &pAhci->ahciPort[iLUN];
int rc = VINF_SUCCESS;
Log(("%s:\n", __FUNCTION__));
AssertMsg(iLUN < pAhci->cPortsImpl, ("iLUN=%u", iLUN));
AssertMsgReturnVoid( pAhciPort->fHotpluggable
|| (fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG),
("AHCI: Port %d is not marked hotpluggable\n", pAhciPort->iLUN));
if (pAhciPort->pAsyncIOThread)
{
int rcThread;
/* Destroy the thread. */
rc = PDMR3ThreadDestroy(pAhciPort->pAsyncIOThread, &rcThread);
if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
AssertMsgFailed(("%s Failed to destroy async IO thread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
pAhciPort->pAsyncIOThread = NULL;
pAhciPort->fWrkThreadSleeping = true;
}
if (pAhciPort->fATAPI)
ahciMediumRemoved(pAhciPort);
if (!(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG))
{
/*
* Inform the guest about the removed device.
*/
pAhciPort->regSSTS = 0;
pAhciPort->regSIG = 0;
/*
* Clear CR bit too to prevent submission of new commands when CI is written
* (AHCI Spec 1.2: 7.4 Interaction of the Command List and Port Change Status).
*/
ASMAtomicAndU32(&pAhciPort->regCMD, ~(AHCI_PORT_CMD_CPS | AHCI_PORT_CMD_CR));
ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_CPDS | AHCI_PORT_IS_PRCS | AHCI_PORT_IS_PCS);
ASMAtomicOrU32(&pAhciPort->regSERR, AHCI_PORT_SERR_X | AHCI_PORT_SERR_N);
if ( (pAhciPort->regIE & AHCI_PORT_IE_CPDE)
|| (pAhciPort->regIE & AHCI_PORT_IE_PCE)
|| (pAhciPort->regIE & AHCI_PORT_IE_PRCE))
ahciHbaSetInterrupt(pAhciPort->CTX_SUFF(pAhci), pAhciPort->iLUN, VERR_IGNORED);
}
/*
* Zero some important members.
*/
pAhciPort->pDrvBase = NULL;
pAhciPort->pDrvBlock = NULL;
pAhciPort->pDrvBlockAsync = NULL;
pAhciPort->pDrvBlockBios = NULL;
}
/**
* Attach command.
*
* This is called when we change block driver for one port.
* The VM is suspended at this point.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
static DECLCALLBACK(int) ahciR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
PAHCIPort pAhciPort = &pThis->ahciPort[iLUN];
int rc;
Log(("%s:\n", __FUNCTION__));
/* the usual paranoia */
AssertMsg(iLUN < pThis->cPortsImpl, ("iLUN=%u", iLUN));
AssertRelease(!pAhciPort->pDrvBase);
AssertRelease(!pAhciPort->pDrvBlock);
AssertRelease(!pAhciPort->pDrvBlockAsync);
Assert(pAhciPort->iLUN == iLUN);
AssertMsgReturn( pAhciPort->fHotpluggable
|| (fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG),
("AHCI: Port %d is not marked hotpluggable\n", pAhciPort->iLUN),
VERR_INVALID_PARAMETER);
/*
* Try attach the block device and get the interfaces,
* required as well as optional.
*/
rc = PDMDevHlpDriverAttach(pDevIns, pAhciPort->iLUN, &pAhciPort->IBase, &pAhciPort->pDrvBase, NULL);
if (RT_SUCCESS(rc))
{
rc = ahciR3ConfigureLUN(pDevIns, pAhciPort);
/*
* In case there is a medium inserted.
*/
ahciMediumInserted(pAhciPort);
ahciMediumTypeSet(pAhciPort, ATA_MEDIA_TYPE_UNKNOWN);
}
else
AssertMsgFailed(("Failed to attach LUN#%d. rc=%Rrc\n", pAhciPort->iLUN, rc));
if (RT_FAILURE(rc))
{
pAhciPort->pDrvBase = NULL;
pAhciPort->pDrvBlock = NULL;
}
else
{
char szName[24];
RTStrPrintf(szName, sizeof(szName), "Port%d", iLUN);
if ( pAhciPort->pDrvBlockAsync
&& !pAhciPort->fATAPI)
pAhciPort->fAsyncInterface = true;
else
pAhciPort->fAsyncInterface = false;
rc = SUPSemEventCreate(pThis->pSupDrvSession, &pAhciPort->hEvtProcess);
if (RT_FAILURE(rc))
return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
N_("AHCI: Failed to create SUP event semaphore"));
/* Create the async IO thread. */
rc = PDMDevHlpThreadCreate(pDevIns, &pAhciPort->pAsyncIOThread, pAhciPort, ahciAsyncIOLoop, ahciAsyncIOLoopWakeUp, 0,
RTTHREADTYPE_IO, szName);
if (RT_FAILURE(rc))
return rc;
/*
* Init vendor product data.
*/
if (RT_SUCCESS(rc))
rc = ahciR3VpdInit(pDevIns, pAhciPort, szName);
/* Inform the guest about the added device in case of hotplugging. */
if ( RT_SUCCESS(rc)
&& !(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG))
{
AssertMsgReturn(pAhciPort->fHotpluggable,
("AHCI: Port %d is not marked hotpluggable\n", pAhciPort->iLUN),
VERR_NOT_SUPPORTED);
/*
* Initialize registers
*/
ASMAtomicOrU32(&pAhciPort->regCMD, AHCI_PORT_CMD_CPS);
ASMAtomicOrU32(&pAhciPort->regIS, AHCI_PORT_IS_CPDS | AHCI_PORT_IS_PRCS | AHCI_PORT_IS_PCS);
ASMAtomicOrU32(&pAhciPort->regSERR, AHCI_PORT_SERR_X | AHCI_PORT_SERR_N);
if (pAhciPort->fATAPI)
pAhciPort->regSIG = AHCI_PORT_SIG_ATAPI;
else
pAhciPort->regSIG = AHCI_PORT_SIG_DISK;
pAhciPort->regSSTS = (0x01 << 8) | /* Interface is active. */
(0x02 << 4) | /* Generation 2 (3.0GBps) speed. */
(0x03 << 0); /* Device detected and communication established. */
if ( (pAhciPort->regIE & AHCI_PORT_IE_CPDE)
|| (pAhciPort->regIE & AHCI_PORT_IE_PCE)
|| (pAhciPort->regIE & AHCI_PORT_IE_PRCE))
ahciHbaSetInterrupt(pAhciPort->CTX_SUFF(pAhci), pAhciPort->iLUN, VERR_IGNORED);
}
}
return rc;
}
/**
* Common reset worker.
*
* @param pDevIns The device instance data.
*/
static int ahciR3ResetCommon(PPDMDEVINS pDevIns, bool fConstructor)
{
PAHCI pAhci = PDMINS_2_DATA(pDevIns, PAHCI);
ahciHBAReset(pAhci);
/* Hardware reset for the ports. */
for (uint32_t i = 0; i < RT_ELEMENTS(pAhci->ahciPort); i++)
ahciPortHwReset(&pAhci->ahciPort[i]);
return VINF_SUCCESS;
}
/**
* Callback employed by ahciR3Reset.
*
* @returns true if we've quiesced, false if we're still working.
* @param pDevIns The device instance.
*/
static DECLCALLBACK(bool) ahciR3IsAsyncResetDone(PPDMDEVINS pDevIns)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
if (!ahciR3AllAsyncIOIsFinished(pDevIns))
return false;
ASMAtomicWriteBool(&pThis->fSignalIdle, false);
ahciR3ResetCommon(pDevIns, false /*fConstructor*/);
return true;
}
/**
* Reset notification.
*
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(void) ahciR3Reset(PPDMDEVINS pDevIns)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
ASMAtomicWriteBool(&pThis->fSignalIdle, true);
if (!ahciR3AllAsyncIOIsFinished(pDevIns))
PDMDevHlpSetAsyncNotification(pDevIns, ahciR3IsAsyncResetDone);
else
{
ASMAtomicWriteBool(&pThis->fSignalIdle, false);
ahciR3ResetCommon(pDevIns, false /*fConstructor*/);
}
}
/**
* Poweroff notification.
*
* @param pDevIns Pointer to the device instance
*/
static DECLCALLBACK(void) ahciR3PowerOff(PPDMDEVINS pDevIns)
{
Log(("achiR3PowerOff\n"));
ahciR3SuspendOrPowerOff(pDevIns);
}
/**
* Destroy a driver instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(int) ahciR3Destruct(PPDMDEVINS pDevIns)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
int rc = VINF_SUCCESS;
PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
/*
* At this point the async I/O thread is suspended and will not enter
* this module again. So, no coordination is needed here and PDM
* will take care of terminating and cleaning up the thread.
*/
if (PDMCritSectIsInitialized(&pThis->lock))
{
TMR3TimerDestroy(pThis->CTX_SUFF(pHbaCccTimer));
pThis->CTX_SUFF(pHbaCccTimer) = NULL;
Log(("%s: Destruct every port\n", __FUNCTION__));
for (unsigned iActPort = 0; iActPort < pThis->cPortsImpl; iActPort++)
{
PAHCIPort pAhciPort = &pThis->ahciPort[iActPort];
if (pAhciPort->hEvtProcess != NIL_SUPSEMEVENT)
{
SUPSemEventClose(pThis->pSupDrvSession, pAhciPort->hEvtProcess);
pAhciPort->hEvtProcess = NIL_SUPSEMEVENT;
}
/* Free all cached tasks. */
for (uint32_t i = 0; i < AHCI_NR_COMMAND_SLOTS; i++)
if (pAhciPort->aCachedTasks[i])
{
RTMemFree(pAhciPort->aCachedTasks[i]);
pAhciPort->aCachedTasks[i] = NULL;
}
}
PDMR3CritSectDelete(&pThis->lock);
}
return rc;
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
static DECLCALLBACK(int) ahciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
PAHCI pThis = PDMINS_2_DATA(pDevIns, PAHCI);
PPDMIBASE pBase;
int rc = VINF_SUCCESS;
unsigned i = 0;
bool fGCEnabled = false;
bool fR0Enabled = false;
uint32_t cbTotalBufferSize = 0;
PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
LogFlowFunc(("pThis=%#p\n", pThis));
/*
* Validate and read configuration.
*/
if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0"
"R0Enabled\0"
"PrimaryMaster\0"
"PrimarySlave\0"
"SecondaryMaster\0"
"SecondarySlave\0"
"PortCount\0"
"UseAsyncInterfaceIfAvailable\0"
"Bootable\0"
"CmdSlotsAvail\0"))
return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("AHCI configuration error: unknown option specified"));
rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read GCEnabled as boolean"));
Log(("%s: fGCEnabled=%d\n", __FUNCTION__, fGCEnabled));
rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read R0Enabled as boolean"));
Log(("%s: fR0Enabled=%d\n", __FUNCTION__, fR0Enabled));
rc = CFGMR3QueryU32Def(pCfg, "PortCount", &pThis->cPortsImpl, AHCI_MAX_NR_PORTS_IMPL);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read PortCount as integer"));
Log(("%s: cPortsImpl=%u\n", __FUNCTION__, pThis->cPortsImpl));
if (pThis->cPortsImpl > AHCI_MAX_NR_PORTS_IMPL)
return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
N_("AHCI configuration error: PortCount=%u should not exceed %u"),
pThis->cPortsImpl, AHCI_MAX_NR_PORTS_IMPL);
if (pThis->cPortsImpl < 1)
return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
N_("AHCI configuration error: PortCount=%u should be at least 1"),
pThis->cPortsImpl);
rc = CFGMR3QueryBoolDef(pCfg, "UseAsyncInterfaceIfAvailable", &pThis->fUseAsyncInterfaceIfAvailable, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read UseAsyncInterfaceIfAvailable as boolean"));
rc = CFGMR3QueryBoolDef(pCfg, "Bootable", &pThis->fBootable, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read Bootable as boolean"));
rc = CFGMR3QueryU32Def(pCfg, "CmdSlotsAvail", &pThis->cCmdSlotsAvail, AHCI_NR_COMMAND_SLOTS);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read CmdSlotsAvail as integer"));
Log(("%s: cCmdSlotsAvail=%u\n", __FUNCTION__, pThis->cCmdSlotsAvail));
if (pThis->cCmdSlotsAvail > AHCI_NR_COMMAND_SLOTS)
return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
N_("AHCI configuration error: CmdSlotsAvail=%u should not exceed %u"),
pThis->cPortsImpl, AHCI_NR_COMMAND_SLOTS);
if (pThis->cCmdSlotsAvail < 1)
return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
N_("AHCI configuration error: CmdSlotsAvail=%u should be at least 1"),
pThis->cCmdSlotsAvail);
/*
* Initialize the instance data (everything touched by the destructor need
* to be initialized here!).
*/
pThis->fR0Enabled = fR0Enabled;
pThis->fGCEnabled = fGCEnabled;
pThis->pDevInsR3 = pDevIns;
pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
pThis->pSupDrvSession = PDMDevHlpGetSupDrvSession(pDevIns);
PCIDevSetVendorId (&pThis->dev, 0x8086); /* Intel */
PCIDevSetDeviceId (&pThis->dev, 0x2829); /* ICH-8M */
PCIDevSetCommand (&pThis->dev, 0x0000);
#ifdef VBOX_WITH_MSI_DEVICES
PCIDevSetStatus (&pThis->dev, VBOX_PCI_STATUS_CAP_LIST);
PCIDevSetCapabilityList(&pThis->dev, 0x80);
#else
PCIDevSetCapabilityList(&pThis->dev, 0x70);
#endif
PCIDevSetRevisionId (&pThis->dev, 0x02);
PCIDevSetClassProg (&pThis->dev, 0x01);
PCIDevSetClassSub (&pThis->dev, 0x06);
PCIDevSetClassBase (&pThis->dev, 0x01);
PCIDevSetBaseAddress (&pThis->dev, 5, false, false, false, 0x00000000);
PCIDevSetInterruptLine(&pThis->dev, 0x00);
PCIDevSetInterruptPin (&pThis->dev, 0x01);
pThis->dev.config[0x70] = VBOX_PCI_CAP_ID_PM; /* Capability ID: PCI Power Management Interface */
pThis->dev.config[0x71] = 0xa8; /* next */
pThis->dev.config[0x72] = 0x03; /* version ? */
pThis->dev.config[0x90] = 0x40; /* AHCI mode. */
pThis->dev.config[0x92] = 0x3f;
pThis->dev.config[0x94] = 0x80;
pThis->dev.config[0x95] = 0x01;
pThis->dev.config[0x97] = 0x78;
pThis->dev.config[0xa8] = 0x12; /* SATACR capability */
pThis->dev.config[0xa9] = 0x00; /* next */
PCIDevSetWord(&pThis->dev, 0xaa, 0x0010); /* Revision */
PCIDevSetDWord(&pThis->dev, 0xac, 0x00000028); /* SATA Capability Register 1 */
pThis->cThreadsActive = 0;
/* Initialize port members. */
for (i = 0; i < AHCI_MAX_NR_PORTS_IMPL; i++)
{
PAHCIPort pAhciPort = &pThis->ahciPort[i];
pAhciPort->pDevInsR3 = pDevIns;
pAhciPort->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pAhciPort->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
pAhciPort->iLUN = i;
pAhciPort->pAhciR3 = pThis;
pAhciPort->pAhciR0 = PDMINS_2_DATA_R0PTR(pDevIns);
pAhciPort->pAhciRC = PDMINS_2_DATA_RCPTR(pDevIns);
pAhciPort->Led.u32Magic = PDMLED_MAGIC;
pAhciPort->pDrvBase = NULL;
pAhciPort->pAsyncIOThread = NULL;
pAhciPort->hEvtProcess = NIL_SUPSEMEVENT;
pAhciPort->fHotpluggable = true;
}
/*
* Init locks, using explicit locking where necessary.
*/
rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpCritSectInit(pDevIns, &pThis->lock, RT_SRC_POS, "AHCI#%u", iInstance);
if (RT_FAILURE(rc))
{
Log(("%s: Failed to create critical section.\n", __FUNCTION__));
return rc;
}
/*
* Register the PCI device, it's I/O regions.
*/
rc = PDMDevHlpPCIRegister (pDevIns, &pThis->dev);
if (RT_FAILURE(rc))
return rc;
#ifdef VBOX_WITH_MSI_DEVICES
PDMMSIREG MsiReg;
RT_ZERO(MsiReg);
MsiReg.cMsiVectors = 1;
MsiReg.iMsiCapOffset = 0x80;
MsiReg.iMsiNextOffset = 0x70;
rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
if (RT_FAILURE(rc))
{
LogRel(("Chipset cannot do MSI: %Rrc\n", rc));
PCIDevSetCapabilityList(&pThis->dev, 0x70);
/* That's OK, we can work without MSI */
}
#endif
/*
* Solaris 10 U5 fails to map the AHCI register space when the sets (0..5) for the legacy
* IDE registers are not available.
* We set up "fake" entries in the PCI configuration register.
* That means they are available but read and writes from/to them have no effect.
* No guest should access them anyway because the controller is marked as AHCI in the Programming interface
* and we don't have an option to change to IDE emulation (real hardware provides an option in the BIOS
* to switch to it which also changes device Id and other things in the PCI configuration space).
*/
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 8, PCI_ADDRESS_SPACE_IO, ahciR3LegacyFakeIORangeMap);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI cannot register PCI I/O region"));
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1, 1, PCI_ADDRESS_SPACE_IO, ahciR3LegacyFakeIORangeMap);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI cannot register PCI I/O region"));
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2, 8, PCI_ADDRESS_SPACE_IO, ahciR3LegacyFakeIORangeMap);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI cannot register PCI I/O region"));
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 3, 1, PCI_ADDRESS_SPACE_IO, ahciR3LegacyFakeIORangeMap);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI cannot register PCI I/O region"));
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 4, 0x10, PCI_ADDRESS_SPACE_IO, ahciR3IdxDataIORangeMap);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI cannot register PCI I/O region for BMDMA"));
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 5, 4352, PCI_ADDRESS_SPACE_MEM, ahciR3MMIOMap);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI cannot register PCI memory region for registers"));
/* Create the timer for command completion coalescing feature. */
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ahciCccTimer, pThis,
TMTIMER_FLAGS_NO_CRIT_SECT, "AHCI CCC Timer", &pThis->pHbaCccTimerR3);
if (RT_FAILURE(rc))
{
AssertMsgFailed(("pfnTMTimerCreate -> %Rrc\n", rc));
return rc;
}
pThis->pHbaCccTimerR0 = TMTimerR0Ptr(pThis->pHbaCccTimerR3);
pThis->pHbaCccTimerRC = TMTimerRCPtr(pThis->pHbaCccTimerR3);
/* Status LUN. */
pThis->IBase.pfnQueryInterface = ahciR3Status_QueryInterface;
pThis->ILeds.pfnQueryStatusLed = ahciR3Status_QueryStatusLed;
/*
* Create the notification queue.
*
* We need 2 items for every port because of SMP races.
*/
rc = PDMDevHlpQueueCreate(pDevIns, sizeof(DEVPORTNOTIFIERQUEUEITEM), AHCI_MAX_NR_PORTS_IMPL * 2, 0,
ahciNotifyQueueConsumer, true, "AHCI-Xmit", &pThis->pNotifierQueueR3);
if (RT_FAILURE(rc))
return rc;
pThis->pNotifierQueueR0 = PDMQueueR0Ptr(pThis->pNotifierQueueR3);
pThis->pNotifierQueueRC = PDMQueueRCPtr(pThis->pNotifierQueueR3);
/* Initialize static members on every port. */
for (i = 0; i < AHCI_MAX_NR_PORTS_IMPL; i++)
{
PAHCIPort pAhciPort = &pThis->ahciPort[i];
PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatDMA, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
"Number of DMA transfers.", "/Devices/SATA%d/Port%d/DMA", iInstance, i);
PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
"Amount of data read.", "/Devices/SATA%d/Port%d/ReadBytes", iInstance, i);
PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
"Amount of data written.", "/Devices/SATA%d/Port%d/WrittenBytes", iInstance, i);
PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatIORequestsPerSecond, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
"Number of processed I/O requests per second.", "/Devices/SATA%d/Port%d/IORequestsPerSecond", iInstance, i);
#ifdef VBOX_WITH_STATISTICS
PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatProfileProcessTime, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
"Amount of time to process one request.", "/Devices/SATA%d/Port%d/ProfileProcessTime", iInstance, i);
PDMDevHlpSTAMRegisterF(pDevIns, &pAhciPort->StatProfileReadWrite, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_NS_PER_CALL,
"Amount of time for the read/write operation to complete.", "/Devices/SATA%d/Port%d/ProfileReadWrite", iInstance, i);
#endif
ahciPortHwReset(pAhciPort);
}
/* Attach drivers to every available port. */
for (i = 0; i < pThis->cPortsImpl; i++)
{
char szName[24];
RTStrPrintf(szName, sizeof(szName), "Port%u", i);
PAHCIPort pAhciPort = &pThis->ahciPort[i];
/*
* Init interfaces.
*/
pAhciPort->IBase.pfnQueryInterface = ahciR3PortQueryInterface;
pAhciPort->IPortAsync.pfnTransferCompleteNotify = ahciR3TransferCompleteNotify;
pAhciPort->IPort.pfnQueryDeviceLocation = ahciR3PortQueryDeviceLocation;
pAhciPort->IMountNotify.pfnMountNotify = ahciR3MountNotify;
pAhciPort->IMountNotify.pfnUnmountNotify = ahciR3UnmountNotify;
pAhciPort->fWrkThreadSleeping = true;
/* Query per port configuration options if available. */
PCFGMNODE pCfgPort = CFGMR3GetChild(pDevIns->pCfg, szName);
if (pCfgPort)
{
rc = CFGMR3QueryBoolDef(pCfgPort, "Hotpluggable", &pAhciPort->fHotpluggable, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("AHCI configuration error: failed to read Hotpluggable as boolean"));
}
/*
* Attach the block driver
*/
rc = PDMDevHlpDriverAttach(pDevIns, pAhciPort->iLUN, &pAhciPort->IBase, &pAhciPort->pDrvBase, szName);
if (RT_SUCCESS(rc))
{
rc = ahciR3ConfigureLUN(pDevIns, pAhciPort);
if (RT_FAILURE(rc))
{
Log(("%s: Failed to configure the %s.\n", __FUNCTION__, szName));
return rc;
}
/* Mark that a device is present on that port */
if (i < 6)
pThis->dev.config[0x93] |= (1 << i);
/*
* Init vendor product data.
*/
rc = ahciR3VpdInit(pDevIns, pAhciPort, szName);
if (RT_FAILURE(rc))
return rc;
/*
* If the new async interface is available we use a PDMQueue to transmit
* the requests into R3.
* Otherwise we use a event semaphore and a async I/O thread which processes them.
*/
if (pAhciPort->pDrvBlockAsync && pThis->fUseAsyncInterfaceIfAvailable)
{
LogRel(("AHCI: LUN#%d: using async I/O\n", pAhciPort->iLUN));
pAhciPort->fAsyncInterface = true;
}
else
{
LogRel(("AHCI: LUN#%d: using normal I/O\n", pAhciPort->iLUN));
pAhciPort->fAsyncInterface = false;
}
rc = SUPSemEventCreate(pThis->pSupDrvSession, &pAhciPort->hEvtProcess);
if (RT_FAILURE(rc))
return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
N_("AHCI: Failed to create SUP event semaphore"));
rc = PDMDevHlpThreadCreate(pDevIns, &pAhciPort->pAsyncIOThread, pAhciPort, ahciAsyncIOLoop,
ahciAsyncIOLoopWakeUp, 0, RTTHREADTYPE_IO, szName);
if (RT_FAILURE(rc))
return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
N_("AHCI: Failed to create worker thread %s"), szName);
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
pAhciPort->pDrvBase = NULL;
rc = VINF_SUCCESS;
LogRel(("%s: no driver attached\n", szName));
}
else
return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
N_("AHCI: Failed to attach drive to %s"), szName);
}
/*
* Attach status driver (optional).
*/
rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
if (RT_SUCCESS(rc))
{
pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
pThis->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIANOTIFY);
}
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_("AHCI cannot attach to status driver"));
}
rc = PDMDevHlpSSMRegisterEx(pDevIns, AHCI_SAVED_STATE_VERSION, sizeof(*pThis) + cbTotalBufferSize, NULL,
NULL, ahciR3LiveExec, NULL,
ahciR3SavePrep, ahciR3SaveExec, NULL,
ahciR3LoadPrep, ahciR3LoadExec, NULL);
if (RT_FAILURE(rc))
return rc;
/*
* Register the info item.
*/
char szTmp[128];
RTStrPrintf(szTmp, sizeof(szTmp), "%s%d", pDevIns->pReg->szName, pDevIns->iInstance);
PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "AHCI info", ahciR3Info);
return ahciR3ResetCommon(pDevIns, true /*fConstructor*/);
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceAHCI =
{
/* u32Version */
PDM_DEVREG_VERSION,
/* szName */
"ahci",
/* szRCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"Intel AHCI controller.\n",
/* fFlags */
PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 |
PDM_DEVREG_FLAGS_FIRST_SUSPEND_NOTIFICATION | PDM_DEVREG_FLAGS_FIRST_POWEROFF_NOTIFICATION |
PDM_DEVREG_FLAGS_FIRST_RESET_NOTIFICATION,
/* fClass */
PDM_DEVREG_CLASS_STORAGE,
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(AHCI),
/* pfnConstruct */
ahciR3Construct,
/* pfnDestruct */
ahciR3Destruct,
/* pfnRelocate */
ahciR3Relocate,
/* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
ahciR3Reset,
/* pfnSuspend */
ahciR3Suspend,
/* pfnResume */
ahciR3Resume,
/* pfnAttach */
ahciR3Attach,
/* pfnDetach */
ahciR3Detach,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
ahciR3PowerOff,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
PDM_DEVREG_VERSION
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */