]> Zhao Yanbai Git Server - kernel.git/commitdiff
支持SATA硬盘读取identify信息
authoracevest <zhaoyanbai@126.com>
Sun, 18 Jan 2026 08:51:28 +0000 (16:51 +0800)
committeracevest <zhaoyanbai@126.com>
Sun, 18 Jan 2026 08:53:32 +0000 (16:53 +0800)
drivers/ahci.c [new file with mode: 0644]
drivers/ahci.h [new file with mode: 0644]
drivers/pci.c
drivers/sata.c
drivers/sata.h [new file with mode: 0644]
include/pci.h
kernel/setup.c
qemu.sh

diff --git a/drivers/ahci.c b/drivers/ahci.c
new file mode 100644 (file)
index 0000000..4ea8f2c
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * ------------------------------------------------------------------------
+ *   File Name: ahci.c
+ *      Author: Zhao Yanbai
+ *              2026-01-16 21:32:08 Friday CST
+ * Description: none
+ * ------------------------------------------------------------------------
+ */
+
+#include <pci.h>
+#include <system.h>
+#include <ioremap.h>
+#include <string.h>
+
+#include <sata.h>
+
+#include <pci.h>
+#include <list.h>
+
+void init_ahci_device(pci_device_t* pci, int index) {
+    assert(pci != NULL);
+    assert(index >= 0);
+
+    printk("found ahci[%d] sata pci %03d:%02d.%d #%02d %04X:%04X\n", index, pci->bus, pci->dev, pci->devfn,
+           pci->intr_line, pci->vendor, pci->device);
+
+    uint32_t bar5 = pci->bars[5];
+    printk("ahci pci BAR5 value 0x%08X\n", bar5);
+    for (int i = 0; i < 6; i++) {
+        printk("  ahci pci BAR%u value 0x%08X\n", i, pci->bars[i]);
+    }
+
+    pci_write_config_word(0xFFFFFFFF, PCI_BAR5);
+    uint32_t size = pci_read_config_word(PCI_BAR5);
+    assert(size > 0);
+#if 1
+    size += 1;
+#else
+    size &= 0xFFFFFFF0;
+    size = (~size) + 1;
+#endif
+    printk("ahci pci BAR5 size 0x%08X\n", size);
+
+    uint32_t* mapped = ioremap(bar5, size);
+    if (mapped == NULL) {
+        panic("failed to map sata bar5\n");
+        return;
+    }
+    printk("ahci pci BAR5 mapped to 0x%08X\n", mapped);
+
+    struct {
+        uint32_t offset;
+        const char* name;
+    } regs[] = {
+        {0x00, "CAP"},        //
+        {0x04, "GHC"},        //
+        {0x08, "IS"},         //
+        {0x0C, "PI"},         //
+        {0x10, "VS"},         //
+        {0x14, "CCC_CTL"},    //
+        {0x18, "CCC_PORTS"},  //
+        {0x1C, "EM_LOC"},     //
+        {0x20, "EM_CTL"},     //
+        {0x24, "CAP2"},       //
+        {0x28, "BOHC"},       //
+    };
+
+    for (int i = 0; i < sizeof(regs) / sizeof(regs[0]); i++) {
+        uint32_t value = mapped[regs[i].offset / 4];
+        printk("  ahci registers 0x%08X[%s]\n", value, regs[i].name);
+    }
+
+    ahci_hba_t* hba = (ahci_hba_t*)mapped;
+
+    hba->global_hba_control |= AHCI_ENABLE;
+
+    uint32_t cap = hba->capability;
+
+    int num_ports = (cap & 0x1F) + 1;
+    int num_cs = ((cap >> 8) & 0x1F) + 1;  // command slots
+    int iss = (cap >> 20) & 0x0F;          // interface speed support
+    int s64a = (cap >> 31) & 0x01;         // supports 64bit addressing
+    printk("ahci ports %d cmd slots %d\n", num_ports, num_cs);
+    printk("support 64bit addressing %s\n", s64a ? "Y" : "N");
+
+    switch (iss) {
+    case 0b0001:
+        printk(" Gen1 (1.5 Gbps)\n");
+        break;
+    case 0b0010:
+        printk(" Gen2 (3.0 Gbps)\n");
+        break;
+    case 0b0100:
+        printk(" Gen3 (6.0 Gbps)\n");
+        break;
+    default:
+        printk(" unknown interface speed support %02X\n", iss);
+        break;
+    }
+
+    uint32_t ahci_pi = hba->ports_implemented;
+    printk("ahci port implemented %08X\n", ahci_pi);
+
+    uint32_t ahci_version = hba->version;
+    char version[16];
+    switch (ahci_version) {
+    case 0x00000905:
+        strcpy(version, "0.95");
+        break;
+    case 0x00010000:
+        strcpy(version, "1.0");
+        break;
+    case 0x00010100:
+        strcpy(version, "1.1");
+        break;
+    case 0x00010200:
+        strcpy(version, "1.2");
+        break;
+    case 0x00010300:
+        strcpy(version, "1.3");
+        break;
+    case 0x00010301:
+        strcpy(version, "1.3.1");
+        break;
+    default:
+        strcpy(version, "unknown");
+        break;
+    }
+    printk("ahci version %s[%08x]\n", version, ahci_version);
+
+    assert(sizeof(ahci_port_t) == 0x80);
+    int sata_index = 0;
+    for (int i = 0; i < num_ports; i++) {
+        if (ahci_pi & (1 << i)) {
+            ahci_port_t* port = hba->ports + i;
+            uint32_t sata_status = port->sata_status;
+            uint32_t sata_det = (sata_status >> 0) & 0x0F;  // device detection
+            uint32_t sata_spd = (sata_status >> 4) & 0x0F;  // current interface speed
+            uint32_t sata_ipm = (sata_status >> 8) & 0x0F;  // interface power management
+
+            if (sata_det != 3) {
+                continue;
+            }
+
+            if (sata_ipm != 0x01) {
+                continue;
+            }
+
+            switch (port->signature) {
+            case SATA_SIGNATURE_ATA:
+                printk("SATA device detected\n");
+                extern void init_sata_device(ahci_hba_t * hba, ahci_port_t * port, int index);
+                init_sata_device(hba, port, sata_index);
+                sata_index++;
+                break;
+            case SATA_SIGNATURE_ATAPI:
+                printk("SATAPI device detected\n");
+                break;
+            case SATA_SIGNATURE_SEMB:
+                printk("SEMB device detected\n");
+                break;
+            case SATA_SIGNATURE_PM:
+                printk("PM device detected\n");
+                break;
+            default:
+                printk("unknown ahci device detected\n");
+                break;
+            }
+        }
+    }
+
+    // TODO
+    // 开启ahci的global_hba_control的interrupt enable位
+    hba->global_hba_control |= AHCI_INTERRUPT_ENABLE;
+}
+
+void init_ahci() {
+    // progif
+    // 0x01 AHCI
+    pci_init_device(0x0106, 0x01, init_ahci_device);
+}
diff --git a/drivers/ahci.h b/drivers/ahci.h
new file mode 100644 (file)
index 0000000..9852e57
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * ------------------------------------------------------------------------
+ *   File Name: ahci.h
+ *      Author: Zhao Yanbai
+ *              2026-01-16 21:32:06 Friday CST
+ * Description: none
+ * ------------------------------------------------------------------------
+ */
+
+#pragma once
+
+#include <types.h>
+
+const static int AHCI_PORT_COUNT = 32;
+const static int AHCI_CMD_SLOT_COUNT = 32;
+
+const static int AHCI_DEVICE_NULL = 0;
+const static int AHCI_DEVICE_SATA = 1;
+const static int AHCI_DEVICE_SEMB = 2;
+const static int AHCI_DEVICE_PM = 3;
+const static int AHCI_DEVICE_SATAPI = 4;
+
+const static int AHCI_FIS_TYPE_REG_H2D = 0x27;
+const static int AHCI_FIS_TYPE_REG_D2H = 0x34;
+
+typedef struct {
+    uint8_t fis_type;
+    uint8_t pmport : 4;  // port multiplier
+    uint8_t _reserved0 : 3;
+    uint8_t c : 1;  // 0: control; 1: command
+
+    uint8_t command;
+    uint8_t feature;
+
+    uint8_t lba0;
+    uint8_t lba1;
+    uint8_t lba2;
+    uint8_t device;
+
+    uint8_t lba3;
+    uint8_t lba4;
+    uint8_t lba5;
+    uint8_t feature_high;
+
+    uint8_t count_low;
+    uint8_t count_high;
+    uint8_t icc;
+    uint8_t control;
+
+    uint8_t _reserved1[4];
+} ahci_fis_reg_h2d_t;
+
+typedef struct {
+    uint32_t data_base;  // 第0位必为0
+
+    uint32_t _data_base_upper;
+
+    uint32_t _reserved;
+
+    uint32_t data_byte_count : 22;  // 第0位必为1。另外该值表示实际为data_byte_count+1字节
+    uint32_t _reserved1 : 9;
+    uint32_t ioc : 1;  // interrupt on completion
+
+} ahci_prdt_entry_t;
+
+typedef struct {
+    // 0x00 - 0x40
+    uint8_t cmd_fis[64];
+
+    // 0x40 - 0x50
+    uint8_t _atapi_cmd[16];  // 12 or 16  bytes
+
+    // 0x50 - 0x80
+    uint8_t _reserved[48];
+
+    // 0x80
+    ahci_prdt_entry_t prdt[0];  // update to 65536 entries
+} ahci_cmd_table_t;
+
+// prd: physical region descriptor
+typedef struct {
+    uint32_t cfl : 5;  // length of the command fis: 命令FIS大小(双字为单位)
+    uint32_t a : 1;    // 该命令需要发送到ATAPI设备
+    uint32_t w : 1;    // 该命令需要向设备写入数据
+    uint32_t p : 1;    //
+    uint32_t r : 1;
+    uint32_t b : 1;
+    uint32_t c : 1;  // 命令执行完后,需要将 task_file_data的BSY位清零
+    uint32_t R : 1;
+    uint32_t pmp : 4;
+    uint32_t prdtl : 16;  // prdt entry count
+
+    uint32_t prd_byte_count;  // prd byte count
+
+    uint32_t cmd_table_base;  // 指向ahci_cmd_table_t
+    uint32_t _cmd_table_base_upper;
+
+    uint32_t reserved[4];
+} ahci_cmd_header_t;
+
+typedef struct {
+    uint32_t cmd_list_base;
+    uint32_t _cmd_list_base_upper;
+    uint32_t fis_base;
+    uint32_t _fis_base_upper;
+    uint32_t interrupt_status;
+    uint32_t interrupt_enable;
+    uint32_t command_and_status;
+    uint32_t reserved;
+    uint32_t task_file_data;
+    uint32_t signature;
+    // DET[03:00]: Device Detection
+    // SPD[07:04]: Current Interface Speed
+    // IPM[11:08]: Interface Power Management
+    uint32_t sata_status;
+    uint32_t sata_control;
+    uint32_t sata_error;
+    uint32_t sata_active;
+    uint32_t cmd_issue;
+    uint32_t sata_notification;
+    uint32_t fis_base_switch_control;
+    uint32_t device_sleep;
+    uint32_t reserved2[10];
+    uint32_t vendor_specific[4];
+} ahci_port_t;
+
+typedef struct {
+    // 0x00 - 0x2C
+    uint32_t capability;
+#define AHCI_ENABLE 0x80000000
+#define AHCI_INTERRUPT_ENABLE 0x00000002
+#define AHCI_RESET 0x00000001
+    uint32_t global_hba_control;
+    uint32_t interrupt_status;
+    uint32_t ports_implemented;
+    uint32_t version;
+    uint32_t ccc_ctrl;             // command completion coalescing control
+    uint32_t ccc_ports;            // command completion coalescing ports
+    uint32_t em_loc;               // enclosure management location
+    uint32_t em_ctl;               // enclosure management control
+    uint32_t capability_extended;  // capability extended
+    uint32_t bios_handoff_control_status;
+
+    // 0x2C - 0xA0
+    uint8_t reserved[0xA0 - 0x2C];
+
+    // 0xA0 - 0x100
+    uint8_t vendor_specific[0x100 - 0xA0];
+
+    // 0x100
+    ahci_port_t ports[32];
+} ahci_hba_t;
index c34bc082033879aca83c7f5255d453977004abab..eb5482b281b0990b1e32495705c73b746bf44821 100644 (file)
@@ -132,7 +132,6 @@ pci_device_t* pci_find_device(unsigned int vendor, unsigned int device) {
 }
 
 pci_device_t* pci_find_device_by_classcode(unsigned int classcode) {
-    int i;
     list_head_t* p;
     pci_device_t* pci = 0;
 
@@ -146,6 +145,24 @@ pci_device_t* pci_find_device_by_classcode(unsigned int classcode) {
     return 0;
 }
 
+void pci_init_device(uint32_t classcode, uint32_t progif, void (*handler)(pci_device_t*, int)) {
+    list_head_t* p = NULL;
+    pci_device_t* pci = NULL;
+
+    assert(handler != NULL);
+
+    int index = 0;
+
+    list_for_each(p, &pci_devs) {
+        pci = list_entry(p, pci_device_t, list);
+        assert(pci != NULL);
+        if (pci->classcode == classcode && pci->progif == progif) {
+            handler(pci, index);
+            index++;
+        }
+    }
+}
+
 const char* pci_intr_pin(int pin) {
     switch (pin) {
     case 0:
index 369828b681eb78e168ed25bd3dc848abbb57da46..a1755d2fdf02175e17aa0b88efa7efc473df5063 100644 (file)
  * ------------------------------------------------------------------------
  */
 
-#include <pci.h>
-#include <system.h>
+#include <sata.h>
+#include <printk.h>
 #include <ioremap.h>
+#include <page.h>
+#include <assert.h>
 #include <string.h>
+#include <system.h>
 
-void init_sata() {
-    pci_device_t* pci = pci_find_device_by_classcode(0x0106);
-    if (pci == NULL) {
-        printk("can not find pci classcode: %08x", 0x0106);
-        printk("can not find sata controller");
-        return;
+void init_sata_device(ahci_hba_t* hba, ahci_port_t* port, int index) {
+    assert(hba != NULL);
+    assert(port != NULL);
+    assert(index >= 0 && index < MAX_SATA_DEVICES);
+
+    sata_device_t* sata = sata_devices + index;
+    sata->hba = hba;
+    sata->port = port;
+    sata->index = index;
+
+    printk("ahci port clb %08x fb %08x sata_status 0x%08X signature %08X\n", port->cmd_list_base, port->fis_base,
+           port->sata_status, port->signature);
+
+    vaddr_t cmd_list_base = (vaddr_t)ioremap(PAGE_ALIGN(port->cmd_list_base), 0x1000);
+    vaddr_t fis_base = cmd_list_base;
+
+    if (PAGE_ALIGN(port->cmd_list_base) != PAGE_ALIGN(port->fis_base)) {
+        fis_base = (vaddr_t)ioremap(PAGE_ALIGN(port->fis_base), 0x1000);
     }
 
-    // progif
-    // 0x01 AHCI
+    cmd_list_base += port->cmd_list_base - PAGE_ALIGN(port->cmd_list_base);
+    fis_base += port->fis_base - PAGE_ALIGN(port->fis_base);
+
+    printk(" cmd_list_base %08x->%08x\n", port->cmd_list_base, cmd_list_base);
+    printk("      fis_base %08x->%08x\n", port->fis_base, fis_base);
+
+    sata->cmd_list_base_vaddr = (ahci_cmd_header_t*)cmd_list_base;
+    sata->fis_base_vaddr = fis_base;
+
     //
-    // 另外读取 BAR5 的 CAP 寄存器,如果 CAP 寄存器的 31 位为 1,则表示支持 AHCI 模式。
+    // 1. 端口重置
+    // 2. 分配空间,设置cmd_list_base, fis_base
+    // 3. 将sata_error清空
+    // 4. 将需要使用的中断通过interrupt_enable开启
 
-    if (pci->progif == 0x01) {
-        printk("AHCI mode supported\n");
-    } else {
-        printk("AHCI mode not supported\n");
+    //
+    assert(sizeof(ahci_cmd_header_t) == 32);
+    ahci_cmd_header_t* cmd_list = (ahci_cmd_header_t*)sata->cmd_list_base_vaddr;
+#if 1
+    for (int i = 0; i < 32; i++) {
+        ahci_cmd_header_t* hdr = &cmd_list[i];
+        printk(" cmd_list[%02d] base %08x prdt %04x:%d\n", i, hdr->cmd_table_base, hdr->prdtl, hdr->prdtl);
+        hdr->cmd_table_base = 0;
+        hdr->prdtl = 0;
     }
+#endif
+
+    sata->cmd_table_paddr = (paddr_t)page2pa(alloc_one_page(0));
+    sata->cmd_table_vaddr = (ahci_cmd_table_t*)ioremap(sata->cmd_table_paddr, PAGE_SIZE);
+    memset((void*)sata->cmd_table_vaddr, 0, PAGE_SIZE);
+
+    sata->data_paddr = (paddr_t)page2pa(alloc_one_page(0));
+    sata->data_vaddr = (vaddr_t)ioremap(sata->data_paddr, PAGE_SIZE);
+    memset((void*)sata->data_vaddr, 0, PAGE_SIZE);
+
+    memset((void*)cmd_list, 0, sizeof(ahci_cmd_header_t));
+    cmd_list[0].cfl = 0;
+    cmd_list[0].a = 0;
+    cmd_list[0].w = 0;
+    cmd_list[0].pmp = 0;
+    cmd_list[0].prdtl = 1;
+    cmd_list[0].prd_byte_count = 0;
+    cmd_list[0].cmd_table_base = sata->cmd_table_paddr;
+    sata->cmd_list0 = cmd_list;
+
+    ahci_cmd_table_t* cmd_table = (ahci_cmd_table_t*)sata->cmd_table_vaddr;
+    memset(cmd_table, 0, sizeof(ahci_cmd_table_t));
+    sata->cmd_table0 = cmd_table + 0;
+    sata->prdte0 = cmd_table[0].prdt + 0;
+
+    sata->prdte0->data_base = sata->data_paddr;
+    sata->prdte0->data_byte_count = (512 - 1) | 1;
+    sata->prdte0->ioc = 1;
 
-    printk("found ahci sata pci progif %02x %03d:%02d.%d #%02d %04X:%04X\n", pci->progif, pci->bus, pci->dev,
-           pci->devfn, pci->intr_line, pci->vendor, pci->device);
+    //
+    ahci_fis_reg_h2d_t* fis = (ahci_fis_reg_h2d_t*)sata->cmd_table0->cmd_fis;
+    memset(fis, 0, sizeof(ahci_fis_reg_h2d_t));
+    fis->fis_type = AHCI_FIS_TYPE_REG_H2D;
+    fis->c = 1;
+    fis->command = SATA_CMD_IDENTIFY;
+    fis->device = SATA_DEVICE_LBA;
+
+    sata->cmd_list0->cfl = sizeof(ahci_fis_reg_h2d_t) / sizeof(uint32_t);
+    sata->cmd_list0->prdtl = 1;
+    sata->cmd_list0->w = 0;  // read
+    sata->cmd_list0->a = 0;  // ata device
+    sata->cmd_list0->prd_byte_count = 0;
+
+    // 清除中断状态
+    // 写1清0
+    port->interrupt_status = port->interrupt_status;
 
-    uint32_t bar5 = pci->bars[5];
-    printk("ahci pci BAR5 value 0x%08X\n", bar5);
-    for (int i = 0; i < 6; i++) {
-        printk("  ahci pci BAR%u value 0x%08X\n", i, pci->bars[i]);
-        // if (pci->bars[i] != 0) {
-        //     assert((pci->bars[i] & 0x1) == 0x1);
-        // }
-    }
+    //
+    port->cmd_issue = 1 << 0;
 
-    pci_write_config_word(0xFFFFFFFF, PCI_BAR5);
-    uint32_t size = pci_read_config_word(PCI_BAR5);
-    printk("ahci pci BAR5 size 0x%08X\n", size);
+    uint32_t timeout = 1000000;
+    while (timeout--) {
+        if (port->interrupt_status & 1) {
+            break;
+        }
+        if (port->sata_error) {
+            printk("SATA ERROR: %08x\n", port->sata_error);
+            return;
+        }
+        asm("pause");
+    }
 
-    assert(size > 0);
-    size += 1;
+    if (timeout == 0) {
+        printk("SATA TIMEOUT\n");
+        return;
+    }
 
-    uint32_t* mapped = ioremap(bar5, size);
-    if (mapped == NULL) {
-        panic("failed to map sata bar5\n");
+    if (sata->cmd_list0->prd_byte_count != 512) {
+        printk("SATA PRD BYTE COUNT: %08x\n", sata->cmd_list0->prd_byte_count);
         return;
     }
-    printk("ahci pci BAR5 mapped to 0x%08X\n", mapped);
-
-    struct {
-        uint32_t offset;
-        const char* name;
-        uint32_t value;
-    } regs[] = {
-        {0x00, "CAP"},        //
-        {0x04, "GHC"},        //
-        {0x08, "IS"},         //
-        {0x0C, "PI"},         //
-        {0x10, "VS"},         //
-        {0x14, "CCC_CTL"},    //
-        {0x18, "CCC_PORTS"},  //
-        {0x1C, "EM_LOC"},     //
-        {0x20, "EM_CTL"},     //
-        {0x24, "CAP2"},       //
-        {0x28, "BOHC"},       //
-    };
-
-    for (int i = 0; i < sizeof(regs) / sizeof(regs[0]); i++) {
-        regs[i].value = mapped[regs[i].offset / 4];
-        printk("ahci registers 0x%08X[%s]\n", regs[i].value, regs[i].name);
+
+    printk("identify data %08x\n", sata->data_vaddr);
+    uint16_t* identify = (uint16_t*)sata->data_vaddr;
+
+    // 第49个word的第8个bit位表示是否支持DMA
+    // 第83个word的第10个bit位表示是否支持LBA48,为1表示支持。
+    // 第100~103个word的八个字节表示user的LBA最大值
+    if ((identify[49] & (1 << 8)) != 0) {
+        sata->dma = 1;
+    } else {
+        panic("your sata disk drive do not support DMA");
     }
 
-    uint32_t cap = mapped[0x00 / 4];
-    int num_ports = (cap & 0x1F) + 1;
-    int num_cs = (cap >> 8) & 0x1F;  // command slots
-    int iss = (cap >> 20) & 0x0F;    // interface speed support
-    int s64a = (cap >> 31) & 0x01;
-    printk("ahci num_ports %d num_cs %d\n", num_ports, num_cs);
-
-    switch (iss) {
-    case 0b0001:
-        printk(" Gen1 (1.5 Gbps)\n");
-        break;
-    case 0b0010:
-        printk(" Gen2 (3.0 Gbps)\n");
-        break;
-    case 0b0100:
-        printk(" Gen3 (6.0 Gbps)\n");
-        break;
-    default:
-        printk(" unknown iss %02X\n", iss);
-        break;
+    uint64_t max_lba = 0;
+
+    if ((identify[83] & (1 << 10)) != 0) {
+        sata->lba48 = 1;
+        max_lba = *(uint64_t*)(identify + 100);
+    } else {
+        panic("your sata disk drive do not support LBA48");
+        // max_lba = (identify[61] << 16) | identify[60];
     }
-    printk("support 64bit addressing %s\n", s64a ? "Y" : "N");
-
-    uint32_t ahci_pi = mapped[0x0C / 4];
-    printk("ahci port implemented %08X\n", ahci_pi);
-
-    uint32_t ahci_version = mapped[0x10 / 4];
-    printk("ahci version 0x%08X\n", ahci_version);
-
-    char version[16];
-    switch (ahci_version) {
-    case 0x00000905:
-        strcpy(version, "0.95");
-        break;
-    case 0x00010000:
-        strcpy(version, "1.0");
-        break;
-    case 0x00010100:
-        strcpy(version, "1.1");
-        break;
-    case 0x00010200:
-        strcpy(version, "1.2");
-        break;
-    case 0x00010300:
-        strcpy(version, "1.3");
-        break;
-    case 0x00010301:
-        strcpy(version, "1.3.1");
-        break;
-    default:
-        strcpy(version, "unknown");
-        break;
+
+    sata->max_lba = max_lba;
+
+    printk("%s %s size: %u MB ", sata->dma == 1 ? "DMA" : "", sata->lba48 == 1 ? "LBA48" : "LBA28",
+           (max_lba * 512) >> 20);
+
+    uint16_t sata_major = identify[80];
+    uint16_t sata_minor = identify[81];
+    if (sata_major & (1 << 8)) {
+        printk("ATA8-ACS ");
     }
-    printk("ahci version %s\n", version);
-
-    typedef struct {
-        uint32_t cmd_list_base;
-        uint32_t cmd_list_base_upper;
-        uint32_t fis_base;
-        uint32_t fis_base_upper;
-        uint32_t interrupt_status;
-        uint32_t interrupt_enable;
-        uint32_t command_and_status;
-        uint32_t reserved;
-        uint32_t task_file_data;
-        uint32_t signature;
-        uint32_t sata_status;
-        uint32_t sata_control;
-        uint32_t sata_error;
-        uint32_t sata_active;
-        uint32_t cmd_issue;
-        uint32_t sata_notification;
-        uint32_t fis_base_switch_control;
-        uint32_t device_sleep;
-        uint32_t reserved2[10];
-        uint32_t vendor_specific[4];
-    } ahci_port_t;
-
-    assert(sizeof(ahci_port_t) == 0x80);
-    for (int i = 0; i < 32; i++) {
-        if (ahci_pi & (1 << i)) {
-            // printk("ahci port %d implemented\n", i);
-            ahci_port_t* port = (ahci_port_t*)(mapped + (0x100 + i * 0x80) / 4);
-            // printk("ahci port %d dt 0x%08X\n", i, port->command_and_status);
-            uint32_t sata_status = port->sata_status;
-            uint32_t sata_det = (sata_status >> 0) & 0x0F;
-            uint32_t sata_ipm = (sata_status >> 8) & 0x0F;
-
-            if (sata_det != 3) {
-                continue;
-            }
-
-            if (sata_ipm != 0x01) {
-                continue;
-            }
-
-            printk("ahci port %d clb %08x fb %08x sata_status 0x%08X signature %08X\n", i, port->cmd_list_base,
-                   port->fis_base, sata_status, port->signature);
-
-#define SATA_SIGNATURE_ATA 0x00000101
-#define SATA_SIGNATURE_ATAPI 0xEB140101
-#define SATA_SIGNATURE_SEMB 0xC33C0101
-#define SATA_SIGNATURE_PM 0x96690101
-
-            switch (port->signature) {
-            case SATA_SIGNATURE_ATA:
-                printk("SATA device detected\n");
-                break;
-            case SATA_SIGNATURE_ATAPI:
-                printk("SATAPI device detected\n");
-                break;
-            case SATA_SIGNATURE_SEMB:
-                printk("SEMB device detected\n");
-                break;
-            case SATA_SIGNATURE_PM:
-                printk("PM device detected\n");
-                break;
-            default:
-                printk("unknown device detected\n");
-                break;
-            }
-        }
+    if (sata_major & (1 << 7)) {
+        printk("ATA/ATAPI-7 ");
+    }
+    if (sata_major & (1 << 6)) {
+        printk("ATA/ATAPI-6 ");
+    }
+    if (sata_major & (1 << 5)) {
+        printk("ATA/ATAPI-5 ");
+    }
+    if (sata_major & (1 << 4)) {
+        printk("ATA/ATAPI-4 ");
+    }
+
+    printk("[Major %04X Minor %02X]\n", sata_major, sata_minor);
+
+    char s[64];
+    sata_read_identify_string(identify, 10, 19, s);
+    printk("SN: %s\n", s);
+
+    sata_read_identify_string(identify, 23, 26, s);
+    printk("Firmware Revision: %s\n", s);
+
+    sata_read_identify_string(identify, 27, 46, s);
+    printk("HD Model: %s\n", s);
+}
+
+sata_device_t sata_devices[MAX_SATA_DEVICES] = {0};
+
+void sata_read_identify_string(const uint16_t* identify, int bgn, int end, char* buf) {
+    const char* p = (const char*)(identify + bgn);
+    int i = 0;
+    for (; i <= (end - bgn); i++) {
+        buf[2 * i + 1] = p[0];
+        buf[2 * i + 0] = p[1];
+        p += 2;
     }
+    buf[i] = 0;
 }
diff --git a/drivers/sata.h b/drivers/sata.h
new file mode 100644 (file)
index 0000000..e233b16
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * ------------------------------------------------------------------------
+ *   File Name: sata.h
+ *      Author: Zhao Yanbai
+ *              2026-01-16 22:10:01 Friday CST
+ * Description: none
+ * ------------------------------------------------------------------------
+ */
+
+#pragma once
+
+#include <ahci.h>
+#include <types.h>
+
+#define SATA_SIGNATURE_ATA 0x00000101
+#define SATA_SIGNATURE_ATAPI 0xEB140101
+#define SATA_SIGNATURE_SEMB 0xC33C0101
+#define SATA_SIGNATURE_PM 0x96690101
+
+#define MAX_SATA_DEVICES 4
+
+#define SATA_CMD_READ_PIO 0x20
+#define SATA_CMD_READ_PIO_EXT 0x24
+#define SATA_CMD_READ_DMA_EXT 0x25
+#define SATA_CMD_READ_DMA 0xC8
+#define SATA_CMD_IDENTIFY 0xEC
+
+// 参考ATA的DEVICE的定义
+// DEVICE寄存器
+// bit7: Obsolete Always set.
+// bit6: L 如果为1,LBA Mode
+// bit5: Obsolete Always set.
+// bit4: DRIVE
+// bit[0, 3] HS 如果L为0就是磁头号Head Number,如果L为1,则为LBA28的24-27位
+// 虽然理论上每个SATA端口复用器(PM)可以连接多个设备,但不计划支持,所以DRIVE位为0
+// 另外LBA在FIS中填写,所以bit0~3都可为0
+// 最后:在现代AHCI单设备场景中,这个字段通常被设为0,控制器会自动处理。大多数驱动设为0即可。
+// 所以这里定义成0x00 或 0xE0 都行
+#define SATA_DEVICE_LBA ((1 << 7) | (1 << 6) | (1 << 5))
+
+typedef struct {
+    ahci_hba_t* hba;
+    ahci_port_t* port;
+    int index;
+
+    int dma;    // 是否支持dma
+    int lba48;  // 是否支持lba48
+    uint64_t max_lba;
+
+    // 虽然有32个cmd header,但只用第0个
+    ahci_cmd_header_t* cmd_list_base_vaddr;
+    vaddr_t fis_base_vaddr;
+
+    // 分配一个cmd table页,但只用第0个cmd table
+    paddr_t cmd_table_paddr;
+    ahci_cmd_table_t* cmd_table_vaddr;
+
+    // 分配一个prdt页,但只用第0个prdt entry
+    paddr_t prdte_paddr;
+    vaddr_t prdte_vaddr;
+
+    // 分配一个数据页
+    paddr_t data_paddr;
+    vaddr_t data_vaddr;
+
+    // 第0个cmd header
+    ahci_cmd_header_t* cmd_list0;
+    // 第0个cmd table
+    ahci_cmd_table_t* cmd_table0;
+    // 第0个prdte
+    ahci_prdt_entry_t* prdte0;
+} sata_device_t;
+
+extern sata_device_t sata_devices[MAX_SATA_DEVICES];
+
+void sata_read_identify_string(const uint16_t* identify, int bgn, int end, char* buf);
index 773cac86cb8813a8af6685c005a5b322eb620767..fb4cf4ce1d9d6f599fbde2e81aea633cff123afa 100644 (file)
@@ -268,6 +268,8 @@ typedef union pci_device
 pci_device_t* pci_find_device(unsigned int vendor, unsigned int device);
 pci_device_t* pci_find_device_by_classcode(unsigned int classcode);
 
+void pci_init_device(uint32_t classcode, uint32_t progif, void (*handler)(pci_device_t* pci, int index));
+
 static inline u32 pci_cmd(pci_device_t* pci, unsigned int reg) {
     return PCI_CMD(pci->bus, pci->dev, pci->devfn, reg);
 }
index 544c5902f9b5d52410e607f9248b463b908c2d00..68218d7063f305d3e94cf0a38912696c21411330 100644 (file)
@@ -191,8 +191,8 @@ void setup_kernel() {
     void ide_init();
     ide_init();
 #endif
-    void init_sata();
-    init_sata();
+    void init_ahci();
+    init_ahci();
 
     void dump_fixmap();
     dump_fixmap();
diff --git a/qemu.sh b/qemu.sh
index a6aedc4fba2ce3f2f654c1138963a5acc7fddeca..b4bbf40c3bf58bc684af3be042a8db5786a3fe8c 100755 (executable)
--- a/qemu.sh
+++ b/qemu.sh
@@ -19,18 +19,18 @@ set -m
 
 qemu-system-i386 \
     -boot d \
-    -m 3100\
+    -m 128\
     -smp 2 \
     -cpu qemu32,+x2apic \
     -machine q35 \
     -serial tcp::6666,server,nowait \
-    -drive file=hd.img,format=raw,index=0,media=disk \
-    -drive file=kernel.iso,index=1,media=cdrom \
     -drive file=sata.img,format=raw,if=none,id=sata-disk \
+    -drive file=sata2.img,format=raw,if=none,id=sata-disk2 \
     -drive file=kernel.iso,format=raw,if=none,id=atapi-cdrom \
     -device ahci,id=ahci0 \
     -device ide-hd,drive=sata-disk,bus=ahci0.0 \
     -device ide-cd,drive=atapi-cdrom,bus=ahci0.1 \
+    -device ide-hd,drive=sata-disk2,bus=ahci0.5 \
     -name kernel \
     -vga std \
     -display cocoa \