feat: B7 APIC/IOAPIC接管 + NVMe写路径 + virtio_net virtqueue + PE兼容层修复 + QEMU测试套件全自动化

中断路由 (B7):
- apic.drv: LAPIC enable + IOAPIC重定向表编程(MADT ISO感知) + apic_route/apic_route_legacy/apic_diag开关
- UTSM: LAPIC EOI钩子(register_apic_eoi, ABI尾部追加) + IDT 0-255全覆盖 + spurious 0xFF特判
- FUCK: apic_route=0默认PIC(零回归) / =1 IOAPIC->LAPIC接管(失败完整降级) / legacy放开IRQ1/12/11

存储:
- nvme.drv: 写路径 + PRP list + 读回校验 + MSI/MSI-X中断驱动(nvme_msi开关, 回退纯轮询)
- build.ps1: apic.drv纳入构建(原为预置二进制)

网络:
- virtio_net: virtqueue RX/TX + netdev注册 + DHCP自测(slirp真实OFFER)

PE兼容层:
- pe_shim: guest->host指针转换(g_emu_mem_base + gp()) + shim fn32列补齐
- gen_pe.py: PE32+ RVA偏置修复(BUG-004) + cmd.exe生成
- desktop_app.h: da_mouse_poll先判AUX位 + da_mouse_init冲刷残留(BUG-003/008)
- shell: Ctrl修饰键跟踪(BUG-001) + g_run_image扩至1MB(BUG-002)

QEMU测试套件:
- 9用例全自动化(Run-All.ps1): boot-regression/driver-matrix/linux-compat/pe-compat/deaicup-e2e/userapps-matrix/network-e2e/performance-baseline/x86emu32-coverage
- QemuTest.ps1公共库: 串口断言/monitor sendkey/screendump/HTTP服务器/并发隔离

