diff --git a/VKernel/Kernel/drivers/storage/ide/ide.vp b/VKernel/Kernel/drivers/storage/ide/ide.vp index 613e0cc..dea9a1a 100644 --- a/VKernel/Kernel/drivers/storage/ide/ide.vp +++ b/VKernel/Kernel/drivers/storage/ide/ide.vp @@ -70,17 +70,30 @@ def readSector(lba: uint32, buffer: rawptr) -> int: if err != IDError.IDE_ERR_NONE: return err asm.outb(0x3F6, 0x00) + asm.io_wait() + # 命令块寄存器**连写之间必须插 I/O 延时**:QEMU 的 IDE 是逐条采样寄存器的,缺延时 + # 会出现"只写入部分寄存器"⇒ LBA 保留上一次的值 ⇒ 读到**另一个扇区**的数据(现场: + # 读根目录扇区 LBA 8192 却读回 ELF 文件头,导致目录项全是垃圾 → spawn 失败)。 asm.outb(0x1F2, 1) + asm.io_wait() asm.outb(0x1F3, uint8(lba & 0xFF)) + asm.io_wait() asm.outb(0x1F4, uint8((lba >> 8) & 0xFF)) + asm.io_wait() asm.outb(0x1F5, uint8((lba >> 16) & 0xFF)) + asm.io_wait() asm.outb(0x1F6, uint8(0xE0 | ((lba >> 24) & 0x0F))) + asm.io_wait() asm.outb(0x1F7, 0x20) + asm.io_wait() err = waitDataReady(IDE_baseport_PRIMARY) if err != IDError.IDE_ERR_NONE: st: uint8 = asm.inb(0x1F7) return err st2: uint8 = asm.inb(0x1F7) + # 读完再核一次 ERR:置位说明这次数据无效,不能当成功返回(否则上层会拿到错内容) + if st2 & IDE_STATUS_ERR: + return IDError.IDE_ERR_DEVICE_ERROR buf: uint16* = uint16*(buffer) for j in range(256): *buf = asm.inw(0x1F0) @@ -96,12 +109,21 @@ def writeSector(lba: uint32, buffer: const rawptr) -> int: err: IDError = waitReady(IDE_baseport_PRIMARY) if err != IDError.IDE_ERR_NONE: return err asm.outb(0x3F6, 0x00) + asm.io_wait() + # 与 readSector 同理:命令块寄存器连写之间必须插 I/O 延时,否则 QEMU 只采样到部分寄存器 + # ⇒ LBA 残留旧值 ⇒ 把数据写到**错误的扇区**(写盘路径同样致命)。 asm.outb(0x1F2, 1) + asm.io_wait() asm.outb(0x1F3, uint8(lba & 0xFF)) + asm.io_wait() asm.outb(0x1F4, uint8((lba >> 8) & 0xFF)) + asm.io_wait() asm.outb(0x1F5, uint8((lba >> 16) & 0xFF)) + asm.io_wait() asm.outb(0x1F6, uint8(0xE0 | ((lba >> 24) & 0x0F))) + asm.io_wait() asm.outb(0x1F7, 0x30) + asm.io_wait() err = waitDataReady(IDE_baseport_PRIMARY) if err != IDError.IDE_ERR_NONE: return err buf: uint16* = uint16*(buffer)