From: acevest Date: Mon, 15 Nov 2021 14:06:38 +0000 (+0800) Subject: ide irq read ata identify X-Git-Url: http://zhaoyanbai.com/repos/%7B%24global.css%7D?a=commitdiff_plain;h=4267967054df3e9391adf381c1ad9bf71027045e;p=kernel.git ide irq read ata identify --- diff --git a/drivers/ata.c b/drivers/ata.c index 01e32d1..1f070d6 100644 --- a/drivers/ata.c +++ b/drivers/ata.c @@ -41,21 +41,14 @@ void ata_test(uint64_t nr) { // 2. 等到status的BSY位清除 // 3. 等到status的DRQ位或ERR位设置 u16 identify[256]; -void ata_read_identify(int dev) { // 这里所用的dev是逻辑编号 ATA0、ATA1下的Master、Salve的dev分别为0,1,2,3 - - outb(ATA_CTL_NIEN, REG_CTL(dev)); // 在读IDENTIFY的时候禁用硬盘中断 +void ata_send_read_identify_cmd(int dev) { + outb(0x00, REG_CTL(dev)); outb(0x00 | ((dev & 0x01) << 4), REG_DEVICE(dev)); // 根据文档P113,这里不用指定bit5, bit7,直接指示DRIVE就行 outb(ATA_CMD_IDENTIFY, REG_CMD(dev)); - while (1) { - u8 status = inb(REG_STATUS(dev)); - printk("hard disk status: %x %x\n", status, REG_STATUS(dev)); - if (status == 0) { - panic("no ata device"); - } - if ((status & ATA_STATUS_BSY) == 0 && (status & ATA_STATUS_DRQ) != 0) { - break; - } - } +} +void ata_read_identify(int dev) { // 这里所用的dev是逻辑编号 ATA0、ATA1下的Master、Salve的dev分别为0,1,2,3 + + sleep_on_ide(); insw(REG_DATA(dev), identify, SECT_SIZE / sizeof(u16)); diff --git a/drivers/bak.ata b/drivers/bak.ata new file mode 100644 index 0000000..01e32d1 --- /dev/null +++ b/drivers/bak.ata @@ -0,0 +1,230 @@ +/* + * ------------------------------------------------------------------------ + * File Name: ata.c + * Author: Zhao Yanbai + * 2021-11-10 13:55:27 Wednesday CST + * Description: none + * ------------------------------------------------------------------------ + */ +#include +#include +#include +#include +#include +#include + +extern ide_pci_controller_t ide_pci_controller; + +#define ATA_TIMEOUT 10 // 10次时钟中断 + +void ata_dma_read_ext(int dev, uint64_t pos, uint16_t count, void *dest); +int ata_pio_read_ext(int dev, uint64_t pos, uint16_t count, int timeout, void *dest); + +void *mbr_buf; +void ata_test(uint64_t nr) { + memset(mbr_buf, 0xAA, SECT_SIZE); + ata_dma_read_ext(0, nr, 1, mbr_buf); +} + +// 本程序参考文档《AT Attachment with Packet Interface - 6》 + +// 仅考虑ATA硬盘的情况 +// 一个IDE接口能接Master、Slave两个DRIVE。 +// 一个PC机上通常有两个IDE接口(IDE0, IDE1或ATA0, ATA1),通常称通道0、1 +// 对于同一个IDE通道的两个DRIVE,共享同一组寄存器,它们之间的区分是通过Device寄存器的第4个bit位来实现的。0为Master,1为Slave +// +// 使用IDENTIFY命令步骤: +// 1. 选择DRIVE构造命令,发送到Device寄存器(发送到master: 0x00, 发送到slave: 0x40) +// 2. 发送IDENTIFY(0xEC)命令到该通道的命令寄存器 +// 检查status寄存器: +// 1. 若为0,就认为没有IDE +// 2. 等到status的BSY位清除 +// 3. 等到status的DRQ位或ERR位设置 +u16 identify[256]; +void ata_read_identify(int dev) { // 这里所用的dev是逻辑编号 ATA0、ATA1下的Master、Salve的dev分别为0,1,2,3 + + outb(ATA_CTL_NIEN, REG_CTL(dev)); // 在读IDENTIFY的时候禁用硬盘中断 + outb(0x00 | ((dev & 0x01) << 4), REG_DEVICE(dev)); // 根据文档P113,这里不用指定bit5, bit7,直接指示DRIVE就行 + outb(ATA_CMD_IDENTIFY, REG_CMD(dev)); + while (1) { + u8 status = inb(REG_STATUS(dev)); + printk("hard disk status: %x %x\n", status, REG_STATUS(dev)); + if (status == 0) { + panic("no ata device"); + } + if ((status & ATA_STATUS_BSY) == 0 && (status & ATA_STATUS_DRQ) != 0) { + break; + } + } + + insw(REG_DATA(dev), identify, SECT_SIZE / sizeof(u16)); + + // 第49个word的第8个bit位表示是否支持DMA + // 第83个word的第10个bit位表示是否支持LBA48,为1表示支持。 + // 第100~103个word的八个字节表示user的LBA最大值 + printk("%04x %04x %d %d\n", identify[49], 1 << 8, identify[49] & (1 << 8), (identify[49] & (1 << 8)) != 0); + if ((identify[49] & (1 << 8)) != 0) { + printk("support DMA\n"); + } + + if ((identify[83] & (1 << 10)) != 0) { + printk("support LBA48\n"); + + u64 lba = *(u64 *)(identify + 100); + printk("hard disk size: %u MB\n", (lba * 512) >> 20); + } + + printk("bus iobase %x cmd %x status %x prdt %x \n", ide_pci_controller.bus_iobase, ide_pci_controller.bus_cmd, + ide_pci_controller.bus_status, ide_pci_controller.bus_prdt); + + // TODO REMOVE + mbr_buf = kmalloc(SECT_SIZE, 0); + // ata_test(0); + + // ata_pio_read_ext(0, 0, 1, ATA_TIMEOUT, mbr_buf); + // uint16_t *p = (uint16_t *)mbr_buf; + // for (int i = 0; i < 256; i++) { + // if (i % 12 == 0) { + // printk("\n>%03d ", i); + // } + // printk("%04x ", p[i]); + // } +} + +// ATA_CMD_READ_DMA_EXT +void ata_dma_read_ext(int dev, uint64_t pos, uint16_t count, void *dest) { + // 停止DMA + outb(PCI_IDE_CMD_STOP, ide_pci_controller.bus_cmd); + + // 配置描述符表 + unsigned long dest_paddr = va2pa(dest); + ide_pci_controller.prdt[0].phys_addr = dest_paddr; + ide_pci_controller.prdt[0].byte_count = SECT_SIZE; + ide_pci_controller.prdt[0].reserved = 0; + ide_pci_controller.prdt[0].eot = 1; + outl(va2pa(ide_pci_controller.prdt), ide_pci_controller.bus_prdt); + + printk("paddr: %x prdt: %x %x prdte %x %x\n", dest_paddr, ide_pci_controller.prdt, va2pa(ide_pci_controller.prdt), + ide_pci_controller.prdt[0].phys_addr, *(((unsigned int *)ide_pci_controller.prdt) + 1)); + + // 清除中断位和错误位 + // 这里清除的方式是是设置1后清除 + outb(PCI_IDE_STATUS_INTR | PCI_IDE_STATUS_ERR, ide_pci_controller.bus_status); + + // 不再设置nIEN,DMA需要中断 + outb(0x00, REG_CTL(dev)); + + // 等待硬盘不BUSY + while (inb(REG_STATUS(dev)) & ATA_STATUS_BSY) { + nop(); + } + + // 选择DRIVE + outb(ATA_LBA48_DEVSEL(dev), REG_DEVICE(dev)); + + // 先写扇区数的高字节 + outb((count >> 8) & 0xFF, REG_NSECTOR(dev)); + + // 接着写LBA48,高三个字节 + outb((pos >> 24) & 0xFF, REG_LBAL(dev)); + outb((pos >> 32) & 0xFF, REG_LBAM(dev)); + outb((pos >> 40) & 0xFF, REG_LBAH(dev)); + + // 再写扇区数的低字节 + outb((count >> 0) & 0xFF, REG_NSECTOR(dev)); + + // 接着写LBA48,低三个字节 + outb((pos >> 0) & 0xFF, REG_LBAL(dev)); + outb((pos >> 8) & 0xFF, REG_LBAM(dev)); + outb((pos >> 16) & 0xFF, REG_LBAH(dev)); + + // 等待硬盘READY + while (inb(REG_STATUS(dev)) & ATA_STATUS_RDY == 0) { + nop(); + } + + outb(ATA_CMD_READ_DMA_EXT, REG_CMD(dev)); + + // 这一句非常重要,如果不加这一句 + // 在qemu中用DMA的方式读数据就会读不到数据,而只触是发中断,然后寄存器(Bus Master IDE Status + // Register)的值会一直是5 也就是INTERRUPT和和ACTIVE位是1,正常应该是4,也就是只有INTERRUPT位为1 + // 在bochs中则加不加这一句不会有影响,都能正常读到数据 + unsigned int v = pci_read_config_word(pci_cmd(ide_pci_controller.pci, PCI_COMMAND)); + printk(" ide pci command %04x\n", v); + pci_write_config_word(v | PCI_COMMAND_MASTER, pci_cmd(ide_pci_controller.pci, PCI_COMMAND)); + + // 指定DMA操作为读取硬盘操作,内核用DMA读取,对硬盘而言是写出 + // 并设置DMA的开始位,开始DMA + outb(PCI_IDE_CMD_WRITE | PCI_IDE_CMD_START, ide_pci_controller.bus_cmd); +} + +// ATA_CMD_READ_PIO_EXT +int ata_pio_read_ext(int dev, uint64_t pos, uint16_t count, int timeout, void *dest) { + // PIO读,禁用中断 + outb(ATA_CTL_NIEN, REG_CTL(dev)); + + // 等待硬盘不BUSY + while (inb(REG_STATUS(dev)) & ATA_STATUS_BSY) { + nop(); + } + + // 选择DRIVE + outb(ATA_LBA48_DEVSEL(dev), REG_DEVICE(dev)); + + // 先写扇区数的高字节 + outb((count >> 8) & 0xFF, REG_NSECTOR(dev)); + + // 接着写LBA48,高三个字节 + outb((pos >> 24) & 0xFF, REG_LBAL(dev)); + outb((pos >> 32) & 0xFF, REG_LBAM(dev)); + outb((pos >> 40) & 0xFF, REG_LBAH(dev)); + + // 再写扇区数的低字节 + outb((count >> 0) & 0xFF, REG_NSECTOR(dev)); + + // 接着写LBA48,低三个字节 + outb((pos >> 0) & 0xFF, REG_LBAL(dev)); + outb((pos >> 8) & 0xFF, REG_LBAM(dev)); + outb((pos >> 16) & 0xFF, REG_LBAH(dev)); + + while (inb(REG_STATUS(dev)) & ATA_STATUS_RDY == 0) { + nop(); + } + + outb(ATA_CMD_READ_PIO_EXT, REG_CMD(dev)); + + while (timeout > 0) { + timeout--; + + u8 status = inb(REG_STATUS(dev)); + if ((status & ATA_STATUS_BSY) == 0 && (status & ATA_STATUS_DRQ) != 0) { + break; + } + + asm("sti;hlt;"); + } + asm("cli"); + + if (timeout == 0) { + return -1; + } + + insl(REG_DATA(dev), dest, (SECT_SIZE * count) / sizeof(uint32_t)); + + return 0; +} + +uint8_t ata_pci_bus_status() { + uint8_t st = 0; + st = inb(ide_pci_controller.bus_status); + + outb(PCI_IDE_STATUS_INTR, ide_pci_controller.bus_status); + + return st; +} + +unsigned int ATA_CHL0_CMD_BASE = 0x1F0; +unsigned int ATA_CHL1_CMD_BASE = 0x170; + +unsigned int ATA_CHL0_CTL_BASE = 0x3F6; +unsigned int ATA_CHL1_CTL_BASE = 0x376; \ No newline at end of file diff --git a/drivers/console.c b/drivers/console.c index 59b2f7b..e9eaf60 100644 --- a/drivers/console.c +++ b/drivers/console.c @@ -53,7 +53,7 @@ static void cnsl_queue_init(cnsl_queue_t *cq) { memset((void *)cq, 0, sizeof(*cq)); cq->head = 0; cq->tail = 0; - init_wait_queue(&cq->wait); + init_wait_queue_head(&cq->wait); } wait_queue_head_t rdwq; @@ -62,7 +62,7 @@ void cnsl_init() { cnsl_queue_init(&cnsl.rd_q); cnsl_queue_init(&cnsl.wr_q); cnsl_queue_init(&cnsl.sc_q); - init_wait_queue(&rdwq); + init_wait_queue_head(&rdwq); } int cnsl_kbd_write(char ch) { @@ -85,7 +85,7 @@ int cnsl_kbd_write(char ch) { if (ch == '\n') { clear(&cnsl.wr_q); - return 0; // TODO FIX + return 0; // TODO FIX while (get(&cnsl.sc_q, &ch)) put(&cnsl.rd_q, ch); wake_up(&rdwq); } diff --git a/drivers/ide.c b/drivers/ide.c index b970f70..2c51213 100644 --- a/drivers/ide.c +++ b/drivers/ide.c @@ -10,14 +10,14 @@ // #include #include // #include -// #include +#include // #include // #include // #include // #include // #include -// #include +#include // typedef struct prd { // unsigned int addr; @@ -533,9 +533,40 @@ void ide_irq() { ide_intr_func(); } // } void ata_read_identify(int dev); + +DECLARE_WAIT_QUEUE_HEAD(ide_wait_queue_head); + +void sleep_on_ide() { sleep_on(&ide_wait_queue_head); } + +extern void *mbr_buf; +uint8_t ata_pci_bus_status(); +extern ide_pci_controller_t ide_pci_controller; +void ide_irq_handler(unsigned int irq, pt_regs_t *regs, void *devid) { + printk("ide irq handler %d \n", irq); + + printk("ide pci status after interrupt: %x\n", ata_pci_bus_status()); + +#if 0 + unsigned int v = pci_read_config_word(pci_cmd(ide_pci_controller.pci, PCI_COMMAND)); + pci_write_config_word(v & (~PCI_COMMAND_MASTER), pci_cmd(ide_pci_controller.pci, PCI_COMMAND)); + + uint16_t *p = (uint16_t *)mbr_buf; + for (int i = 0; i < 256; i++) { + if (i % 12 == 0) { + printk("\n%03d ", i); + } + printk("%04x ", p[i]); + } +#endif + + wake_up(&ide_wait_queue_head); +} + void ide_init() { // memset((void *)&drv, 0, sizeof(drv)); + request_irq(0x0E, ide_irq_handler, "hard", "IDE"); + // init_pci_controller(0x0106); init_pci_controller(0x0101); diff --git a/drivers/ide.h b/drivers/ide.h index cf09767..803ff55 100644 --- a/drivers/ide.h +++ b/drivers/ide.h @@ -173,4 +173,6 @@ typedef struct _ide_pci_controller { unsigned int bus_prdt; prdte_t *prdt; -} ide_pci_controller_t; \ No newline at end of file +} ide_pci_controller_t; + +void sleep_on_ide(); \ No newline at end of file diff --git a/drivers/keyboard.c b/drivers/keyboard.c index a7c4325..4f82446 100644 --- a/drivers/keyboard.c +++ b/drivers/keyboard.c @@ -102,8 +102,10 @@ void kbd_debug(unsigned char scan_code) { // if (scan_code == 0x43) // F9 // ide_dma_pci_lba48(); if (scan_code == 0x44) { // F10 - void ata_test(uint64_t nr); - ata_test(0); + // void ata_test(uint64_t nr); + // ata_test(0); + void ata_send_read_identify_cmd(int dev); + ata_send_read_identify_cmd(0); } if (scan_code == 0x57) // F11 diff --git a/include/wait.h b/include/wait.h index 9e0430f..2f79426 100644 --- a/include/wait.h +++ b/include/wait.h @@ -31,6 +31,9 @@ typedef struct { #define DECLARE_WAIT_QUEUE(name, tsk) wait_queue_t name = WAIT_QUEUE_INITIALIZER(name, tsk) -void init_wait_queue(wait_queue_head_t *wqh); +void init_wait_queue_head(wait_queue_head_t *wqh); void add_wait_queue(wait_queue_head_t *wqh, wait_queue_t *wq); void del_wait_queue(wait_queue_head_t *wqh, wait_queue_t *wq); + +void sleep_on(wait_queue_head_t *head); +void wake_up(wait_queue_head_t *head); diff --git a/kernel/irq.c b/kernel/irq.c index 54beee6..b9ed8b8 100644 --- a/kernel/irq.c +++ b/kernel/irq.c @@ -66,18 +66,26 @@ int request_irq(unsigned int irq, void (*handler)(unsigned int, pt_regs_t *, voi void *dev_id) { irq_action_t *p; - if (irq >= NR_IRQS) return -EINVAL; - if (handler == NULL) return -EINVAL; + if (irq >= NR_IRQS) { + return -EINVAL; + } + if (handler == NULL) { + return -EINVAL; + } // 检查是否已经注册过处理函数了 p = irq_desc[irq].action; while (p != NULL) { - if (p->handler == handler) return 0; + if (p->handler == handler) { + return 0; + } p = p->next; } p = (irq_action_t *)kmalloc(sizeof(irq_action_t), 0); - if (p == NULL) return -ENOMEM; + if (p == NULL) { + return -ENOMEM; + } p->dev_name = devname; p->dev_id = dev_id; diff --git a/kernel/system.c b/kernel/system.c index 0b90a7e..f5efd71 100644 --- a/kernel/system.c +++ b/kernel/system.c @@ -90,25 +90,6 @@ void setup_gate() { } void ide_irq(); -extern void *mbr_buf; -uint8_t ata_pci_bus_status(); -extern ide_pci_controller_t ide_pci_controller; -void default_ide_irq_handler(unsigned int irq, pt_regs_t *regs, void *dev_id) { - printk("default irq handler %d \n", irq); - - printk("ide pci status after interrupt: %x", ata_pci_bus_status()); - - unsigned int v = pci_read_config_word(pci_cmd(ide_pci_controller.pci, PCI_COMMAND)); - pci_write_config_word(v & (~PCI_COMMAND_MASTER), pci_cmd(ide_pci_controller.pci, PCI_COMMAND)); - - uint16_t *p = (uint16_t *)mbr_buf; - for (int i = 0; i < 256; i++) { - if (i % 12 == 0) { - printk("\n%03d ", i); - } - printk("%04x ", p[i]); - } -} void default_irq_handler(unsigned int irq, pt_regs_t *regs, void *dev_id) { printk("default irq handler %d \n", irq); } @@ -129,8 +110,7 @@ void setup_irqs() { request_irq(0x00, clk_handler, "Intel 8254", "Clock Chip"); request_irq(0x01, kbd_handler, "Intel 8042", "PS/2 Keyboard"); - request_irq(0x0A, default_ide_irq_handler, "hard", "IDE"); - request_irq(0x0E, default_ide_irq_handler, "hard", "IDE"); + // request_irq(0x0E, default_ide_irq_handler, "hard", "IDE"); for (int i = 0; i < 16; i++) { if (i != 0 && i != 1 && i != 10 && i != 14) { request_irq(i, default_irq_handler, "default", "default"); @@ -149,7 +129,6 @@ void setup_irqs() { // 打开支持的中断 open_irq(0x00); open_irq(0x01); - open_irq(0x0A); open_irq(0x0E); } void set_tss() { diff --git a/kernel/wait.c b/kernel/wait.c index e8cb71d..bcee2bb 100644 --- a/kernel/wait.c +++ b/kernel/wait.c @@ -12,32 +12,58 @@ #include #include -void init_wait_queue(wait_queue_head_t *wqh) { INIT_LIST_HEAD(&wqh->task_list); } +void init_wait_queue_head(wait_queue_head_t *wqh) { INIT_LIST_HEAD(&wqh->task_list); } -void add_wait_queue(wait_queue_head_t *wqh, wait_queue_t *wq) { +void add_wait_queue(wait_queue_head_t *head, wait_queue_t *wq) { unsigned long flags; irq_save(flags); - list_add_tail(&wq->task_list, &wqh->task_list); + list_add_tail(&wq->task_list, &head->task_list); irq_restore(flags); } -void del_wait_queue(wait_queue_head_t *wqh, wait_queue_t *wq) { +void del_wait_queue(wait_queue_head_t *head, wait_queue_t *wq) { unsigned long flags; irq_save(flags); list_del(&wq->task_list); irq_restore(flags); } -void wake_up(wait_queue_head_t *wqh) { +void sleep_on(wait_queue_head_t *head) { + DECLARE_WAIT_QUEUE(wait, current); + + current->state = TASK_WAIT; + + unsigned long flags; + irq_save(flags); + + list_add_tail(&wait.task_list, &head->task_list); + + irq_restore(flags); + + schedule(); +} + +void __wake_up(wait_queue_head_t *head, int nr) { unsigned long flags; wait_queue_t *p, *tmp; irq_save(flags); - list_for_each_entry_safe(p, tmp, &wqh->task_list, task_list) { p->task->state = TASK_RUNNING; } + list_for_each_entry_safe(p, tmp, &head->task_list, task_list) { + list_del(&p->task_list); + printk("wakeup: %s\n", p->task->name); + p->task->state = TASK_RUNNING; + + --nr; + if (nr == 0) { + break; + } + } irq_restore(flags); // no schedule() here. } +void wake_up(wait_queue_head_t *head) { __wake_up(head, 1); } + #include DECLARE_WAIT_QUEUE_HEAD(debug_wq); unsigned int debug_global_var = 0;