From: acevest Date: Sat, 24 Jan 2026 15:10:51 +0000 (+0800) Subject: 初步支持DMA读SATA硬盘扇区数据 X-Git-Url: http://repos.zhaoyanbai.com/Mou_128.png?a=commitdiff_plain;h=8f323b7a7fe5a263c5814f3c0b2c4b21195c7c03;p=kernel.git 初步支持DMA读SATA硬盘扇区数据 --- diff --git a/drivers/ahci.c b/drivers/ahci.c index 4ea8f2c..65a8a2b 100644 --- a/drivers/ahci.c +++ b/drivers/ahci.c @@ -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"); diff --git a/drivers/ahci.h b/drivers/ahci.h index b7e1405..415d874 100644 --- a/drivers/ahci.h +++ b/drivers/ahci.h @@ -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 diff --git a/drivers/sata.c b/drivers/sata.c index 031cf48..ff495a7 100644 --- a/drivers/sata.c +++ b/drivers/sata.c @@ -17,26 +17,40 @@ 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; diff --git a/drivers/sata.h b/drivers/sata.h index 69e3599..9cd8a75 100644 --- a/drivers/sata.h +++ b/drivers/sata.h @@ -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); diff --git a/kernel/apic.c b/kernel/apic.c index a92310d..7a157f5 100644 --- a/kernel/apic.c +++ b/kernel/apic.c @@ -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 }