aboutsummaryrefslogtreecommitdiff
path: root/kernel/include/drivers/disk/ahci.h
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/include/drivers/disk/ahci.h')
-rw-r--r--kernel/include/drivers/disk/ahci.h325
1 files changed, 325 insertions, 0 deletions
diff --git a/kernel/include/drivers/disk/ahci.h b/kernel/include/drivers/disk/ahci.h
new file mode 100644
index 0000000..1c7acf6
--- /dev/null
+++ b/kernel/include/drivers/disk/ahci.h
@@ -0,0 +1,325 @@
+#pragma once
+
+#include <types.h>
+
+/* Documents referenced:
+ * ATA Command Set 4:
+ * http://www.t13.org/Documents/UploadedDocuments/docs2016/di529r14-ATAATAPI_Command_Set_-_4.pdf
+ * AHCI SATA 1.3.1:
+ * https://www.intel.com/content/www/us/en/io/serial-ata/serial-ata-ahci-spec-rev1-3-1.html
+ * Serial ATA Revision 2.6:
+ * http://read.pudn.com/downloads157/doc/project/697017/SerialATA_Revision_2_6_Gold.pdf
+ */
+
+/* Macros for working with physical region descriptors. */
+#define AHCI_PRDT_DBC_WIDTH 22
+#define AHCI_MAX_PRDT_SIZE (1 << AHCI_PRDT_DBC_WIDTH)
+#define ATA_SECTOR_SIZE 512
+#define AHCI_SECTORS_PER_PRDT (AHCI_MAX_PRDT_SIZE / ATA_SECTOR_SIZE)
+#define AHCI_MAX_SECTORS_PER_COMMAND \
+ (1 << 16) /* FLAG: Where does this come from? */
+#define ACHI_NUM_PRDTS_PER_COMMAND_TABLE \
+ (AHCI_MAX_SECTORS_PER_COMMAND / AHCI_SECTORS_PER_PRDT)
+
+#define AHCI_MAX_NUM_PORTS 32
+#define AHCI_COMMAND_HEADERS_PER_LIST 32
+
+#define AHCI_COMMAND_LIST_ARRAY_BASE(ahci_base) (ahci_base)
+#define AHCI_COMMAND_LIST_ARRAY_SIZE \
+ (AHCI_MAX_NUM_PORTS * sizeof(command_list_t))
+
+#define AHCI_RECEIVED_FIS_ARRAY_BASE(ahci_base) \
+ ((ahci_base) + AHCI_COMMAND_LIST_ARRAY_SIZE)
+#define AHCI_RECEIVED_FIS_ARRAY_SIZE \
+ (AHCI_MAX_NUM_PORTS * sizeof(received_fis_t))
+
+#define AHCI_COMMAND_TABLE_ARRAY_BASE(ahci_base) \
+ (AHCI_RECEIVED_FIS_ARRAY_BASE(ahci_base) + AHCI_RECEIVED_FIS_ARRAY_SIZE)
+#define AHCI_COMMAND_TABLE_ARRAY_SIZE \
+ (AHCI_MAX_NUM_PORTS * AHCI_COMMAND_HEADERS_PER_LIST * \
+ sizeof(command_table_t))
+
+#define AHCI_SIZE \
+ (AHCI_COMMAND_LIST_ARRAY_SIZE + AHCI_RECEIVED_FIS_ARRAY_SIZE + \
+ AHCI_COMMAND_TABLE_ARRAY_SIZE)
+#define AHCI_SIZE_PAGES ((uintptr_t)PAGE_ALIGN_UP(AHCI_SIZE) / PAGE_SIZE)
+
+#define ALIGN_DOWN_POW_2(x, align) ((x) & -(align))
+#define ALIGN_UP_POW_2(x, align) (ALIGN_DOWN_POW_2((x)-1, align) + (align))
+
+/*=============================
+ * Frame Information Structures
+ *============================*/
+
+/* fis_type_t - FIS types are recognized by an ID.
+ * For more info, see section 10.3 (FIS Types) of Serial ATA Revision 2.6. */
+typedef enum fis_type
+{
+ fis_type_h2d_register = 0x27
+} packed fis_type_t;
+
+/* Command codes used when forming the host-to-device FIS (see: ATA Command Set
+ * 4). The first two are standard commands. The second two are for NCQ commands.
+ */
+#define ATA_READ_DMA_EXT_COMMAND 0x25
+#define ATA_WRITE_DMA_EXT_COMMAND 0x35
+#define ATA_READ_FPDMA_QUEUED_COMMAND 0x60
+#define ATA_WRITE_FPDMA_QUEUED_COMMAND 0x61
+
+/* 8-bit device setting for host-to-device FIS.
+ * Bit 6 is specified as either obsolete or "shall be set to one" for all
+ * commands used in Weenix. So, we can safely just default to this value for all
+ * commands. More info in sections 7.20, 7.21, 7.55, and 7.57 of ATA Command
+ * Set 4. */
+#define ATA_DEVICE_LBA_MODE 0x40
+
+/* h2d_register_fis - Register Host to Device FIS.
+ * This is the only FIS used in Weenix.
+ */
+typedef struct h2d_register_fis
+{
+ uint8_t fis_type; /* Must be set to fis_type_h2d_register. */
+ uint8_t : 7;
+ uint8_t c : 1; /* When set, indicates that this is an FIS for a command.
+ * This is always the case in Weenix. */
+ uint8_t command; /* See command codes further up. */
+ uint8_t
+ features; /* For regular read/write, no use.
+ * For NCQ commands, features and features_exp form the lower
+ * and upper 8 bits of sector count, respectively. */
+ uint32_t lba : 24; /* lba and lba_exp form the lower and upper 24 bits of
+ the first logical block address, respectively. */
+ uint8_t device; /* Device register.
+ * For Weenix's purposes, this should always be set to
+ * ATA_DEVICE_LBA_MODE. */
+ uint32_t lba_exp : 24;
+ uint8_t features_exp;
+ uint16_t sector_count; /* For regular read/write, specifies number of
+ * sectors to read/write.
+ * For NCQ commands, bits 7:3 specify NCQ tag. */
+ uint16_t : 16;
+ uint32_t : 32;
+} packed h2d_register_fis_t;
+
+/*========================
+ * Command List Structures
+ *=======================*/
+
+/* command_fis_t - Represents a software-constructed FIS stored in a
+ * command_table_t. */
+typedef union command_fis {
+ h2d_register_fis_t h2d_register_fis;
+ /* Must occupy 64 bytes in its corresponding command_table_t.
+ * Recall that unions conform to the size of the largest member. */
+ struct
+ {
+ uint8_t size[64];
+ };
+} packed command_fis_t;
+
+/* received_fis_t - Per-port structure that contains information on received
+ * FISes. More info in section 4.2.1 of the 1.3.1 spec. */
+typedef struct received_fis
+{
+ uint8_t _omit[256]; /* Weenix does not make use of any received FIS from the
+ device. */
+} packed received_fis_t;
+
+/* prd_t - Physical Region Descriptor.
+ * Represents an entry in the PRD table in a command table
+ * (command_table_t->prdt). Points to a chunk of system memory for the device to
+ * use according to whatever command it is executing.
+ */
+typedef struct prd
+{
+ uint64_t dba; /* Data Base Address. */
+ uint32_t : 32;
+ uint32_t
+ dbc : 22; /* Data Byte Count: Indicates length of data block in bytes,
+ * but starts counting from 0. Ex: Length 1 is 0x0. Length 2
+ * is 0x1. Length 3 is 0x10. And so on... Must be even. Due to
+ * counting from 0, this means least-significant bit MUST
+ * be 1. Max length is 4MB (all bits set). */
+ uint16_t : 9;
+ uint8_t i : 1; /* Interrupt on Completion: When set, then upon processing
+ * all PRDs in the current FIS, the port will try to generate
+ * an interrupt by setting PxIS.DPS.
+ *
+ * Whether or not this actually behaves as expected, or ever
+ * is even used, is unclear.
+ */
+} packed prd_t;
+
+/* command_table_t - Structure detailing a command and associated data / memory.
+ * More info in section 4.2.3 of SATA AHCI 1.3.1.
+ */
+typedef struct command_table
+{
+ command_fis_t
+ cfis; /* Command FIS: The actual software constructed command. */
+ uint8_t _omit[64];
+ prd_t prdt[ACHI_NUM_PRDTS_PER_COMMAND_TABLE]; /* Physical Region Descriptor
+ * Table: A list of,
+ * theoretically, up to 2^16
+ * entries of PRDs.
+ * Number of actual usable
+ * entries is indicated by
+ * command_header_t->prdtl. */
+} packed command_table_t;
+
+/* command_header_t - Structure detailing command details. Stored in a
+ * command_list_t. More info in section 4.2.2 of the SATA AHCI 1.3.1 spec. */
+typedef struct command_header
+{
+ uint8_t cfl : 5; /* Command FIS length in DW (4 bytes). Max value is 0x10
+ (16). */
+ uint8_t : 1;
+ uint8_t write : 1; /* Write: Set indicates write, clear indicates read. */
+ uint16_t : 9;
+ uint16_t prdtl; /* Physical Region Descriptor Table Length: Number of PRD
+ entries. */
+ uint32_t : 32;
+ uint64_t ctba; /* Command Table Descriptor Base Address: Pointer to the
+ command table. */
+ uint64_t : 64;
+ uint64_t : 64;
+} packed command_header_t;
+
+/* command_list_t - Per-port command list.
+ * More info in section 4.2.2 of the SATA AHCI 1.3.1 spec.
+ * See also: Figure 5: Port System Memory Structures. */
+typedef struct command_list
+{
+ command_header_t command_headers[AHCI_COMMAND_HEADERS_PER_LIST];
+} packed command_list_t;
+
+/*=================
+ * Host Bus Adapter
+ *================*/
+
+/* px_interrupt_status - Per-port bitmap indicating that a corresponding
+ * interrupt has occurred on the port. Observe that this is a union, making
+ * initialization a little easier. */
+typedef union px_interrupt_status {
+ struct
+ {
+ uint8_t dhrs : 1; /* Interrupt requested by a device-to-host FIS.
+ * Used by normal read/write commands, see 5.6.2
+ * in 1.3.1. */
+ uint8_t : 2;
+ uint8_t
+ sdbs : 1; /* Interrupt requested by a set device bits FIS.
+ * Used by NCQ read/write commands, see 5.6.4 in 1.3.1. */
+ uint8_t : 1;
+ uint8_t dps : 1; /* Interrupt set upon completing an FIS that requested
+ * an interrupt upon completion.
+ * Currently doesn't seem to be working... */
+ uint32_t : 26;
+ } bits;
+ uint32_t value;
+} packed px_interrupt_status_t;
+
+/* Observe that, to clear interrupt status, must set to 1. */
+static px_interrupt_status_t px_interrupt_status_clear = {.value =
+ (uint32_t)-1};
+
+/* Port x Interrupt Enable - Bitwise register controlling generation of various
+ * interrupts. */
+typedef union px_interrupt_enable {
+ uint32_t value;
+} packed px_interrupt_enable_t;
+
+/* Weenix uses this to initialize all ports to enable all interrupts by default.
+ */
+static px_interrupt_enable_t px_interrupt_enable_all_enabled = {
+ .value = (uint32_t)-1};
+
+/* hba_ghc_t - Generic Host Control: Information and control registers
+ * pertaining to the entire HBA. More info in section 3.1 of 1.3.1.
+ */
+typedef struct hba_ghc
+{
+ struct
+ {
+ uint32_t : 30;
+ uint8_t sncq : 1; /* Supports Native Command Queueing. */
+ uint8_t : 1;
+ } packed cap;
+ struct
+ {
+ uint8_t : 1;
+ uint8_t ie : 1; /* Interrupt Enable: Enables/disables interrupts from
+ HBA. */
+ uint32_t : 29;
+ uint8_t ae : 1; /* AHCI Enable: Indicates software adheres to AHCI
+ specification. */
+ } packed ghc;
+ uint32_t is; /* Interrupt Status: If bit x is set, then port x has a pending
+ interrupt. */
+ uint32_t pi; /* Ports Implemented: If bit x is set, then port x is available
+ for use. */
+ uint32_t _omit[7];
+} packed hba_ghc_t;
+
+/* Signature for SATA devices. Compare this against hba_port_t->px_sig to
+ * determine if a SATA device is sitting behind a given port. */
+#define SATA_SIG_ATA 0x00000101
+
+/* hba_port - A per-port structure storing port information.
+ * Each port represents a device that the HBA is communicating with (e.g. a
+ * SATA device!). Details not relevant to Weenix have been omitted. More info in
+ * section 3.3 of the SATA AHCI 1.3.1 spec.
+ */
+typedef struct hba_port
+{
+ uint64_t px_clb; /* 1K-byte aligned base physical address of this port's
+ * command list. This is a pointer to a command_list_t. */
+ uint64_t px_fb; /* Base physical address for received FISes.
+ * Weenix never uses received FIS, but we allocate and set
+ * up memory to make the HBA happy. */
+ px_interrupt_status_t px_is; /* Interrupt Status. */
+ px_interrupt_enable_t px_ie; /* Interrupt Enable. */
+ struct
+ {
+ uint8_t st : 1; /* Start: Allows the HBA to process the command list. */
+ uint8_t : 3;
+ uint8_t fre : 1; /* FIS Receive Enable: Allows HBA to post received
+ FISes in px_fb. */
+ uint16_t : 9;
+ uint8_t fr : 1; /* FIS Receive Running: Read-only indicating if FIS
+ Receive DMA is running. */
+ uint8_t cr : 1; /* Command List Running: Read-only indicating if command
+ list DMA is running. */
+ uint16_t : 16;
+ } packed px_cmd; /* Port Command and Status. */
+ uint64_t : 64;
+ uint32_t px_sig; /* Signature: Contains attached device's signature.
+ * SATA devices should have signature SATA_SIG_ATA, defined
+ * above. */
+ uint64_t : 64;
+ uint32_t px_serr; /* SATA Error: Unclear how Weenix is actually making use
+ of this register. */
+ uint32_t px_sact; /* SATA Active: Used for NCQ.
+ * Each bit corresponds to TAG and command slot of an NCQ
+ * command. Must be set by software before issuing a NCQ
+ * for a command slot.
+ */
+ uint32_t px_ci; /* Commands Issued: Software sets bit x if a command x is
+ * ready to be sent. Each bit corresponds to a command slot.
+ * HBA clears bit upon completing a command.
+ */
+ uint32_t _omit[17];
+} packed hba_port_t;
+
+/* Host Bus Adapter - Control block for the device that actually interfaces
+ * between the OS and the SATA disk device. For more info, see section 3 of
+ * the 1.3.1 spec.
+ */
+typedef struct hba
+{
+ hba_ghc_t ghc; /* Generic Host Control. */
+ uint32_t _omit[53];
+ hba_port_t ports[32]; /* Static array of port descriptors. */
+} packed hba_t;
+
+#define PORT_INDEX(hba, port) ((port) - (hba)->ports)