文档:
- CLAUDE.md: APIC/IOAPIC能力 + kernel_api暴露 + IRQ后端升级状态
- LESSONS_LEARNED.md: B7 LAPIC/IOAPIC经验教训(2条)
- Bug记录.txt: BUG-20260801-001~008 + BUG-20260803-009(9条)
- test-cases/*.md: 全部补充自动化脚本小节
- 桌面设计.md: .lnk快捷方式 + LINUXAPP.CNF Linux应用图标
- 系统开发策划.md: NVMe/virtio_net标完成 + B2/B7标已解决 + M2进度列
This commit is contained in:
xyou
2026-08-04 18:08:08 +08:00
parent f52157b7af
commit a26866e339
70 changed files with 4652 additions and 504 deletions
Binary file not shown.
Binary file not shown.
+140 -26
View File
@@ -128,7 +128,11 @@ def build_pe32_plus():
code_rva = 0x1000 # .text RVA
main_rva = code_rva
printf_iat_rva = code_rva + 0x200 # IAT 在 .text 后(同一 section 内,简化)
exitproc_iat_rva = printf_iat_rva + 8
# 注意:每个 import descriptor 的 thunk 数组必须 NULL 终止。
# printf thunk 占 0x200..0x207,0x208..0x20F 为 NULL 终止符,
# ExitProcess thunk 放 0x210(此前放 0x208 导致 desc0 的 thunk 数组
# 无 NULL 终止、加载器把 ExitProcess 误配到 msvcrt.dll)。
exitproc_iat_rva = printf_iat_rva + 16
# 修正 lea rcx, [rip + msg]: msg 在 code[19] 之后(lea 7字节 + call 6 + xor 2 + call 6 + ret 1 = 22)
msg_offset_in_code = 22
@@ -140,30 +144,41 @@ def build_pe32_plus():
struct.pack_into('<i', code, 3, 15)
# 修正 call [rip + printf_iat]: printf IAT 在 printf_iat_rva, call 后下一指令 RIP = main_rva + 13
# disp32 = printf_iat_rva - (main_rva + 13) = 0x200 - 13 = 499
struct.pack_into('<i', code, 10, 0x200 - 13)
# 注意:call 指令在 code[7..12](FF 15 + disp32),disp32 位于偏移 9;
# 此前误写偏移 10 → disp 变为 0x1F300 且覆盖 xor ecx,ecx 的 0x31 → 三重故障。
struct.pack_into('<i', code, 9, (printf_iat_rva - code_rva) - 13)
# 修正 call [rip + exitproc]: ExitProcess IAT 在 exitproc_iat_rva, call 后下一指令 RIP = main_rva + 21
# disp32 = exitproc_iat_rva - (main_rva + 21) = 0x208 - 21 = 491
struct.pack_into('<i', code, 17, 0x208 - 21)
# disp32 = exitproc_iat_rva - (main_rva + 21)
struct.pack_into('<i', code, 17, (exitproc_iat_rva - code_rva) - 21)
# IAT 8字节对齐填充
while len(code) < 0x200:
code += b'\x00'
# IAT 内容(指向 import 函数名 RVA)
# 注意:以下 RVA 都必须带 code_rva(0x1000) 偏置——此前写成裸缓冲区偏移
# (0x300/0x320/0x350/0x360/0x400),导致 import 数据目录指向 headers 区、
# rva_to_offset 解析失败、IAT 永远不被 shim 修补,call [IAT] 跳到 0x300
# → #PF → DSK 阶段异常无法投递 → 三重故障(QEMU -d int 实测定案)。
printf_name_rva = code_rva + 0x300 # 0x1300
exitproc_name_rva = code_rva + 0x320 # 0x1320
msvcrt_dll_rva = code_rva + 0x350 # 0x1350
kernel32_dll_rva = code_rva + 0x360 # 0x1360
import_desc_rva = code_rva + 0x400 # 0x1400
# IAT 内容(指向 import 函数名 RVA),每个 descriptor 的 thunk 数组 NULL 终止
iat_data = bytearray()
iat_data += struct.pack('<Q', 0x300) # printf function name RVA (hint+name 表)
iat_data += struct.pack('<Q', 0x320) # ExitProcess function name RVA
iat_data += struct.pack('<Q', printf_name_rva) # printf function name RVA (hint+name 表)
iat_data += struct.pack('<Q', 0) # desc0 thunk 数组 NULL 终止
iat_data += struct.pack('<Q', exitproc_name_rva) # ExitProcess function name RVA
iat_data += struct.pack('<Q', 0) # desc1 thunk 数组 NULL 终止
code += iat_data
# Import descriptor 数据
import_desc_rva = 0x400 # 在 IAT 后
# Import descriptor 数据(在 IAT 后)
while len(code) < 0x300:
code += b'\x00'
# Hint/Name 表
# "msvcrt.dll\0" + printf name "printf\0"
# "kernel32.dll\0" + ExitProcess name "ExitProcess\0"
printf_name_rva = 0x300
exitproc_name_rva = 0x320
printf_hint_name = struct.pack('<H', 0) + b'printf\0' # hint=0
exitproc_hint_name = struct.pack('<H', 0) + b'ExitProcess\0'
while len(code) < printf_name_rva - code_rva:
@@ -174,8 +189,6 @@ def build_pe32_plus():
code += exitproc_hint_name
# DLL 名(放在 import descriptor 之后)
msvcrt_dll_rva = 0x350
kernel32_dll_rva = 0x360
while len(code) < msvcrt_dll_rva - code_rva:
code += b'\x00'
code += b'msvcrt.dll\0'
@@ -324,7 +337,11 @@ def build_pe32():
code_rva = 0x1000
main_rva = code_rva
printf_iat_rva = code_rva + 0x200 # 0x1200
exitproc_iat_rva = printf_iat_rva + 4 # 0x1204 (4-byte thunk)
# 注意:每个 import descriptor 的 thunk 数组必须 NULL 终止。
# printf thunk 占 0x1200,0x1204 为 NULL 终止符,ExitProcess thunk 放 0x1208
# (此前放 0x1204 导致 desc0 thunk 数组无 NULL 终止,
# 加载器把 ExitProcess 误配到 msvcrt.dll → "[PE32] unimplemented import")。
exitproc_iat_rva = printf_iat_rva + 8 # 0x1208 (4-byte thunk + NULL 间隔)
printf_name_rva = 0x1300
exitproc_name_rva = 0x1320
msvcrt_dll_rva = 0x1350
@@ -348,9 +365,11 @@ def build_pe32():
# 对齐到 IAT (0x200)
while len(code) < 0x200:
code += b'\x00'
# IAT (4-byte thunks,初始值 = hint/name RVA)
# IAT (4-byte thunks,初始值 = hint/name RVA),每个 descriptor 的 thunk 数组 NULL 终止
code += struct.pack('<I', printf_name_rva)
code += struct.pack('<I', 0) # desc0 thunk 数组 NULL 终止
code += struct.pack('<I', exitproc_name_rva)
code += struct.pack('<I', 0) # desc1 thunk 数组 NULL 终止
# 对齐到 hint/name 表 (0x300)
while len(code) < printf_name_rva - code_rva:
@@ -494,23 +513,31 @@ def build_cmd_exe():
code_rva = 0x1000
main_rva = code_rva
printf_iat_rva = code_rva + 0x200
exitproc_iat_rva = printf_iat_rva + 8
# 同 build_pe32_plus:thunk 数组 NULL 终止,ExitProcess thunk 放 0x210
exitproc_iat_rva = printf_iat_rva + 16
msg_offset_in_code = 22
struct.pack_into('<i', code, 3, 15)
struct.pack_into('<i', code, 10, 0x200 - 13)
struct.pack_into('<i', code, 17, 0x208 - 21)
# call#1 disp32 位于偏移 9(同 build_pe32_plus 的偏移修正说明)
struct.pack_into('<i', code, 9, (printf_iat_rva - code_rva) - 13)
struct.pack_into('<i', code, 17, (exitproc_iat_rva - code_rva) - 21)
# 同 build_pe32_plus 修复:RVA 必须带 code_rva(0x1000) 偏置
printf_name_rva = code_rva + 0x300 # 0x1300
exitproc_name_rva = code_rva + 0x320 # 0x1320
msvcrt_dll_rva = code_rva + 0x350 # 0x1350
kernel32_dll_rva = code_rva + 0x360 # 0x1360
import_desc_rva = code_rva + 0x400 # 0x1400
while len(code) < 0x200:
code += b'\x00'
code += struct.pack('<Q', 0x300)
code += struct.pack('<Q', 0x320)
code += struct.pack('<Q', printf_name_rva)
code += struct.pack('<Q', 0) # desc0 thunk 数组 NULL 终止
code += struct.pack('<Q', exitproc_name_rva)
code += struct.pack('<Q', 0) # desc1 thunk 数组 NULL 终止
import_desc_rva = 0x400
while len(code) < 0x300:
code += b'\x00'
printf_name_rva = 0x300
exitproc_name_rva = 0x320
printf_hint_name = struct.pack('<H', 0) + b'printf\0'
exitproc_hint_name = struct.pack('<H', 0) + b'ExitProcess\0'
while len(code) < printf_name_rva - code_rva:
@@ -520,8 +547,6 @@ def build_cmd_exe():
code += b'\x00'
code += exitproc_hint_name
msvcrt_dll_rva = 0x350
kernel32_dll_rva = 0x360
while len(code) < msvcrt_dll_rva - code_rva:
code += b'\x00'
code += b'msvcrt.dll\0'
@@ -602,6 +627,89 @@ def build_cmd_exe():
return bytes(headers) + bytes(code)
def build_fault64():
"""构造 fault64.exe — PE32+ 故意 #PF 验收样本(BUG-20260801-005)。
用途: 验证 DSK 诊断 IDT 的异常投递。机器码:
movabs rax, 0x400000000000 ; 48 B8 <8B>
mov dword [rax], 1 ; C7 00 01 00 00 00
jmp $ ; EB FE (兜底:万一未 fault 原地死循环)
0x400000000000 是低位规范地址(bit47=0)且远超任何已映射区,
写入必然 #PF(err=0x2, supervisor write non-present, cr2=0x400000000000)。
预期串口: "[IDT] exception" + vector=0xe + cr2=0x0000400000000000。
无导入表(pe_loader 对空 import directory 直接跳过)。
"""
image_base = 0x140000000
section_align = 0x1000
file_align = 0x200
dos_header = bytearray(64)
struct.pack_into('<H', dos_header, 0, IMAGE_DOS_SIGNATURE)
struct.pack_into('<I', dos_header, 60, 64)
pe_sig = struct.pack('<I', IMAGE_NT_SIGNATURE)
coff_header = struct.pack('<HHIIIHH',
IMAGE_FILE_MACHINE_AMD64,
1, 0, 0, 0, 240,
IMAGE_FILE_MACHINE_AMD64_CHARACTERISTICS)
code = bytearray()
code += b'\x48\xB8' + struct.pack('<Q', 0x400000000000) # movabs rax, 0x400000000000
code += b'\xC7\x00' + struct.pack('<I', 1) # mov dword [rax], 1
code += b'\xEB\xFE' # jmp $
while len(code) % file_align != 0:
code += b'\x00'
code_rva = 0x1000
main_rva = code_rva
size_of_code = len(code)
size_of_image = code_rva + size_of_code
while size_of_image % section_align != 0:
size_of_image += 1
opt_header = bytearray(240)
struct.pack_into('<H', opt_header, 0, 0x20b) # Magic = PE32+
struct.pack_into('<I', opt_header, 4, size_of_code) # SizeOfCode
struct.pack_into('<I', opt_header, 16, main_rva) # AddressOfEntryPoint
struct.pack_into('<I', opt_header, 20, code_rva) # BaseOfCode
struct.pack_into('<Q', opt_header, 24, image_base) # ImageBase
struct.pack_into('<I', opt_header, 32, section_align)
struct.pack_into('<I', opt_header, 36, file_align)
struct.pack_into('<H', opt_header, 40, 6) # MajorOperatingSystemVersion
struct.pack_into('<H', opt_header, 48, 6) # MajorSubsystemVersion
struct.pack_into('<I', opt_header, 56, size_of_image)
struct.pack_into('<I', opt_header, 60, 0x200) # SizeOfHeaders
struct.pack_into('<H', opt_header, 68, IMAGE_SUBSYSTEM_WINDOWS_CUI)
struct.pack_into('<Q', opt_header, 72, 0x100000) # SizeOfStackReserve
struct.pack_into('<Q', opt_header, 80, 0x1000) # SizeOfStackCommit
struct.pack_into('<Q', opt_header, 88, 0x100000) # SizeOfHeapReserve
struct.pack_into('<Q', opt_header, 96, 0x1000) # SizeOfHeapCommit
struct.pack_into('<I', opt_header, 108, 16) # NumberOfRvaAndSizes
# DataDirectory 全 0(无导入表)
section_header = bytearray(40)
section_header[0:8] = b'.text\0\0\0'
struct.pack_into('<I', section_header, 8, size_of_code)
struct.pack_into('<I', section_header, 12, code_rva)
struct.pack_into('<I', section_header, 16, size_of_code)
struct.pack_into('<I', section_header, 20, file_align)
struct.pack_into('<I', section_header, 36,
IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ)
headers = bytearray()
headers += dos_header
headers += pe_sig
headers += coff_header
headers += opt_header
headers += section_header
while len(headers) % file_align != 0:
headers += b'\x00'
return bytes(headers) + bytes(code)
def main():
out_dir = os.path.dirname(os.path.abspath(__file__))
@@ -623,7 +731,13 @@ def main():
f.write(cmd_exe)
print(f"[gen] Generated cmd.exe: {out_path} ({len(cmd_exe)} bytes)")
print("[gen] 完成。复制到 SYSTEM/system/deshab64/tools/ 并重新运行 build.bat")
fault64 = build_fault64()
out_path = os.path.join(out_dir, 'fault64.exe')
with open(out_path, 'wb') as f:
f.write(fault64)
print(f"[gen] Generated fault64.exe: {out_path} ({len(fault64)} bytes)")
print("[gen] 完成。复制到 SYSTEM/bin/ 并重新运行 build.bat")
if __name__ == '__main__':
Binary file not shown.
Binary file not shown.