fix(asm): io_wait 改为连续 4 次读备选状态寄存器 0x3F6,提供真实 400ns I/O 延时

原先写的是诊断端口 0x80:该端口背后没有设备,在 QEMU 上几乎不消耗时间,等于没有延时。于是 IDE 命令块寄存器 0x1F2..0x1F7 连写会丢写,LBA 寄存器残留上一次的值,导致读到别的扇区(现场:读根目录扇区 8192 却读回 ELF 文件头,目录项全是垃圾,五个 app 全部 spawn 失败;另有 waitDataReady 超时表现为 diskio.read 返回 FR_DISK_ERR)。0ee5ec5 曾为同一现场补过 io_wait 调用,但当时见效靠的是调用开销与代码节奏的余量;内核变大后被吃掉,故障必现。改为 4 次 in al, 0x3F6(真实设备端口,每次均为真实 I/O 往返),并补 CLOBBER_RAX(in al 会改写 RAX,原实现未声明该 clobber)。
This commit is contained in:
TermiNexus committed 2026-10-16 19:56:26 +08:00
1 parent 6a22633b6b
commit 172af1e172
1 file changed
+12 -2
+12 -2
View File
@@ -52,8 +52,18 @@ def outl(port: uint16, value: uint32):
op = [ASM_DESCR.CLOBBER_RDX, ASM_DESCR.CLOBBER_RAX])
def io_wait():
__asm__(f"mov dx, 0x80; out dx, al",
op = [ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_DX])
# ATA 规范的 I/O 延时:**连续 4 次读备选状态寄存器 0x3F6**。它是真实设备端口,每次 in
# 都是一次真实 I/O 往返,稳定给出 ≥400ns(规范要求命令块寄存器两次写之间至少 400ns)。
# 原先写的是诊断端口 0x80 —— 该端口背后没有设备,在 QEMU 上几乎不消耗时间,等于**没有
# 延时**:IDE 命令块寄存器(0x1F2..0x1F7)连写会丢写 ⇒ LBA 寄存器残留上一次的值 ⇒ 读到
# **别的扇区**的数据。现场(参见 0ee5ec5 与 serial.log):读根目录扇区 8192 却读回 ELF
# 文件头 ⇒ 目录项全是垃圾 ⇒ app spawn 失败;或 waitDataReady 超时 ⇒ diskio.read 报错。
__asm__(f"""mov dx, 0x3F6
in al, dx
in al, dx
in al, dx
in al, dx""",
op = [ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_DX, ASM_DESCR.CLOBBER_RAX])
inline def pushall():