diff options
author | nthnluu <nate1299@me.com> | 2024-01-28 21:20:27 -0500 |
---|---|---|
committer | nthnluu <nate1299@me.com> | 2024-01-28 21:20:27 -0500 |
commit | c63f340d90800895f007de64b7d2d14624263331 (patch) | |
tree | 2c0849fa597dd6da831c8707b6f2603403778d7b /kernel/include/drivers/disk/ahci.h |
Created student weenix repository
Diffstat (limited to 'kernel/include/drivers/disk/ahci.h')
-rw-r--r-- | kernel/include/drivers/disk/ahci.h | 325 |
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) |