From 172af1e172d5a750073bb4eb94132da3efea79bf Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Fri, 16 Oct 2026 19:56:26 +0800 Subject: [PATCH] =?UTF-8?q?fix(asm):=20io=5Fwait=20=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E8=BF=9E=E7=BB=AD=204=20=E6=AC=A1=E8=AF=BB=E5=A4=87=E9=80=89?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=AF=84=E5=AD=98=E5=99=A8=200x3F6=EF=BC=8C?= =?UTF-8?q?=E6=8F=90=E4=BE=9B=E7=9C=9F=E5=AE=9E=20400ns=20I/O=20=E5=BB=B6?= =?UTF-8?q?=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原先写的是诊断端口 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)。 --- asm.vp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/asm.vp b/asm.vp index dd47a24..798fcb7 100644 --- a/asm.vp +++ b/asm.vp @@ -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():