From 61dd73b79fbed2a54f28d99c948f8c9a5b9262bf Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 21 Oct 2019 16:14:34 +0300 Subject: [PATCH] Add AHCI SATA device enumeration + SATA device IDENTIFY --- .gitignore | 1 + etc/make/amd64/platform.mk | 2 + include/sys/amd64/hw/ide/ahci.h | 227 ++++++++++++++++++++++++++++++++ include/sys/amd64/hw/ide/ata.h | 17 +++ include/sys/amd64/hw/pci/ahci.h | 7 + sys/amd64/hw/ide/ahci.c | 181 +++++++++++++++++++++++++ sys/amd64/hw/pci/ahci.c | 161 +--------------------- sys/debug.c | 42 ++++++ 8 files changed, 484 insertions(+), 154 deletions(-) create mode 100644 include/sys/amd64/hw/ide/ahci.h create mode 100644 include/sys/amd64/hw/ide/ata.h create mode 100644 sys/amd64/hw/ide/ahci.c diff --git a/.gitignore b/.gitignore index 65dd674..d11aba0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ doc/latex bochsrc.txt config test.img +eth_* diff --git a/etc/make/amd64/platform.mk b/etc/make/amd64/platform.mk index 1a8916a..552e345 100644 --- a/etc/make/amd64/platform.mk +++ b/etc/make/amd64/platform.mk @@ -36,6 +36,7 @@ OBJS+=$(O)/sys/amd64/hw/rs232.o \ $(O)/sys/amd64/hw/pci/pci.o \ $(O)/sys/amd64/hw/pci/ide.o \ $(O)/sys/amd64/hw/pci/ahci.o \ + $(O)/sys/amd64/hw/ide/ahci.o \ $(O)/sys/amd64/hw/pci/pcidb.o \ $(ACPICA_OBJS) \ $(O)/sys/amd64/acpi_osl_mem.o \ @@ -82,4 +83,5 @@ DIRS+=$(O)/sys/amd64/image/boot/grub \ $(O)/sys/amd64/sys \ $(O)/sys/amd64/mm \ $(O)/sys/amd64/hw/pci \ + $(O)/sys/amd64/hw/ide \ $(ACPICA_OBJD) diff --git a/include/sys/amd64/hw/ide/ahci.h b/include/sys/amd64/hw/ide/ahci.h new file mode 100644 index 0000000..7803e63 --- /dev/null +++ b/include/sys/amd64/hw/ide/ahci.h @@ -0,0 +1,227 @@ +#pragma once +#include "sys/types.h" + +#define AHCI_PORT_SSTS_DET_OK 0x3 +#define AHCI_PORT_SSTS_IPM_ACTIVE 0x1 +#define AHCI_PORT_SIG_SATA 0x00000101 +#define AHCI_PORT_SIG_SATAPI 0xEB140101 + +#define AHCI_PORT_CMD_ST (1 << 0) +#define AHCI_PORT_CMD_FRE (1 << 4) +#define AHCI_PORT_CMD_FR (1 << 14) +#define AHCI_PORT_CMD_CR (1 << 15) + +enum ahci_fis_type { + FIS_REG_H2D = 0x27, + FIS_REG_D2H = 0x34, + FIS_DMA_ACT = 0x39, + FIS_DMA_SETUP = 0x41, + FIS_DATA = 0x46, + FIS_BIST = 0x58, + FIS_PIO_SETUP = 0x5F, + FIS_DEV_BITS = 0xA1 +}; + +struct ahci_registers { + // Generic host control + uint32_t cap; // 0x00 + uint32_t ghc; // 0x04 + uint32_t is; // 0x08 + uint32_t pi; // 0x0C + uint32_t vs; // 0x10 + uint32_t ccc_ctl; // 0x14 + uint32_t ccc_ports; // 0x18 + uint32_t em_loc; // 0x1C + uint32_t em_ctl; // 0x20 + uint32_t cap2; // 0x24 + uint32_t bohc; // 0x28 + // Reserved + uint32_t __res0[13]; + // Reserved for NVMHCI + // Also vendor specific settings + uint32_t __res1[40]; + + // Port control registers + struct ahci_port_registers { + uint32_t p_clb; + uint32_t p_clbu; + uint32_t p_fb; + uint32_t p_fbu; + uint32_t p_is; + uint32_t p_ie; + uint32_t p_cmd; + uint32_t __res0; + uint32_t p_tfd; + uint32_t p_sig; + uint32_t p_ssts; + uint32_t p_sctl; + uint32_t p_serr; + uint32_t p_sact; + uint32_t p_ci; + uint32_t p_sntf; + uint32_t p_fbs; + uint32_t p_devslp; + uint32_t __res1[14]; + } __attribute__((packed)) ports[32]; +} __attribute__((packed)); + +struct ahci_fis_reg_h2d { + uint8_t type; + uint8_t cmd_port; + uint8_t cmd; + uint8_t feature_low; + + uint8_t lba0; + uint8_t lba1; + uint8_t lba2; + uint8_t dev; + + uint8_t lba3; + uint8_t lba4; + uint8_t lba5; + uint8_t feature_high; + + uint8_t countl; + uint8_t counth; + uint8_t icc; + uint8_t control; + + uint8_t __res0[4]; +}; + +struct ahci_fis_reg_d2h { + uint8_t type; + uint8_t cmd_port; + uint8_t status; + uint8_t error; + + uint8_t lba0; + uint8_t lba1; + uint8_t lba2; + uint8_t dev; + + uint8_t lba3; + uint8_t lba4; + uint8_t lba5; + uint8_t __res0; + + uint8_t countl; + uint8_t counth; + + uint8_t __res1[6]; +}; + +struct ahci_fis_dma_setup { + uint8_t type; + uint8_t cmd_port; + uint8_t __res0[2]; + uint64_t dma_buffer_id; + uint32_t __res1; + uint32_t dma_buffer_offset; + uint32_t transfer_count; + uint32_t __res2; +}; + +struct ahci_fis_pio_setup { + uint8_t type; + uint8_t cmd_port; + uint8_t status; + uint8_t error; + + uint8_t lba0; + uint8_t lba1; + uint8_t lba2; + uint8_t device; + + uint8_t lba3; + uint8_t lba4; + uint8_t lba5; + uint8_t __res0; + + uint8_t countl; + uint8_t counth; + uint8_t __res1; + uint8_t e_status; + + uint16_t tc; + uint8_t __res2[2]; +}; + +struct ahci_fis_data { + uint8_t type; + uint8_t cmd_port; + + uint8_t __res0[2]; + + uint32_t data[]; +}; + +struct ahci_recv_fis { + union { + // DMA setup FIS + struct ahci_fis_dma_setup dsfis; + char __block0[0x20]; + }; + union { + // PIO setup FIS + struct ahci_fis_pio_setup psfis; + char __block1[0x20]; + }; + union { + // Register FIS + struct ahci_fis_reg_d2h rfis; + char __block2[0x18]; + }; + union { + // Set device bits FIS + char __block3[0x2]; + }; + union { + // Unknown FIS + char __block4[0x40]; + }; +}; + +struct ahci_command_header { + // Bits: + // 0..4 Command length in dwords + // 5 ATAPI + uint16_t attr; + uint16_t prdtl; + volatile uint32_t prdbc; + uint32_t ctba; + uint32_t ctbau; + uint32_t __res0[4]; +} __attribute__((packed)); + +struct ahci_prdt_entry { + uint32_t dba; + uint32_t dbau; + uint32_t __res0; + // Bits: + // 0 1 + // 1..21 Data byte count + // 22..30 Reserved + // 31 Interrupt + uint32_t dbc; +} __attribute__((packed)); + +struct ahci_command_table_entry { + union { + struct ahci_fis_reg_h2d fis_reg_h2d; + uint8_t __cmd_fis[64]; + } __attribute__((packed)); + uint8_t acmd[16]; + uint8_t __res0[48]; + struct ahci_prdt_entry prdt[]; +} __attribute__((packed)); + +#define AHCI_PORT_CMD_LIST(p) (struct ahci_command_header *) MM_VIRTUALIZE(((uintptr_t) (p)->p_clbu << 32) | ((p)->p_clb)) +#define AHCI_CMD_TABLE_ENTRY(l, i) (struct ahci_command_table_entry *) MM_VIRTUALIZE(((uintptr_t) (l)[i].ctbau << 32) | ((l)[i].ctba)) + +void ahci_port_start(struct ahci_port_registers *port); +void ahci_port_stop(struct ahci_port_registers *port); + +void ahci_port_init(uint8_t n, struct ahci_port_registers *port); + +void ahci_init(struct ahci_registers *regs); diff --git a/include/sys/amd64/hw/ide/ata.h b/include/sys/amd64/hw/ide/ata.h new file mode 100644 index 0000000..c7adf27 --- /dev/null +++ b/include/sys/amd64/hw/ide/ata.h @@ -0,0 +1,17 @@ +#pragma once + +#define ATA_CMD_IDENTIFY 0xEC + +#define ATA_SR_BUSY (1 << 7) +#define ATA_SR_DRQ (1 << 3) + +#define ATA_IDENT_DEVICE_TYPE 0x00 +#define ATA_IDENT_CYLINDERS 0x02 +#define ATA_IDENT_HEADS 0x06 +#define ATA_IDENT_SECTORS 0x0C +#define ATA_IDENT_SERIAL 0x14 +#define ATA_IDENT_MODEL 0x36 +#define ATA_IDENT_CAPS 0x62 +#define ATA_IDENT_MAX_LBA 0x78 +#define ATA_IDENT_CMD_SETS 0xA4 +#define ATA_IDENT_MAX_LBAEXT 0xC8 diff --git a/include/sys/amd64/hw/pci/ahci.h b/include/sys/amd64/hw/pci/ahci.h index 99908c0..80eb47e 100644 --- a/include/sys/amd64/hw/pci/ahci.h +++ b/include/sys/amd64/hw/pci/ahci.h @@ -1,4 +1,11 @@ #pragma once #include "pci.h" +#include "sys/amd64/hw/ide/ahci.h" + +struct pci_ahci { + pci_addr_t addr; + uint32_t abar_phys; + struct ahci_registers *volatile regs; +}; void pci_ahci_init(pci_addr_t addr); diff --git a/sys/amd64/hw/ide/ahci.c b/sys/amd64/hw/ide/ahci.c new file mode 100644 index 0000000..e021c68 --- /dev/null +++ b/sys/amd64/hw/ide/ahci.c @@ -0,0 +1,181 @@ +#include "sys/amd64/hw/ide/ahci.h" +#include "sys/amd64/hw/ide/ata.h" +#include "sys/amd64/mm/phys.h" +#include "sys/string.h" +#include "sys/assert.h" +#include "sys/debug.h" +#include "sys/mm.h" + +// Memory required for a port: +// 1024 - command list (32 * sizeof(ahci_command_header)) +// 256 - received FIS +// 8192 - command table (32 * sizeof(ahci_command_table_entry)) +// So we need at least 3 pages for all the stuff (buffers not included) + +static int ahci_alloc_cmd(struct ahci_port_registers *port) { + uint32_t slots = (port->p_sact | port->p_ci); + for (int i = 0; i < 32; ++i) { + if (!(slots & (1 << i))) { + return i; + } + } + return -1; +} + +static void ahci_sata_port_identify(struct ahci_port_registers *port) { + port->p_is = -1; + + int cmd = ahci_alloc_cmd(port); + _assert(cmd != -1); + + // Command list + struct ahci_command_header *cmd_list = AHCI_PORT_CMD_LIST(port); + struct ahci_command_header *cmd_header = &cmd_list[cmd]; + // Command table entry for the slot + struct ahci_command_table_entry *cmd_table = AHCI_CMD_TABLE_ENTRY(cmd_list, cmd); + + // One PRDT entry + char ident_buf[1024] = {0}; + uintptr_t ident_buf_phys = ((uintptr_t) ident_buf) - 0xFFFFFF0000000000; + + cmd_header->attr = (sizeof(struct ahci_fis_reg_h2d) / 4); + cmd_header->prdtl = 1; + + memset(cmd_table, 0, sizeof(struct ahci_command_table_entry) + sizeof(struct ahci_prdt_entry)); + cmd_table->prdt[0].dba = ident_buf_phys & 0xFFFFFFFF; + cmd_table->prdt[0].dbau = ident_buf_phys >> 32; + cmd_table->prdt[0].dbc = 1 | ((sizeof(ident_buf) - 1) << 1); + + struct ahci_fis_reg_h2d *fis_reg_h2d = &cmd_table->fis_reg_h2d; + memset(fis_reg_h2d, 0, sizeof(struct ahci_fis_reg_h2d)); + fis_reg_h2d->type = FIS_REG_H2D; + fis_reg_h2d->cmd = ATA_CMD_IDENTIFY; + fis_reg_h2d->cmd_port = 1 << 7; + + uint32_t spin = 0; + while ((port->p_tfd & (ATA_SR_BUSY | ATA_SR_DRQ)) && spin < 1000000) { + ++spin; + } + if (spin == 1000000) { + panic("SATA port hang\n"); + } + + port->p_ci |= 1 << cmd; + + while (1) { + if (!(port->p_ci & (1 << cmd))) { + break; + } + + if (port->p_is & (1 << 30) /* AHCI_PORT_IS_TFES */) { + panic("Port task file error\n"); + } + } + + kdebug("Drive identification:\n"); + + char model_str[41] = {0}; + uint32_t cmd_sets = *(uint32_t *) (ident_buf + ATA_IDENT_CMD_SETS); + uint32_t disk_size = 0; + for (size_t i = 0; i < 40; i += 2) { + model_str[i] = ident_buf[ATA_IDENT_MODEL + i + 1]; + model_str[i + 1] = ident_buf[ATA_IDENT_MODEL + i]; + } + + if (cmd_sets & (1 << 26)) { + disk_size = *((uint32_t *) (ident_buf + ATA_IDENT_MAX_LBAEXT)); + } else { + disk_size = *((uint32_t *) (ident_buf + ATA_IDENT_MAX_LBA)); + } + + kdebug("Model: %s\n", model_str); + kdebug("Size: %S\n", disk_size * 512); +} + +static void ahci_sata_port_init(struct ahci_port_registers *port) { + ahci_port_stop(port); + + uintptr_t page0 = amd64_phys_alloc_page(); + _assert(page0 != MM_NADDR); + kdebug("Command list/recv FIS: %p\n", page0); + + memset((void *) MM_VIRTUALIZE(page0), 0, 4096); + + port->p_clb = page0 & 0xFFFFFFFF; + port->p_clbu = page0 >> 32; + + port->p_fb = (page0 + 0x400) & 0xFFFFFFFF; + port->p_fbu = (page0 + 0x400) >> 32; + + // 8K + uintptr_t page1 = amd64_phys_alloc_contiguous(2); + _assert(page1 != MM_NADDR); + memset((void *) MM_VIRTUALIZE(page1), 0, 8192); + + struct ahci_command_header *cmd_list = (struct ahci_command_header *) MM_VIRTUALIZE(page0); + for (size_t i = 0; i < 32; ++i) { + uintptr_t addr = page1 + i * 256; + + cmd_list[i].prdtl = 8; + cmd_list[i].ctba = addr & 0xFFFFFFFF; + cmd_list[i].ctbau = addr >> 32; + } + + ahci_port_start(port); + + // TODO: add the device somewhere to kernel as /dev/sdX + // guess I should implement device management eventually +} + +// Setup a SATA port +void ahci_port_init(uint8_t n, struct ahci_port_registers *port) { + switch (port->p_sig) { + case AHCI_PORT_SIG_SATA: + ahci_sata_port_init(port); + ahci_sata_port_identify(port); + break; + default: + kdebug("Skipping unknown drive type\n"); + break; + } +} + +void ahci_port_stop(struct ahci_port_registers *port) { + port->p_cmd &= ~(AHCI_PORT_CMD_ST | AHCI_PORT_CMD_FRE); + + while (port->p_cmd & (AHCI_PORT_CMD_CR | AHCI_PORT_CMD_FR)); +} + +void ahci_port_start(struct ahci_port_registers *port) { + while (port->p_cmd & AHCI_PORT_CMD_CR); + + port->p_cmd |= AHCI_PORT_CMD_FRE | AHCI_PORT_CMD_ST; +} + +void ahci_init(struct ahci_registers *regs) { + // Read AHCI version + kdebug("Initializing AHCI controller version %d.%d\n", regs->vs >> 16, regs->vs & 0xFFFF); + + uint32_t pi = regs->pi; + + // Find available ports + for (size_t i = 0; i < 32; ++i) { + uint32_t bit = 1 << i; + + if (pi & bit) { + struct ahci_port_registers *port = ®s->ports[i]; + uint32_t ssts = port->p_ssts; + + uint8_t ipm = (ssts >> 8) & 0x0F; + uint8_t spd = (ssts >> 4) & 0x0F; + uint8_t det = ssts & 0x0F; + + if (ipm != AHCI_PORT_SSTS_IPM_ACTIVE || spd == 0 || det != AHCI_PORT_SSTS_DET_OK) { + // For one of the reasons the device is not available at this port + continue; + } + + ahci_port_init(i, port); + } + } +} diff --git a/sys/amd64/hw/pci/ahci.c b/sys/amd64/hw/pci/ahci.c index 8871e8b..4a7ca91 100644 --- a/sys/amd64/hw/pci/ahci.c +++ b/sys/amd64/hw/pci/ahci.c @@ -1,131 +1,15 @@ -#include "sys/amd64/hw/pci/pci.h" +#include "sys/amd64/hw/pci/ahci.h" +#include "sys/amd64/hw/ide/ahci.h" +#include "sys/amd64/hw/ide/ata.h" +#include "sys/amd64/mm/phys.h" +#include "sys/assert.h" +#include "sys/string.h" #include "sys/debug.h" #include "sys/mm.h" -#define AHCI_PORT_SSTS_DET_OK 0x3 -#define AHCI_PORT_SSTS_IPM_ACTIVE 0x1 -#define AHCI_PORT_SIG_SATA 0x00000101 -#define AHCI_PORT_SIG_SATAPI 0xEB140101 - -enum ahci_fis_type { - FIS_REG_H2D = 0x27, - FIS_REG_D2H = 0x34, - FIS_DMA_ACT = 0x39, - FIS_DMA_SETUP = 0x41, - FIS_DATA = 0x46, - FIS_BIST = 0x58, - FIS_PIO_SETUP = 0x5F, - FIS_DEV_BITS = 0xA1 -}; - -struct ahci_registers { - // Generic host control - uint32_t cap; // 0x00 - uint32_t ghc; // 0x04 - uint32_t is; // 0x08 - uint32_t pi; // 0x0C - uint32_t vs; // 0x10 - uint32_t ccc_ctl; // 0x14 - uint32_t ccc_ports; // 0x18 - uint32_t em_loc; // 0x1C - uint32_t em_ctl; // 0x20 - uint32_t cap2; // 0x24 - uint32_t bohc; // 0x28 - // Reserved - uint32_t __res0[13]; - // Reserved for NVMHCI - // Also vendor specific settings - uint32_t __res1[40]; - - // Port control registers - struct ahci_port_registers { - uint32_t p_clb; - uint32_t p_clbu; - uint32_t p_fb; - uint32_t p_fbu; - uint32_t p_is; - uint32_t p_ie; - uint32_t p_cmd; - uint32_t __res0; - uint32_t p_tfd; - uint32_t p_sig; - uint32_t p_ssts; - uint32_t p_sctl; - uint32_t p_serr; - uint32_t p_sact; - uint32_t p_ci; - uint32_t p_sntf; - uint32_t p_fbs; - uint32_t p_devslp; - uint32_t __res1[14]; - } __attribute__((packed)) ports[32]; -} __attribute__((packed)); - -struct ahci_fis_reg_h2d { - uint8_t type; - uint8_t cmd_port; - uint8_t cmd; - uint8_t feature_low; - - uint8_t lba0; - uint8_t lba1; - uint8_t lba2; - uint8_t dev; - - uint8_t lba3; - uint8_t lba4; - uint8_t lba5; - uint8_t feature_high; - - uint8_t countl; - uint8_t counth; - uint8_t icc; - uint8_t control; - - uint8_t __res0[4]; -}; - -struct ahci_fis_reg_d2h { - uint8_t type; - uint8_t cmd_port; - uint8_t status; - uint8_t error; - - uint8_t lba0; - uint8_t lba1; - uint8_t lba2; - uint8_t dev; - - uint8_t lba3; - uint8_t lba4; - uint8_t lba5; - uint8_t __res0; - - uint8_t countl; - uint8_t counth; - - uint8_t __res1[6]; -}; - -struct pci_ahci { - pci_addr_t addr; - uint32_t abar_phys; - struct ahci_registers *volatile regs; -}; - // Only one controller is supported now static struct pci_ahci ahci; -// Setup a SATA port -static void pci_ahci_sata_add(uint8_t n, struct ahci_port_registers *port) { - kdebug("SATA port %d\n", n); -} - -// SATAPI port -static void pci_ahci_satapi_add(uint8_t n, struct ahci_port_registers *port) { - kdebug("SATAPI port %d\n", n); -} - void pci_ahci_init(pci_addr_t addr) { kdebug("Initializing AHCI controller at " PCI_FMTADDR "\n", PCI_VAADDR(addr)); @@ -135,36 +19,5 @@ void pci_ahci_init(pci_addr_t addr) { kdebug("AHCI registers: %p\n", ahci.regs); - // Read AHCI version - kdebug("AHCI controller version %d.%d\n", ahci.regs->vs >> 16, ahci.regs->vs & 0xFFFF); - - uint32_t pi = ahci.regs->pi; - - // Find available ports - for (size_t i = 0; i < 32; ++i) { - uint32_t bit = 1 << i; - - if (pi & bit) { - struct ahci_port_registers *port = &ahci.regs->ports[i]; - uint32_t ssts = port->p_ssts; - - uint8_t ipm = (ssts >> 8) & 0x0F; - uint8_t spd = (ssts >> 4) & 0x0F; - uint8_t det = ssts & 0x0F; - - if (ipm != AHCI_PORT_SSTS_IPM_ACTIVE || spd == 0 || det != AHCI_PORT_SSTS_DET_OK) { - // For one of the reasons the device is not available at this port - continue; - } - - switch (port->p_sig) { - case AHCI_PORT_SIG_SATA: - pci_ahci_sata_add(i, port); - break; - case AHCI_PORT_SIG_SATAPI: - pci_ahci_satapi_add(i, port); - break; - } - } - } + ahci_init(ahci.regs); } diff --git a/sys/debug.c b/sys/debug.c index f762f6f..cc1a41b 100644 --- a/sys/debug.c +++ b/sys/debug.c @@ -1,5 +1,6 @@ #include "sys/string.h" #include "sys/debug.h" +#include "sys/ctype.h" #include "sys/attr.h" #include "sys/spin.h" #include @@ -297,3 +298,44 @@ void debugfv(int level, const char *fmt, va_list args) { spin_release_irqrestore(&debug_spin, &irq); } +void debug_dump(int level, const void *block, size_t count) { + size_t n_lines = (count + 15) / 16; + const char *bytes = block; + + for (size_t l = 0; l < n_lines; ++l) { + for (size_t i = 0; i < 8; ++i) { + if (i * 2 + l * 16 < count) { + uint16_t word = *(const uint16_t *) (bytes + i * 2 + l * 16); + + debugc(level, s_debug_xs_set1[(word >> 4) & 0xF]); + debugc(level, s_debug_xs_set1[word & 0xF]); + + debugc(level, s_debug_xs_set1[word >> 12]); + debugc(level, s_debug_xs_set1[(word >> 8) & 0xF]); + } else { + debugc(level, ' '); + debugc(level, ' '); + debugc(level, ' '); + debugc(level, ' '); + } + debugc(level, ' '); + } + + debugc(level, '|'); + debugc(level, ' '); + for (size_t i = 0; i < 16; ++i) { + if (i + l * 16 < count) { + char c = bytes[i + l * 16]; + + if (isprint(c)) { + debugc(level, c); + } else if (c != 0) { + debugc(level, ' '); + } else { + debugc(level, '.'); + } + } + } + debugc(level, '\n'); + } +}