#include <ata.h>
#include <storport.h>
+#define NDEBUG
+#include <debug.h>
+
#define DEBUG 1
+#if defined(_MSC_VER)
#pragma warning(disable:4214) // bit field types other than int
#pragma warning(disable:4201) // nameless struct/union
+#endif
#define MAXIMUM_AHCI_PORT_COUNT 32
#define MAXIMUM_AHCI_PRDT_ENTRIES 32
#define MAXIMUM_QUEUE_BUFFER_SIZE 255
#define MAXIMUM_TRANSFER_LENGTH (128*1024) // 128 KB
+#define DEVICE_ATA_BLOCK_SIZE 512
+
// device type (DeviceParams)
#define AHCI_DEVICE_TYPE_ATA 1
#define AHCI_DEVICE_TYPE_ATAPI 2
#define AHCI_DEVICE_TYPE_NODEVICE 3
// section 3.1.2
-#define AHCI_Global_HBA_CONTROL_HR (1 << 0)
-#define AHCI_Global_HBA_CONTROL_IE (1 << 1)
-#define AHCI_Global_HBA_CONTROL_MRSM (1 << 2)
-#define AHCI_Global_HBA_CONTROL_AE (1 << 31)
#define AHCI_Global_HBA_CAP_S64A (1 << 31)
+// FIS Types : http://wiki.osdev.org/AHCI
+#define FIS_TYPE_REG_H2D 0x27 // Register FIS - host to device
+#define FIS_TYPE_REG_D2H 0x34 // Register FIS - device to host
+#define FIS_TYPE_DMA_ACT 0x39 // DMA activate FIS - device to host
+#define FIS_TYPE_DMA_SETUP 0x41 // DMA setup FIS - bidirectional
+#define FIS_TYPE_BIST 0x58 // BIST activate FIS - bidirectional
+#define FIS_TYPE_PIO_SETUP 0x5F // PIO setup FIS - device to host
+#define FIS_TYPE_DEV_BITS 0xA1 // Set device bits FIS - device to host
+
#define AHCI_ATA_CFIS_FisType 0
#define AHCI_ATA_CFIS_PMPort_C 1
#define AHCI_ATA_CFIS_CommandReg 2
// ATA Functions
#define ATA_FUNCTION_ATA_COMMAND 0x100
#define ATA_FUNCTION_ATA_IDENTIFY 0x101
+#define ATA_FUNCTION_ATA_READ 0x102
// ATAPI Functions
#define ATA_FUNCTION_ATAPI_COMMAND 0x200
// ATA Flags
#define ATA_FLAGS_DATA_IN (1 << 1)
#define ATA_FLAGS_DATA_OUT (1 << 2)
+#define ATA_FLAGS_48BIT_COMMAND (1 << 3)
+#define ATA_FLAGS_USE_DMA (1 << 4)
#define IsAtaCommand(AtaFunction) (AtaFunction & ATA_FUNCTION_ATA_COMMAND)
#define IsAtapiCommand(AtaFunction) (AtaFunction & ATA_FUNCTION_ATAPI_COMMAND)
#define IsAdapterCAPS64(CAP) (CAP & AHCI_Global_HBA_CAP_S64A)
// 3.1.1 NCS = CAP[12:08] -> Align
-#define AHCI_Global_Port_CAP_NCS(x) (((x) & 0xF00) >> 8)
+#define AHCI_Global_Port_CAP_NCS(x) (((x) & 0xF00) >> 8)
#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
-#if DEBUG
- #define DebugPrint(format, ...) StorPortDebugPrint(0, format, __VA_ARGS__)
-#endif
+//#define AhciDebugPrint(format, ...) StorPortDebugPrint(0, format, __VA_ARGS__)
+#define AhciDebugPrint(format, ...) DbgPrint("(%s:%d) " format, __RELFILE__, __LINE__, ##__VA_ARGS__)
typedef
VOID
(*PAHCI_COMPLETION_ROUTINE) (
- __in PVOID AdapterExtension,
__in PVOID PortExtension,
__in PVOID Srb
);
ULONG R : 1; // Reset
ULONG B : 1; // BIST
ULONG C : 1; //Clear Busy upon R_OK
- ULONG DW0_Reserved : 1;
+ ULONG RSV : 1;
ULONG PMP : 4; //Port Multiplier Port
ULONG PRDTL : 16; //Physical Region Descriptor Table Length
ULONG Status;
} AHCI_COMMAND_HEADER_DESCRIPTION;
+typedef union _AHCI_GHC
+{
+ struct
+ {
+ ULONG HR : 1;
+ ULONG IE : 1;
+ ULONG MRSM : 1;
+ ULONG RSV0 : 28;
+ ULONG AE : 1;
+ };
+
+ ULONG Status;
+} AHCI_GHC;
+
// section 3.3.7
typedef union _AHCI_PORT_CMD
{
ULONG Status;
} AHCI_SERIAL_ATA_STATUS;
+typedef union _AHCI_TASK_FILE_DATA
+{
+ struct
+ {
+ struct _STS
+ {
+ UCHAR ERR : 1;
+ UCHAR CS1 : 2;
+ UCHAR DRQ : 1;
+ UCHAR CS2 : 3;
+ UCHAR BSY : 1;
+ } STS;
+ UCHAR ERR;
+ USHORT RSV;
+ };
+
+ ULONG Status;
+} AHCI_TASK_FILE_DATA;
+
typedef struct _AHCI_PRDT
{
ULONG DBA;
typedef struct _AHCI_COMMAND_HEADER
{
AHCI_COMMAND_HEADER_DESCRIPTION DI; // DW 0
- ULONG PRDBC; // DW 1
- ULONG CTBA0; // DW 2
- ULONG CTBA_U0; // DW 3
- ULONG Reserved[4]; // DW 4-7
+ ULONG PRDBC; // DW 1
+ ULONG CTBA; // DW 2
+ ULONG CTBA_U; // DW 3
+ ULONG Reserved[4]; // DW 4-7
} AHCI_COMMAND_HEADER, *PAHCI_COMMAND_HEADER;
// Received FIS
ULONG Vendor[4]; // 0x70 ~ 0x7F, vendor specific
} AHCI_PORT, *PAHCI_PORT;
-#ifdef DEBUG
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, CLB) == 0x00);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, CLBU) == 0x04);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, FB) == 0x08);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, FBU) == 0x0C);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, IS) == 0x10);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, IE) == 0x14);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, CMD) == 0x18);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, RSV0) == 0x1C);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, TFD) == 0x20);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, SIG) == 0x24);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, SSTS) == 0x28);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, SCTL) == 0x2C);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, SERR) == 0x30);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, SACT) == 0x34);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, CI) == 0x38);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, SNTF) == 0x3C);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, FBS) == 0x40);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, RSV1) == 0x44);
- C_ASSERT(FIELD_OFFSET(AHCI_PORT, Vendor) == 0x70);
-#endif
+typedef union _AHCI_INTERRUPT_ENABLE
+{
+ struct
+ {
+ ULONG DHRE :1;
+ ULONG PSE :1;
+ ULONG DSE :1;
+ ULONG SDBE :1;
+ ULONG UFE :1;
+ ULONG DPE :1;
+ ULONG PCE :1;
+ ULONG DMPE :1;
+ ULONG DW5_Reserved :14;
+ ULONG PRCE :1;
+ ULONG IPME :1;
+ ULONG OFE :1;
+ ULONG DW5_Reserved2 :1;
+ ULONG INFE :1;
+ ULONG IFE :1;
+ ULONG HBDE :1;
+ ULONG HBFE :1;
+ ULONG TFEE :1;
+ ULONG CPDE :1;
+ };
+
+ ULONG Status;
+} AHCI_INTERRUPT_ENABLE;
typedef struct _AHCI_MEMORY_REGISTERS
{
AHCI_PORT PortList[MAXIMUM_AHCI_PORT_COUNT];
} AHCI_MEMORY_REGISTERS, *PAHCI_MEMORY_REGISTERS;
-#ifdef DEBUG
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CAP) == 0x00);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, GHC) == 0x04);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, IS) == 0x08);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, PI) == 0x0C);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, VS) == 0x10);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CCC_CTL) == 0x14);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CCC_PTS) == 0x18);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, EM_LOC) == 0x1C);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, EM_CTL) == 0x20);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CAP2) == 0x24);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, BOHC) == 0x28);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, Reserved) == 0x2C);
- C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, VendorSpecific) == 0xA0);
-#endif
-
// Holds information for each attached attached port to a given adapter.
typedef struct _AHCI_PORT_EXTENSION
{
ULONG PortNumber;
ULONG QueueSlots; // slots which we have already assigned task (Slot)
ULONG CommandIssuedSlots; // slots which has been programmed
- BOOLEAN IsActive;
+ ULONG MaxPortQueueDepth;
+
+ struct
+ {
+ UCHAR RemovableDevice;
+ UCHAR Lba48BitMode;
+ UCHAR AccessType;
+ UCHAR DeviceType;
+ UCHAR IsActive;
+ LARGE_INTEGER MaxLba;
+ ULONG BytesPerLogicalSector;
+ ULONG BytesPerPhysicalSector;
+ UCHAR VendorId[41];
+ UCHAR RevisionID[9];
+ UCHAR SerialNumber[21];
+ } DeviceParams;
+
+ STOR_DPC CommandCompletion;
PAHCI_PORT Port; // AHCI Port Infomation
AHCI_QUEUE SrbQueue; // pending Srbs
+ AHCI_QUEUE CompletionQueue;
PSCSI_REQUEST_BLOCK Slot[MAXIMUM_AHCI_PORT_NCS]; // Srbs which has been alloted a port
PAHCI_RECEIVED_FIS ReceivedFIS;
PAHCI_COMMAND_HEADER CommandList;
PVOID NonCachedExtension; // holds virtual address to noncached buffer allocated for Port Extension
- struct
- {
- UCHAR DeviceType;
- } DeviceParams;
-
struct
{
// Message per port or shared port?
ULONG SlotIndex;
LOCAL_SCATTER_GATHER_LIST Sgl;
+ PLOCAL_SCATTER_GATHER_LIST pSgl;
PAHCI_COMPLETION_ROUTINE CompletionRoutine;
+
+ // for alignment purpose -- 128 byte alignment
+ // do not try to access (R/W) this field
+ UCHAR Reserved[128];
} AHCI_SRB_EXTENSION, *PAHCI_SRB_EXTENSION;
//////////////////////////////////////////////////////////////
// Declarations //
//////////////////////////////////////////////////////////////
+VOID
+AhciProcessIO (
+ __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
+ __in UCHAR PathId,
+ __in PSCSI_REQUEST_BLOCK Srb
+ );
+
BOOLEAN
AhciAdapterReset (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension
__in ULONG pathId
);
+UCHAR DeviceRequestSense (
+ __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
+ __in PSCSI_REQUEST_BLOCK Srb,
+ __in PCDB Cdb
+ );
+
+UCHAR DeviceRequestReadWrite (
+ __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
+ __in PSCSI_REQUEST_BLOCK Srb,
+ __in PCDB Cdb
+ );
+
+UCHAR DeviceRequestCapacity (
+ __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
+ __in PSCSI_REQUEST_BLOCK Srb,
+ __in PCDB Cdb
+ );
+
UCHAR
DeviceInquiryRequest (
__in PAHCI_ADAPTER_EXTENSION AdapterExtension,
__in PCDB Cdb
);
+UCHAR DeviceRequestComplete (
+ __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
+ __in PSCSI_REQUEST_BLOCK Srb,
+ __in PCDB Cdb
+ );
+
+UCHAR DeviceReportLuns (
+ __in PAHCI_ADAPTER_EXTENSION AdapterExtension,
+ __in PSCSI_REQUEST_BLOCK Srb,
+ __in PCDB Cdb
+ );
+
__inline
BOOLEAN
AddQueue (
GetSrbExtension(
__in PSCSI_REQUEST_BLOCK Srb
);
+
+__inline
+ULONG64
+AhciGetLba (
+ __in PCDB Cdb,
+ __in ULONG CdbLength
+ );
+
+//////////////////////////////////////////////////////////////
+// Assertions //
+//////////////////////////////////////////////////////////////
+
+// I assert every silly mistake I can do while coding
+// because god never help me debugging the code
+// but these asserts do :')
+
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CAP) == 0x00);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, GHC) == 0x04);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, IS) == 0x08);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, PI) == 0x0C);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, VS) == 0x10);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CCC_CTL) == 0x14);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CCC_PTS) == 0x18);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, EM_LOC) == 0x1C);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, EM_CTL) == 0x20);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, CAP2) == 0x24);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, BOHC) == 0x28);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, Reserved) == 0x2C);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, VendorSpecific) == 0xA0);
+C_ASSERT(FIELD_OFFSET(AHCI_MEMORY_REGISTERS, PortList) == 0x100);
+
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, CLB) == 0x00);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, CLBU) == 0x04);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, FB) == 0x08);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, FBU) == 0x0C);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, IS) == 0x10);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, IE) == 0x14);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, CMD) == 0x18);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, RSV0) == 0x1C);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, TFD) == 0x20);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, SIG) == 0x24);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, SSTS) == 0x28);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, SCTL) == 0x2C);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, SERR) == 0x30);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, SACT) == 0x34);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, CI) == 0x38);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, SNTF) == 0x3C);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, FBS) == 0x40);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, RSV1) == 0x44);
+C_ASSERT(FIELD_OFFSET(AHCI_PORT, Vendor) == 0x70);
+
+C_ASSERT((sizeof(AHCI_COMMAND_TABLE) % 128) == 0);
+
+C_ASSERT(sizeof(AHCI_GHC) == sizeof(ULONG));
+C_ASSERT(sizeof(AHCI_PORT_CMD) == sizeof(ULONG));
+C_ASSERT(sizeof(AHCI_TASK_FILE_DATA) == sizeof(ULONG));
+C_ASSERT(sizeof(AHCI_INTERRUPT_ENABLE) == sizeof(ULONG));
+C_ASSERT(sizeof(AHCI_SERIAL_ATA_STATUS) == sizeof(ULONG));
+C_ASSERT(sizeof(AHCI_SERIAL_ATA_CONTROL) == sizeof(ULONG));
+C_ASSERT(sizeof(AHCI_COMMAND_HEADER_DESCRIPTION) == sizeof(ULONG));
+
+C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, CFIS) == 0x00);
+C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, ACMD) == 0x40);
+C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, RSV0) == 0x50);
+C_ASSERT(FIELD_OFFSET(AHCI_COMMAND_TABLE, PRDT) == 0x80);