]> Zhao Yanbai Git Server - kernel.git/commitdiff
初步支持DMA读SATA硬盘扇区数据
authoracevest <zhaoyanbai@126.com>
Sat, 24 Jan 2026 15:10:51 +0000 (23:10 +0800)
committeracevest <zhaoyanbai@126.com>
Sat, 24 Jan 2026 15:10:51 +0000 (23:10 +0800)
drivers/ahci.c
drivers/ahci.h
drivers/sata.c
drivers/sata.h
kernel/apic.c

index 4ea8f2c1b26ecc4b8fa859622ce99ca5d9dc0043..65a8a2b6ba210cea81cd0278f023b9abea9d661c 100644 (file)
@@ -129,7 +129,6 @@ void init_ahci_device(pci_device_t* pci, int index) {
     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;
@@ -148,10 +147,10 @@ void init_ahci_device(pci_device_t* pci, int index) {
 
             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++;
+                init_sata_device(hba, port, i);
+                printk("SATA device detected at port %d\n", i);
+
                 break;
             case SATA_SIGNATURE_ATAPI:
                 printk("SATAPI device detected\n");
index b7e140520df8b9d28eee153a94f7d2b7a38785f6..415d874286fc5abcb40e2b4ffaaf083e472568dc 100644 (file)
@@ -22,6 +22,27 @@ 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;
+const static int AHCI_FIS_TYPE_DMA_ACTIVE = 0x39;
+const static int AHCI_FIS_TYPE_DMA_SETUP = 0x41;
+const static int AHCI_FIS_TYPE_DATA = 0x46;
+
+const static uint32_t AHCI_INTERRUPT_STATUS_DHRS = (1U << 0);   // device to host fis interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_PSS = (1U << 1);    // pio setup fis interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_DSS = (1U << 2);    // DMA setup fis interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_SDBS = (1U << 3);   // set device bits interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_UFS = (1U << 4);    // unknown fis interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_DPS = (1U << 5);    // descriptor process error interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_PCS = (1U << 6);    // port change error interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_DMPS = (1U << 7);   // device mechanical presence
+const static uint32_t AHCI_INTERRUPT_STATUS_PRCS = (1U << 22);  // phyrdy change interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_IPMS = (1U << 23);  // incorrect port multiplier interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_OFS = (1U << 24);   // overflow status interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_INFS = (1U << 26);  // interface non-fatal error interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_IFS = (1U << 27);   // interface fatal error interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_HBDS = (1U << 28);  // host bus data error interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_HBFS = (1U << 29);  // host bus fatal error interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_TFES = (1U << 30);  // task file error interrupt
+const static uint32_t AHCI_INTERRUPT_STATUS_CPDS = (1U << 31);  // cold port detect interrupt
 
 const static uint32_t AHCI_INTERRUPT_ENABLE_DHRS = (1U << 0);   // device to host fis interrupt enable
 const static uint32_t AHCI_INTERRUPT_ENABLE_PSE = (1U << 1);    // pio setup fis interrupt enable
@@ -68,6 +89,40 @@ typedef struct {
     uint8_t _reserved1[4];
 } ahci_fis_reg_h2d_t;
 
+// 双向
+typedef struct {
+    uint8_t fis_type;
+    uint8_t pmport : 4;
+    uint8_t _r : 1;
+    uint8_t direction : 1;    // direction: 0: transmitter->receiver; 1: receiver->transmitter
+    uint8_t i : 1;            // interrupt
+    uint8_t auto_active : 1;  // auto-activate
+
+    uint8_t _reserved0[2];
+
+    uint32_t dma_buffer_identifier;
+    uint32_t _dma_buffer_identifier_upper;
+
+    uint32_t _reserved1;
+
+    uint32_t dma_buffer_offset;   // bit[1:0] 应该为0
+    uint32_t dma_transfer_count;  // bit[0] 应该为0
+
+    uint32_t _reserved2;
+} ahci_fis_dma_setup_t;
+
+// 双向
+typedef struct {
+    uint8_t fis_type;
+
+    uint8_t pmport : 4;
+    uint8_t _r : 4;
+
+    uint8_t _reserved[2];
+
+    uint8_t data[0];
+} ahci_fis_data_t;
+
 typedef struct {
     uint32_t data_base;  // 第0位必为0
 
@@ -100,10 +155,10 @@ 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 p : 1;    // Prefetchable
+    uint32_t r : 1;    // reset
+    uint32_t b : 1;    // BIST FIS
+    uint32_t c : 1;    // 命令执行完后,需要将 task_file_data的BSY位清零
     uint32_t R : 1;
     uint32_t pmp : 4;
     uint32_t prdtl : 16;  // prdt entry count
index 031cf48f7554cfbe77d1ca4097570c6b297658d2..ff495a789dfee6203586835f3a558cb611b5dd1a 100644 (file)
 
 int sata_irq_triggered = 0;
 
-void init_sata_device(ahci_hba_t* hba, ahci_port_t* port, int index) {
+int max_sata_devices = 0;
+sata_device_t sata_devices[MAX_SATA_DEVICES] = {0};
+
+uint8_t sector[512];
+void init_sata_device(ahci_hba_t* hba, ahci_port_t* port, int port_index) {
+    int index = max_sata_devices;
+
     assert(hba != NULL);
     assert(port != NULL);
+    assert(port_index >= 0 && port_index < MAX_SATA_DEVICES);
     assert(index >= 0 && index < MAX_SATA_DEVICES);
 
     sata_device_t* sata = sata_devices + index;
+    memset(sata, 0, sizeof(sata_device_t));
     sata->hba = hba;
     sata->port = port;
     sata->index = index;
+    sata->port_index = port_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;
+    vaddr_t fis_base = 0;
 
-    if (PAGE_ALIGN(port->cmd_list_base) != PAGE_ALIGN(port->fis_base)) {
+    // fis_base大概率和cmd_list_base在同一个页上
+    // 如果不在同一个页上,那么需要单独分配空间
+    if (PAGE_ALIGN(port->cmd_list_base) == PAGE_ALIGN(port->fis_base)) {
+        fis_base = cmd_list_base;
+    } else {
         fis_base = (vaddr_t)ioremap(PAGE_ALIGN(port->fis_base), 0x1000);
     }
 
+    // 算出各自在页内的偏移
     cmd_list_base += port->cmd_list_base - PAGE_ALIGN(port->cmd_list_base);
     fis_base += port->fis_base - PAGE_ALIGN(port->fis_base);
 
@@ -81,6 +95,129 @@ void init_sata_device(ahci_hba_t* hba, ahci_port_t* port, int index) {
 
     //
     sata_identify(sata);
+
+    if (!sata->lba48) {
+        return;
+    }
+
+    if (!sata->dma) {
+        return;
+    }
+
+    max_sata_devices += 1;
+
+    memset(sector, 0xCC, 512);
+    sata_dma_read(sata, 0, 1, (paddr_t)va2pa(sector));
+    printk("sector %08x\n", (uint32_t*)sector);
+}
+
+void sata_irq_handler(unsigned int irq, pt_regs_t* regs, void* dev_id) {
+    sata_irq_triggered = 1;
+
+    for (int i = 0; i < max_sata_devices; i++) {
+        sata_device_t* sata = sata_devices + i;
+        ahci_port_t* port = sata->port;
+        assert(port != NULL);
+
+        //
+        uint32_t interrupt_status = port->interrupt_status;
+        if (0 == interrupt_status) {
+            continue;
+        }
+
+        printk("SATA[%u] IRQ[%u] is %08x\n", i, irq, interrupt_status);
+        if (interrupt_status & AHCI_INTERRUPT_STATUS_DHRS) {
+            //
+        }
+
+        port->interrupt_status = interrupt_status;
+    }
+
+    ioapic_eoi();
+}
+
+bool sata_ready(sata_device_t* sata) {
+    if (sata->port->task_file_data & 0x80) {
+        return false;
+    }
+    return true;
+}
+int sata_wait_ready(sata_device_t* sata) {
+    assert(sata != NULL);
+    ahci_port_t* port = sata->port;
+    assert(port != NULL);
+
+    // 清除error 和 中断状态
+    port->sata_error = port->sata_error;
+    port->interrupt_status = port->interrupt_status;
+
+    uint32_t timeout = 1000000;
+    while (timeout--) {
+        if (sata_ready(sata)) {
+            return 0;
+        }
+        asm("pause");
+    }
+    return -1;
+}
+
+int sata_dma_read(sata_device_t* sata, uint64_t lba, uint32_t sectors, paddr_t paddr) {
+    assert(sata != NULL);
+    assert(sectors <= 4);
+    assert(lba + sectors <= sata->max_lba);
+
+    ahci_port_t* port = sata->port;
+    assert(port != NULL);
+
+    uint32_t bytes = sectors * 512;
+
+    //
+    if (0 != sata_wait_ready(sata)) {
+        panic("sata wait ready timeout");
+    }
+
+    //
+    sata->prdte0->data_base = paddr;
+    sata->prdte0->data_byte_count = (bytes - 1) | 1;
+    sata->prdte0->ioc = 1;
+
+    //
+    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_READ_DMA_EXT;
+    fis->device = SATA_DEVICE_LBA;
+    fis->lba0 = (lba >> (0 * 8)) & 0xFF;
+    fis->lba1 = (lba >> (1 * 8)) & 0xFF;
+    fis->lba2 = (lba >> (2 * 8)) & 0xFF;
+    fis->lba3 = (lba >> (3 * 8)) & 0xFF;
+    fis->lba4 = (lba >> (4 * 8)) & 0xFF;
+    fis->lba5 = (lba >> (5 * 8)) & 0xFF;
+
+    //
+    fis->count_low = (sectors >> 0) & 0xFF;
+    fis->count_high = (sectors >> 8) & 0xFF;
+
+    //
+    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->c = 1;
+    sata->cmd_list0->prd_byte_count = 0;
+
+    //
+    port->sata_error = port->sata_error;
+    port->interrupt_status = port->interrupt_status;
+
+    //
+    port->interrupt_enable = AHCI_INTERRUPT_ENABLE_DHRS;
+
+    //
+    port->cmd_issue = 1 << 0;
+
+    return 0;
 }
 
 void sata_identify(sata_device_t* sata) {
@@ -101,6 +238,7 @@ void sata_identify(sata_device_t* sata) {
     sata->cmd_list0->prdtl = 1;
     sata->cmd_list0->w = 0;  // read
     sata->cmd_list0->a = 0;  // ata device
+    sata->cmd_list0->c = 1;
     sata->cmd_list0->prd_byte_count = 0;
 
     // 清除中断状态
@@ -198,8 +336,6 @@ void sata_identify(sata_device_t* sata) {
     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;
index 69e359931e2081582c584e14956a2bae57650750..9cd8a7528f74918fe62092d42b44ce3d5c33f5f0 100644 (file)
@@ -42,6 +42,7 @@ typedef struct {
     ahci_hba_t* hba;
     ahci_port_t* port;
     int index;
+    int port_index;
 
     int dma;    // 是否支持dma
     int lba48;  // 是否支持lba48
@@ -73,6 +74,8 @@ typedef struct {
 
 extern sata_device_t sata_devices[MAX_SATA_DEVICES];
 
+int sata_dma_read(sata_device_t* sata, uint64_t lba, uint32_t sectors, paddr_t paddr);
+
 void sata_read_identify_string(const uint16_t* identify, int bgn, int end, char* buf);
 
 void sata_identify(sata_device_t* sata);
index a92310de19c43c56a950ba403519befd293fdfc4..7a157f59e909776ca5a90c0f1de11a43eba15f8e 100644 (file)
@@ -274,13 +274,6 @@ void ioapic_eoi() {
     system.lapic->write(LAPIC_EOI, 0);
 }
 
-extern int sata_irq_triggered;
-void sata0_irq_handler() {
-    printk("SATA#0 IRQ\n");
-    sata_irq_triggered = 1;
-    ioapic_eoi();
-}
-
 void ioapic_init() {
     // 把IO APIC映射进地址空间
     ioapic_map.phys_base = system.ioapic_addr;
@@ -359,7 +352,8 @@ void ioapic_init() {
     ioapic_rte_write(IOAPIC_RTE(irq), rte.value);
     irq_set_chip(irq, &ioapic_chip);
 
-    request_irq(irq, sata0_irq_handler, "SATA#0", "SATA#0");
+    extern void sata_irq_handler(unsigned int irq, pt_regs_t* regs, void* dev_id);
+    request_irq(irq, sata_irq_handler, "SATA", "SATA");
 #endif
 }