fix(vkernel/ide): PIO 命令块寄存器连写补 io_wait + 读完核 ERR - 修读盘读错扇区(根目录扇区读回 ELF 头致 app spawn 失败)

This commit is contained in:
TermiNexus committed 2026-10-15 17:14:12 +08:00
1 parent 2eea065a67
commit 0ee5ec5dcc
1 file changed
+22
+22
View File
@@ -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)