Add AHCI SATA device enumeration + SATA device IDENTIFY

This commit is contained in:
Mark
2019-10-21 16:14:34 +03:00
parent 2d41d1c27a
commit 61dd73b79f
8 changed files with 484 additions and 154 deletions
+1
View File
@@ -5,3 +5,4 @@ doc/latex
bochsrc.txt
config
test.img
eth_*
+2
View File
@@ -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)
+227
View File
@@ -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);
+17
View File
@@ -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
+7
View File
@@ -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);
+181
View File
@@ -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 = &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;
}
ahci_port_init(i, port);
}
}
}
+7 -154
View File
@@ -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);
}
+42
View File
@@ -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 <stdint.h>
@@ -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');
}
}