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:
+165
-13
@@ -405,10 +405,21 @@ static void pic_remap(void) {
|
||||
outb(PIC2_DATA, 0xFF); /* mask all slave */
|
||||
}
|
||||
|
||||
/* 运行时读取当前内核 CS — 必须与实际生效的 GDT 一致。
|
||||
* 注意:UTSM 运行在 Limine 提供的 GDT 下(CS=0x28),不能假设
|
||||
* OSDev 教程布局(0x08=code)。若 IDT gate selector 指向无效/非64位
|
||||
* 代码段,异常派发时加载 CS 会 #GP(selector error),异常链升级为
|
||||
* #DF -> triple fault,且全程无任何日志输出。 */
|
||||
static u16 idt_kernel_cs(void) {
|
||||
u16 cs;
|
||||
__asm__ volatile("mov %%cs, %0" : "=r"(cs));
|
||||
return cs;
|
||||
}
|
||||
|
||||
static void idt_set_entry(u8 vector, void *handler, u8 ist, u8 type) {
|
||||
u64 addr = (u64)handler;
|
||||
g_idt[vector].offset_low = (u16)(addr & 0xffff);
|
||||
g_idt[vector].selector = 0x08; /* kernel code segment (from GDT) */
|
||||
g_idt[vector].selector = idt_kernel_cs();
|
||||
g_idt[vector].ist = ist;
|
||||
g_idt[vector].type_attr = type;
|
||||
g_idt[vector].offset_mid = (u16)((addr >> 16) & 0xffff);
|
||||
@@ -459,15 +470,122 @@ void idt_halt_all(void) {
|
||||
__asm__ volatile("lidt %0" :: "m"(idtr));
|
||||
}
|
||||
|
||||
void idt_install_dsk_diag(void) {
|
||||
/* BUG-20260801-005: DSK 交接诊断 IDT(替代原 idt_halt_all 静默化)。
|
||||
* 原 halt_all 将 256 项全部换成静默 halt stub——防御正确,但 DSK
|
||||
* 阶段任何 CPU 异常(PF/GP/UD)被无声吞掉,零日志无法诊断
|
||||
* (实测 pe HELLO64.EXE 早期 #PF 直接表现为"卡死/三重故障")。
|
||||
* 本函数在保留 IRQ 防御的前提下恢复异常可见性:
|
||||
* vector 0-31 -> 原 isr_stub(走 idt_handler 异常路径,打完整
|
||||
* vector/err/rip/CR2/寄存器日志后安全停机)
|
||||
* vector 32-255 -> 保持 halt stub(PIC 已全 mask 且 IF=0,IRQ 本
|
||||
* 不该到达;spurious IRQ 维持原防御行为)
|
||||
* 不做 pic_remap / sti——DSK 上下文 IF=0 不变式保持不变。
|
||||
* isr_stub / idt_handler 是 UTSM .text 代码,SAS-R0 下常驻内存
|
||||
* 有效(BUG-20260729-013 登记行为:halt_all 引入前 DSK 阶段异常
|
||||
* 本就能送达 idt_handler);异常路径只用 log/serial,不依赖已过期
|
||||
* 的 IRQ handler 状态。 */
|
||||
for (u16 i = 0; i < 32; i++) {
|
||||
idt_set_entry((u8)i, g_isr_table[i], 0, 0x8E);
|
||||
}
|
||||
for (u16 i = 32; i < IDT_ENTRIES; i++) {
|
||||
idt_set_entry((u8)i, (void *)isr_halt_stub, 0, 0x8E);
|
||||
}
|
||||
idt_ptr idtr;
|
||||
idtr.limit = (u16)(sizeof(g_idt) - 1);
|
||||
idtr.base = (u64)&g_idt;
|
||||
__asm__ volatile("lidt %0" :: "m"(idtr));
|
||||
log_info("[IDT] DSK diag IDT installed (exc 0-31 logged, IRQ halted)");
|
||||
}
|
||||
|
||||
void idt_install_pe_diag(void) {
|
||||
/* 语义与 idt_install_dsk_diag 相同:BUG-20260801-005 起 DSK 交接
|
||||
* 默认装备诊断 IDT,PE 原生执行前重装一次保持幂等(防御 PE 运行
|
||||
* 期间 IDT 被第三方改写的场景)。 */
|
||||
idt_install_dsk_diag();
|
||||
}
|
||||
|
||||
/* IRQ handler table — 扩展到 256 个 vector (真机 APIC/IOAPIC/MSI 需要) */
|
||||
static irq_handler_t g_irq_handlers[256];
|
||||
|
||||
/* B7 阶段2: LAPIC EOI 钩子 (由 apic.drv 经 kernel_api.register_apic_eoi 注册)。
|
||||
* 指向写 LAPIC EOI 寄存器 (0xFEE000B0) 的函数; NULL = PIC 路由模式, 不调用。
|
||||
* 单核 BSP 场景: 注册发生在 apic.drv init (普通上下文), 调用发生在 IRQ
|
||||
* 上下文; 函数指针赋值对齐原子, 无需锁。 */
|
||||
static void (*g_apic_eoi_hook)(void) = 0;
|
||||
|
||||
void idt_register_apic_eoi(void (*eoi_fn)(void)) {
|
||||
g_apic_eoi_hook = eoi_fn;
|
||||
if (eoi_fn) {
|
||||
log_info("[IDT] APIC EOI hook registered (LAPIC EOI on IRQ dispatch)");
|
||||
} else {
|
||||
log_info("[IDT] APIC EOI hook cleared");
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 插桩: IRQ 统计计数器 ---- */
|
||||
INSTR_STAT_DECL(pic_irq_total);
|
||||
INSTR_STAT_DECL(apic_irq_total);
|
||||
|
||||
/* ---- B7 阶段3: 动态 IDT 向量分配器 (MSI/MSI-X 用) ----
|
||||
* 分配范围 0x40-0xDF (160 个向量):
|
||||
* 0x00-0x1F CPU 异常
|
||||
* 0x20-0x2F PIC legacy IRQ (保留不动)
|
||||
* 0x30-0x3F 保留给 IOAPIC GSI 16-31 未来扩展 (本阶段不动态分配)
|
||||
* 0x40-0xDF 动态分配池 (MSI/MSI-X)
|
||||
* 0xE0 APIC tick 自测预留 (阶段1 惯例)
|
||||
* 0xFF LAPIC spurious vector
|
||||
* 数据结构: 3 x u64 位图 (192 bit, 用后 160), __builtin_ctzll 找首个
|
||||
* 零位, 分配/释放均 O(1)。热路径无锁: SAS-R0 单核 BSP, alloc/free 只在
|
||||
* driver_init 主上下文调用 (MSI 编程期间对应设备尚未解除中断屏蔽,
|
||||
* IRQ 上下文不可能并发分配), 位图字为对齐 u64 单写原子。
|
||||
* 动态入口无需新增 stub: idt_stubs.S 已为 0-255 全部向量预生成入口,
|
||||
* 分配后 irq_register(vector, handler) 直接挂上既有分发表。 */
|
||||
#define IRQ_VEC_POOL_BASE 0x40
|
||||
#define IRQ_VEC_POOL_COUNT 160
|
||||
static u64 g_vec_bitmap[3];
|
||||
|
||||
int irq_vector_alloc(void) {
|
||||
for (u32 w = 0; w < 3; w++) {
|
||||
u64 free_bits = ~g_vec_bitmap[w];
|
||||
if (!free_bits) continue;
|
||||
u32 bit = (u32)__builtin_ctzll(free_bits);
|
||||
u32 idx = w * 64u + bit;
|
||||
if (idx >= IRQ_VEC_POOL_COUNT) break;
|
||||
g_vec_bitmap[w] |= (1ULL << bit);
|
||||
int vec = IRQ_VEC_POOL_BASE + (int)idx;
|
||||
log_info("[IDT] vector alloc");
|
||||
log_hex64("[IDT] vector=", (u64)vec);
|
||||
return vec;
|
||||
}
|
||||
log_warn("[IDT] vector alloc failed: pool exhausted");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void irq_vector_free(int vector) {
|
||||
if (vector < IRQ_VEC_POOL_BASE ||
|
||||
vector >= IRQ_VEC_POOL_BASE + (int)IRQ_VEC_POOL_COUNT) {
|
||||
return; /* 越界输入幂等忽略 (静态向量不在池内) */
|
||||
}
|
||||
u32 idx = (u32)(vector - IRQ_VEC_POOL_BASE);
|
||||
g_vec_bitmap[idx / 64u] &= ~(1ULL << (idx % 64u));
|
||||
log_info("[IDT] vector free");
|
||||
log_hex64("[IDT] vector=", (u64)vector);
|
||||
}
|
||||
|
||||
int irq_register(u8 vector, irq_handler_t handler) {
|
||||
if ((unsigned)vector >= 256) return -1;
|
||||
/* IRQ号/向量兼容 shim: 0-15 视为 ISA IRQ 号, 换算 vector=0x20+IRQ。
|
||||
* 依据: vector 0-15 是 CPU 异常向量 (#DE/#DB/...), idt_handler 的 IRQ
|
||||
* 分发只覆盖 32-255, 故 0-15 作为注册目标永不可达、无歧义。
|
||||
* 现状调用方两命名空间并存: ps2kbd(1)/e1000(11)/mouseInit(12) 传 IRQ 号,
|
||||
* apic(0xE0)/virtio_net(0x20+n) 传向量 — 统一在此归一到向量空间。 */
|
||||
if (vector < 16) {
|
||||
u8 v = (u8)(0x20u + vector);
|
||||
log_info("[IDT] irq_register: ISA IRQ number translated to vector");
|
||||
log_hex64("[IDT] irq=", vector);
|
||||
log_hex64("[IDT] vector=", v);
|
||||
vector = v;
|
||||
}
|
||||
g_irq_handlers[vector] = handler;
|
||||
INSTR_LOG(INSTR_LOG_DEBUG, INSTR_F_UTSM_TRACE, "[IDT] IRQ handler registered");
|
||||
return 0;
|
||||
@@ -483,25 +601,55 @@ typedef struct {
|
||||
|
||||
void idt_handler(isr_frame *frame) {
|
||||
if (frame->vector < 32) {
|
||||
/* CR2: #PF 的 fault 线性地址;CR3: 当前页表基址。
|
||||
* 寄存器 dump 用于定位 fault 指令的操作数来源。 */
|
||||
u64 cr2, cr3;
|
||||
__asm__ volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3));
|
||||
INSTR_PROBE(EXCP, frame->vector, frame->error_code, frame->rip, 0);
|
||||
log_error("[IDT] exception");
|
||||
log_hex64("[IDT] vector=", frame->vector);
|
||||
log_hex64("[IDT] err=", frame->error_code);
|
||||
log_hex64("[IDT] rip=", frame->rip);
|
||||
log_hex64("[IDT] cr2=", cr2);
|
||||
log_hex64("[IDT] cr3=", cr3);
|
||||
log_hex64("[IDT] rax=", frame->rax);
|
||||
log_hex64("[IDT] rcx=", frame->rcx);
|
||||
log_hex64("[IDT] rdx=", frame->rdx);
|
||||
log_hex64("[IDT] rsi=", frame->rsi);
|
||||
log_hex64("[IDT] rdi=", frame->rdi);
|
||||
log_hex64("[IDT] r8=", frame->r8);
|
||||
log_hex64("[IDT] r9=", frame->r9);
|
||||
log_hex64("[IDT] r10=", frame->r10);
|
||||
log_hex64("[IDT] r11=", frame->r11);
|
||||
log_hex64("[IDT] cs=", frame->cs);
|
||||
log_hex64("[IDT] rflags=", frame->rflags);
|
||||
for (;;) { __asm__ volatile ("cli; hlt"); }
|
||||
}
|
||||
|
||||
/* PIC IRQ: vectors 0x20-0x2F (32-47) */
|
||||
/* PIC IRQ: vectors 0x20-0x2F (32-47)
|
||||
* B7 阶段2: 本分支同时服务两种路由来源:
|
||||
* - 8259 PIC 路由 (默认): handler 后只需 8259 EOI, 钩子为 NULL 跳过;
|
||||
* - IOAPIC->LAPIC 路由 (apic_route=1, legacy 线重定向到 0x20-0x2F):
|
||||
* 中断由 LAPIC 递交, ISR 位置位, 必须写 LAPIC EOI (钩子) 清除,
|
||||
* 否则同级 (vector[7:4] 相同) 中断被永久阻塞 — 每条线只投递一次。
|
||||
* LAPIC EOI 先于 8259 EOI; PIC 全屏蔽时 8259 EOI 为无害 no-op
|
||||
* (8259 无 in-service 位, 非特异性 EOI 不写任何状态)。 */
|
||||
if (frame->vector >= 32 && frame->vector <= 47) {
|
||||
INSTR_STAT_INC(pic_irq_total);
|
||||
u8 irq = (u8)(frame->vector - 0x20);
|
||||
|
||||
/* call registered handler */
|
||||
/* call registered handler; 返回 1 = 自负 EOI 全责 (两者皆抑制) */
|
||||
if (g_irq_handlers[frame->vector]) {
|
||||
int suppress_eoi = g_irq_handlers[frame->vector](irq);
|
||||
if (suppress_eoi) return;
|
||||
}
|
||||
|
||||
/* LAPIC EOI (IOAPIC 路由时必需; PIC 路由时钩子为 NULL) */
|
||||
if (g_apic_eoi_hook) {
|
||||
g_apic_eoi_hook();
|
||||
}
|
||||
|
||||
/* send PIC EOI */
|
||||
if (frame->vector >= 0x28) {
|
||||
outb(PIC2_CMD, 0x20);
|
||||
@@ -511,19 +659,23 @@ void idt_handler(isr_frame *frame) {
|
||||
}
|
||||
|
||||
/* APIC/IOAPIC/MSI IRQ: vectors 0x30-0xFF (48-255)
|
||||
* **QEMU 无法测试但真机必需的代码路径**:
|
||||
* - APIC EOI 写入 LAPIC EOI 寄存器 (0xFEE000B0)
|
||||
* - 当前 PIC 仍激活, 不需要 APIC EOI
|
||||
* - 迁移到 IOAPIC 后需要此处补 APIC EOI */
|
||||
* handler 约定: 返回 0 -> 由钩子补 LAPIC EOI (安全网);
|
||||
* 返回 1 -> handler 已自行 LAPIC EOI, 跳过钩子。
|
||||
* 钩子未注册 (纯 PIC 路由) 时本分支不做任何 EOI — 与旧版行为一致,
|
||||
* 此时 handler 必须自行 LAPIC EOI (参见 apic.drv tick handler)。 */
|
||||
if (frame->vector >= 48 && frame->vector <= 255) {
|
||||
/* LAPIC spurious (SVR 低 8 位 = 0xFF): 规范上不置 ISR 位, 永不需 EOI。
|
||||
* 若在此调钩子写 EOI, 会误清嵌套场景下真正 in-service 的最高位,
|
||||
* 故特判直接返回 (EOI 空 ISR 虽无害, 但语义上 spurious 不该有 EOI)。 */
|
||||
if (frame->vector == 0xFF) return;
|
||||
INSTR_STAT_INC(apic_irq_total);
|
||||
int suppress_eoi = 0;
|
||||
if (g_irq_handlers[frame->vector]) {
|
||||
g_irq_handlers[frame->vector]((u8)frame->vector);
|
||||
suppress_eoi = g_irq_handlers[frame->vector]((u8)frame->vector);
|
||||
}
|
||||
if (!suppress_eoi && g_apic_eoi_hook) {
|
||||
g_apic_eoi_hook();
|
||||
}
|
||||
/* TODO: LAPIC EOI — 写 0 到 LAPIC EOI 寄存器 (mmio offset 0xB0)
|
||||
* 当从 PIC 迁移到 APIC 中断路由时, 必须在此处添加:
|
||||
* *(volatile u32 *)(hhdm_offset + lapic_phys + 0xB0) = 0;
|
||||
*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -56,7 +56,8 @@ static dkm_kernel_api g_kernel_api = {
|
||||
.block = 0,
|
||||
.mmio = 0,
|
||||
.mm_map_mmio = 0,
|
||||
.mm_unmap_mmio = 0
|
||||
.mm_unmap_mmio = 0,
|
||||
.register_apic_eoi = 0
|
||||
};
|
||||
|
||||
const dkm_kernel_api *dkm_get_kernel_api(void) {
|
||||
@@ -94,4 +95,9 @@ void dkm_fill_platform_info(void) {
|
||||
}
|
||||
g_kernel_api.mm_map_mmio = mm_map_mmio;
|
||||
g_kernel_api.mm_unmap_mmio = mm_unmap_mmio;
|
||||
/* B7 阶段2: 暴露 LAPIC EOI 钩子注册入口(apic.drv LAPIC 接管时调用) */
|
||||
g_kernel_api.register_apic_eoi = idt_register_apic_eoi;
|
||||
/* B7 阶段3: 暴露动态向量分配器(MSI/MSI-X 设备驱动使用) */
|
||||
g_kernel_api.irq_vector_alloc = irq_vector_alloc;
|
||||
g_kernel_api.irq_vector_free = irq_vector_free;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -100,6 +100,17 @@ typedef struct dkm_kernel_api {
|
||||
/* —— 新增字段只允许追加在末尾(DSK 以 api+0xA8 硬偏移读取 block) —— */
|
||||
void *(*mm_map_mmio)(u64 phys, u64 size); /* MMIO 独立窗口映射(PCD|PWT),返回虚拟地址,失败返回 0 */
|
||||
void (*mm_unmap_mmio)(void *virt, u64 size); /* 解除 MMIO 独立窗口映射 */
|
||||
/* B7 阶段2: 驱动向 UTSM 注册 LAPIC EOI 钩子(写 LAPIC EOI 寄存器的函数指针)。
|
||||
* apic.drv 在 LAPIC 接管时注册;idt_handler 的 IRQ 分发末尾调用该钩子,
|
||||
* 使 vector 0x20-0x2F 的 legacy 线在 IOAPIC 路由下可持续投递。
|
||||
* 未注册(PIC 路由)时钩子为 NULL,idt_handler 跳过,行为与旧版一致。 */
|
||||
void (*register_apic_eoi)(void (*eoi_fn)(void));
|
||||
/* B7 阶段3: 动态 IDT 向量分配器(MSI/MSI-X 用),尾部追加保持 ABI。
|
||||
* 池范围 0x40-0xDF;alloc 返回向量号、失败 -1;free 幂等越界忽略。
|
||||
* 驱动拿到向量后经 irq_register(vector, handler) 直接注册(现有
|
||||
* 接口已接受向量命名空间,无需新注册 ABI)。 */
|
||||
int (*irq_vector_alloc)(void);
|
||||
void (*irq_vector_free)(int vector);
|
||||
} dkm_kernel_api;
|
||||
|
||||
typedef enum dkm_driver_state {
|
||||
|
||||
@@ -10,11 +10,39 @@ void idt_init(void);
|
||||
* 被 spurious IRQ / 异常触发导致三重故障重启。 */
|
||||
void idt_halt_all(void);
|
||||
|
||||
/* IRQ handler: return 1 to suppress default ACK (PIC), 0 for normal EOI.
|
||||
* 参数为 IDT vector 号 (0x20-0xFF)。
|
||||
* PIC IRQ (0x20-0x2F): 返回 1 抑制 PIC EOI; 返回 0 自动发送 PIC EOI。
|
||||
* APIC/IOAPIC/MSI (0x30-0xFF): 返回值当前忽略, EOI 需手动处理 (TODO)。 */
|
||||
/* DSK 诊断 IDT:vector 0-31 恢复原 isr_stub(异常走 idt_handler
|
||||
* 打完整日志后停机),vector 32-255 保持 halt stub。
|
||||
* BUG-20260801-005:UTSM→DSK 交接处以本函数替代 idt_halt_all,
|
||||
* 恢复 DSK 阶段 CPU 异常可见性(原静默 halt 零诊断)。
|
||||
* 不做 pic_remap / sti,DSK 上下文 IF=0 不变式保持不变。 */
|
||||
void idt_install_dsk_diag(void);
|
||||
|
||||
/* PE 诊断 IDT:idt_install_dsk_diag 的别名(幂等重装)。
|
||||
* 用于 PE 兼容层原生执行 PE32+ 前恢复异常可见性。 */
|
||||
void idt_install_pe_diag(void);
|
||||
|
||||
/* IRQ handler: return 1 to suppress default ACK (EOI), 0 for normal EOI.
|
||||
* 注册参数兼容两种命名空间(二者不重叠,自动判别):
|
||||
* - ISA IRQ 号 0-15 -> 自动换算为 vector 0x20+IRQ (兼容 ps2kbd/e1000/mouseInit)
|
||||
* - IDT vector 0x20+ -> 直接按 vector 注册 (apic 0xE0 / virtio_net 0x20+n 现状)
|
||||
* vector 0-15 是 CPU 异常向量,idt_handler 从不作为 IRQ 分发目标,故无歧义。
|
||||
* EOI 语义:
|
||||
* - PIC IRQ (0x20-0x2F): 返回 0 -> 先调 APIC EOI 钩子(若已注册)再发 8259 EOI;
|
||||
* 返回 1 -> 两者都抑制, handler 自负 EOI 全责。
|
||||
* - APIC/IOAPIC/MSI (0x30-0xFF): 返回 0 -> 调 APIC EOI 钩子(若已注册);
|
||||
* 返回 1 -> 抑制(如 handler 内已自行 LAPIC EOI)。未注册钩子时不做任何 EOI。 */
|
||||
typedef int (*irq_handler_t)(u8 vector);
|
||||
int irq_register(u8 vector, irq_handler_t handler);
|
||||
|
||||
/* B7 阶段2: 注册 LAPIC EOI 钩子(由 apic.drv 在 LAPIC 接管后调用)。
|
||||
* 钩子语义: 向 LAPIC EOI 寄存器 (MMIO base+0xB0) 写 0,清除当前 in-service
|
||||
* 最高优先级 ISR 位。重复注册以最后一次为准;传 NULL 可注销。 */
|
||||
void idt_register_apic_eoi(void (*eoi_fn)(void));
|
||||
|
||||
/* B7 阶段3: 动态 IDT 向量分配器 (MSI/MSI-X 用)。
|
||||
* 池范围 0x40-0xDF (160 个), 位图 O(1); 分配返回向量号, 池耗尽返回 -1。
|
||||
* free 幂等, 越界/静态向量输入忽略。主上下文调用, 无锁。 */
|
||||
int irq_vector_alloc(void);
|
||||
void irq_vector_free(int vector);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -731,12 +731,14 @@ int dsk_load_and_jump(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 4) 将 IDT 所有 256 个条目替换为安全 halt stub。
|
||||
* 实机会产生 spurious IRQ7/15(即使 PIC 已 mask),
|
||||
* 且 SMI/NMI 可能触发异常。如果 UTSM 的 idt_handler
|
||||
* 所调用的 log 函数或串口驱动被覆盖,会导致双重异常→三重故障。 */
|
||||
extern void idt_halt_all(void);
|
||||
idt_halt_all();
|
||||
/* 4) BUG-20260801-005: 换装 DSK 诊断 IDT(替代原静默 halt_all)。
|
||||
* vector 0-31 → UTSM isr_stub(idt_handler 打完整异常现场后
|
||||
* 安全停机),vector 32-255 保持 halt stub 防御 spurious IRQ7/15。
|
||||
* SAS-R0 下 UTSM .text 常驻内存,DSK 阶段异常投递路径已验证
|
||||
* 可用(BUG-20260729-013 登记行为);原 halt_all 把 #PF/#GP
|
||||
* 无声吞掉,DSK 阶段零诊断。 */
|
||||
extern void idt_install_dsk_diag(void);
|
||||
idt_install_dsk_diag();
|
||||
|
||||
/* 5) NMI off */
|
||||
outb(0x70, inb(0x70) | 0x80);
|
||||
|
||||
Binary file not shown.
@@ -48,7 +48,7 @@ typedef struct {
|
||||
const image_file_header *file_hdr;
|
||||
const void *optional_hdr;
|
||||
int is_pe32_plus;
|
||||
u32 image_base; /* 32位截断的加载基址(PE32+ 也截断用于 RVA 计算) */
|
||||
u64 image_base; /* 链接时首选基址(PE32+ 为 64 位,如 0x140000000,必须全宽保留) */
|
||||
u32 size_of_image;
|
||||
u32 size_of_headers;
|
||||
u32 entry_rva;
|
||||
@@ -75,7 +75,7 @@ static int parse_nt_headers(const void *pe_data, u64 size, pe_nt_info *out) {
|
||||
if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
||||
const image_optional_header64 *oh = (const image_optional_header64 *)opt;
|
||||
out->is_pe32_plus = 1;
|
||||
out->image_base = (u32)oh->image_base;
|
||||
out->image_base = oh->image_base; /* PE32+: u64 全宽,禁止截断(重定位 delta 依赖高位) */
|
||||
out->size_of_image = oh->size_of_image;
|
||||
out->size_of_headers = oh->size_of_headers;
|
||||
out->entry_rva = oh->address_of_entry_point;
|
||||
@@ -98,7 +98,7 @@ static int parse_nt_headers(const void *pe_data, u64 size, pe_nt_info *out) {
|
||||
}
|
||||
|
||||
/* ---- 应用基址重定位 ---- */
|
||||
static void apply_relocations(u8 *image, u32 image_size, u64 image_base_actual, u32 image_base_preferred,
|
||||
static void apply_relocations(u8 *image, u32 image_size, u64 image_base_actual, u64 image_base_preferred,
|
||||
const image_data_directory *reloc_dir,
|
||||
const image_section_header *sections, int nsec,
|
||||
const void *pe_data, u64 pe_size, int is_pe32_plus) {
|
||||
@@ -313,7 +313,7 @@ int pe_load_image(const void *pe_data, u64 size, pe_image_info *out) {
|
||||
|
||||
/* 基址重定位 */
|
||||
u64 actual_base = (u64)image;
|
||||
u32 preferred_base = nt.image_base;
|
||||
u64 preferred_base = nt.image_base;
|
||||
/* image 缓冲区按 RVA 排列(image[0]=RVA 0),重定位写入 image 内 */
|
||||
/* 注意:apply_relocations 直接操作 image 缓冲区,delta = actual_base - preferred_base */
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "x86emu32.h"
|
||||
#include "../include/utsm/pe.h"
|
||||
#include <utsm/arena.h>
|
||||
#include <utsm/idt.h>
|
||||
#include <utsm/log.h>
|
||||
#include <utsm/types.h>
|
||||
|
||||
@@ -275,6 +276,12 @@ int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit
|
||||
log_info("[PE] running PE32+ natively");
|
||||
pe_enable_sse();
|
||||
|
||||
/* 恢复异常可见性:UTSM 交权 DSK 时 idt_halt_all() 将 IDT 全部
|
||||
* 替换为静默 halt stub,PE 原生执行期间的 CPU 异常(PF/GP/UD)
|
||||
* 会被无声吞掉。安装 PE 诊断 IDT 后异常走 idt_handler 打完整
|
||||
* vector/err/rip/CR2/寄存器日志再安全停机。 */
|
||||
idt_install_pe_diag();
|
||||
|
||||
/* 独立 1MB 栈(内核栈仅 16KB,egui 渲染会溢出) */
|
||||
u8 *pe_stack = (u8 *)kmem_alloc_aligned(PE_STACK_BYTES, 4096);
|
||||
if (!pe_stack) {
|
||||
@@ -283,9 +290,13 @@ int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit
|
||||
}
|
||||
u64 stack_top = (u64)pe_stack + PE_STACK_BYTES; /* 4096 对齐 -> 16 对齐 */
|
||||
|
||||
/* BUG-20260801-006: __builtin_setjmp 必须直接出现在
|
||||
* pe_service_run 本体内——该帧在 PE 执行全程存活,longjmp
|
||||
* 恢复合法.原 pe_shim_setup_exit() 包装函数 setjmp 后即返回,
|
||||
* longjmp 跨已销毁帧 = UB(实测从脏栈 pop 出 log_hex64 缓冲
|
||||
* 残片充当 R12-R15/RBP → 野指针 call → 三重故障). */
|
||||
u64 code = 0;
|
||||
int jumped = pe_shim_setup_exit();
|
||||
if (jumped == 0) {
|
||||
if (__builtin_setjmp(pe_shim_exit_jmpbuf()) == 0) {
|
||||
pe_entry_fn entry = (pe_entry_fn)info.entry_point;
|
||||
log_hex64("[PE] entry=", (u64)entry);
|
||||
log_hex64("[PE] stack_top=", stack_top);
|
||||
@@ -323,22 +334,30 @@ int pe_service_run(const void *pe_data, u64 size, const char *cmdline, u64 *exit
|
||||
}
|
||||
emu.eip = img.entry_point;
|
||||
|
||||
/* BUG-20260801-007: fn64 回退 shim 收到的指针参数是 32 位
|
||||
* guest VA,设置解释器内存基址使 shim 内 gp() 完成 guest→host
|
||||
* 转换;运行结束立即复位(原生 PE32+ 路径恒为 0,gp 恒等映射). */
|
||||
pe_shim_set_emu_base((u64)img.mem);
|
||||
|
||||
/* BUG-20260801-006: 同 PE32+ 路径,setjmp 直接出现在本体内.
|
||||
* PE32 解释器路径同一 longjmp 机制,同样暴露在 UB 下. */
|
||||
u64 code = 0;
|
||||
int jumped = pe_shim_setup_exit();
|
||||
if (jumped == 0) {
|
||||
if (__builtin_setjmp(pe_shim_exit_jmpbuf()) == 0) {
|
||||
int rrc = x86emu32_run(&emu, &code);
|
||||
if (rrc != 0) {
|
||||
log_error("[PE32] emulator error");
|
||||
code = 0xFFFFFFFFULL;
|
||||
}
|
||||
/* emu 字段只在未发生 longjmp 的正常返回路径读取
|
||||
* (setjmp 语义:longjmp 后被修改的自动变量值不确定) */
|
||||
if (emu.error && !emu.exited) {
|
||||
log_warn("[PE32] emulator stopped with error");
|
||||
}
|
||||
} else {
|
||||
code = pe_shim_get_exit_code();
|
||||
log_hex64("[PE32] ExitProcess code=", code);
|
||||
}
|
||||
|
||||
if (emu.error && !emu.exited) {
|
||||
log_warn("[PE32] emulator stopped with error");
|
||||
}
|
||||
pe_shim_set_emu_base(0);
|
||||
|
||||
if (exit_code) *exit_code = code;
|
||||
return 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pe/pe_service.o: pe\pe_service.c pe\pe_loader.h include\utsm\types.h \
|
||||
include\utsm\pe.h pe\pe_shim.h pe\x86emu32.h pe\..\include\utsm\pe.h \
|
||||
include\utsm\arena.h include\utsm\log.h
|
||||
include\utsm\arena.h include\utsm\idt.h include\utsm\log.h
|
||||
pe\pe_loader.h:
|
||||
include\utsm\types.h:
|
||||
include\utsm\pe.h:
|
||||
@@ -8,4 +8,5 @@ pe\pe_shim.h:
|
||||
pe\x86emu32.h:
|
||||
pe\..\include\utsm\pe.h:
|
||||
include\utsm\arena.h:
|
||||
include\utsm\idt.h:
|
||||
include\utsm\log.h:
|
||||
|
||||
+391
-85
@@ -9,14 +9,19 @@
|
||||
* ntdll.dll NtWriteFile、RtlNtStatusToDosError
|
||||
* bcryptprimitives.dll ProcessPrng (rdtsc xorshift)
|
||||
* api-ms-win-core-synch-l1-2-0.dll WaitOnAddress、WakeByAddress (futex 桩)
|
||||
* user32.dll 窗口类、窗口、消息泵 (PS/2 键盘轮询)、DefWindowProc
|
||||
* user32.dll 窗口类、窗口、消息泵 (PS/2 键盘+AUX 鼠标轮询)、DefWindowProc
|
||||
* gdi32.dll 内存 DC、DIBSection、BitBlt 到 Limine 帧缓冲
|
||||
*
|
||||
* 窗口模型:单顶层窗口 (固定伪句柄),GetDC 返回窗口 DC,
|
||||
* BitBlt(窗口 DC, ...) 把 DIB 后台缓冲按窗口原点拷入帧缓冲.
|
||||
* 消息泵:PeekMessageW 轮询 PS/2 键盘 (0x64/0x60),生成
|
||||
* WM_KEYDOWN、WM_KEYUP、WM_CHAR;Esc 额外合成 WM_CLOSE (无鼠标环境的
|
||||
* 唯一退出途径).鼠标字节 (状态位 0x20) 直接丢弃.
|
||||
* 消息泵:PeekMessageW 轮询 PS/2 (0x64/0x60),按状态位 0x20 分流键盘/鼠标:
|
||||
* 键盘 -> WM_KEYDOWN、WM_KEYUP、WM_CHAR;Esc 额外合成 WM_CLOSE;
|
||||
* 鼠标 -> 3 字节包解码(同步位/溢出防御),坐标钳位到帧缓冲,
|
||||
* 边沿生成 WM_MOUSEMOVE 与 WM_xBUTTONDOWN/WM_xBUTTONUP 系列.
|
||||
* 软件光标:BitBlt 上屏后在光标坐标叠画 12x12 箭头(黑边白心),
|
||||
* 下一帧 BitBlt 自动覆盖旧光标,无需保存/恢复背景.
|
||||
* 鼠标初始化:on-demand(首次轮询时),AUX 启用 + 0xF4 数据报告,
|
||||
* 全程超时保护,单步失败只记日志并禁用鼠标,绝不阻塞.
|
||||
*/
|
||||
|
||||
#include "pe_shim.h"
|
||||
@@ -45,16 +50,39 @@ static int g_exit_jmp_set;
|
||||
static u64 g_exit_code;
|
||||
static const char *g_cmdline = "";
|
||||
|
||||
/* ===== BUG-20260801-007: PE32 guest→host 指针转换 =====
|
||||
* PE32 解释器路径下,fn64 回退 shim 收到的指针参数是 32 位 guest VA
|
||||
* (相对解释器 mem 基址的偏移),直接当 host 指针解引用 → 低地址 #PF
|
||||
* → 三重故障(定案证据:#PF RIP 落在 shim_printf 体内解引用 0x401017)。
|
||||
* x86emu32 执行前经 pe_shim_set_emu_base() 设置 mem 基址,各 shim 在
|
||||
* **指针解引用点**经 gp() 转换(不在参数入口盲目转换——退出码/长度等
|
||||
* 小整数参数会被误转)。阈值 0x10000000 与 pe_service.c 解释器地址
|
||||
* 空间上限一致;shim 伪句柄(0x10000001+)与负值句柄天然豁免。
|
||||
* 原生 PE32+ 路径 g_emu_mem_base 恒为 0,gp() 恒等映射,零影响。 */
|
||||
static u64 g_emu_mem_base = 0;
|
||||
|
||||
void pe_shim_set_emu_base(u64 base) { g_emu_mem_base = base; }
|
||||
|
||||
static inline u64 gp(u64 va) {
|
||||
if (g_emu_mem_base && va && va < 0x10000000ULL)
|
||||
return g_emu_mem_base + va;
|
||||
return va;
|
||||
}
|
||||
|
||||
void pe_shim_init(const char *cmdline) {
|
||||
g_cmdline = cmdline ? cmdline : "";
|
||||
g_exit_jmp_set = 0;
|
||||
g_exit_code = 0;
|
||||
g_emu_mem_base = 0; /* BUG-007: 默认非解释器上下文(原生路径恒等映射) */
|
||||
}
|
||||
|
||||
/* pe_run 调用:保存上下文,返回 0 = 首次(继续执行),1 = ExitProcess 触发 */
|
||||
int pe_shim_setup_exit(void) {
|
||||
/* 返回 ExitProcess longjmp 目标 jmpbuf(见 pe_shim.h 的 BUG-006 说明).
|
||||
* 本函数只提供缓冲区并重置退出状态;__builtin_setjmp 由调用方
|
||||
* (pe_service_run 本体)直接执行,确保 setjmp 帧在 PE 执行全程存活. */
|
||||
void **pe_shim_exit_jmpbuf(void) {
|
||||
g_exit_jmp_set = 1;
|
||||
return __builtin_setjmp(g_exit_jmp);
|
||||
g_exit_code = 0;
|
||||
return g_exit_jmp;
|
||||
}
|
||||
|
||||
void pe_shim_exit_process(u64 exit_code) {
|
||||
@@ -81,7 +109,18 @@ u64 pe_shim_unimpl_stub(void) {
|
||||
}
|
||||
|
||||
/* ===== 串口/帧缓冲输出(shim 内部复用) ===== */
|
||||
/* 单次输出长度上限:防御 PE 侧传野 (buf,len)(如 panic 递归中损坏的
|
||||
* String)把半个地址空间倒进串口并踩穿未映射页(#PF).正常日志远远
|
||||
* 低于此值;截断时留锚点便于定位. */
|
||||
#define SHIM_OUTPUT_MAX (1ULL * 1024ULL * 1024ULL)
|
||||
|
||||
static void shim_output(const char *s, u64 len) {
|
||||
if (len > SHIM_OUTPUT_MAX) {
|
||||
log_error("[shim] output len clamped");
|
||||
log_hex64("[shim] output buf=", (u64)s);
|
||||
log_hex64("[shim] output len=", len);
|
||||
len = SHIM_OUTPUT_MAX;
|
||||
}
|
||||
for (u64 i = 0; i < len; i++) {
|
||||
serial_putc(s[i]);
|
||||
}
|
||||
@@ -115,10 +154,11 @@ static u64 __attribute__((ms_abi)) shim_WriteFile(u64 handle, u64 buffer, u64 le
|
||||
u64 bytes_written_ptr, u64 overlapped) {
|
||||
(void)overlapped;
|
||||
if (handle == SHIM_STDOUT_HANDLE || handle == SHIM_STDERR_HANDLE) {
|
||||
const char *buf = (const char *)buffer;
|
||||
const char *buf = (const char *)gp(buffer);
|
||||
shim_output(buf, length);
|
||||
/* lpNumberOfBytesWritten 是 DWORD*(4 字节),不能按 u64 写 */
|
||||
if (bytes_written_ptr) *(u32 *)bytes_written_ptr = (u32)length;
|
||||
u64 bwp = gp(bytes_written_ptr);
|
||||
if (bwp) *(u32 *)bwp = (u32)length;
|
||||
return 1; /* TRUE */
|
||||
}
|
||||
return 0;
|
||||
@@ -127,8 +167,9 @@ static u64 __attribute__((ms_abi)) shim_WriteFile(u64 handle, u64 buffer, u64 le
|
||||
static u64 __attribute__((ms_abi)) shim_ReadFile(u64 handle, u64 buffer, u64 max_length,
|
||||
u64 bytes_read_ptr, u64 overlapped) {
|
||||
(void)handle; (void)buffer; (void)max_length; (void)overlapped;
|
||||
/* 暂不支持输入读取 */
|
||||
if (bytes_read_ptr) *(u64 *)bytes_read_ptr = 0;
|
||||
/* 暂不支持输入读取;lpNumberOfBytesRead 同为 DWORD*(4 字节) */
|
||||
u64 brp = gp(bytes_read_ptr);
|
||||
if (brp) *(u32 *)brp = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -163,12 +204,13 @@ static u64 __attribute__((ms_abi)) shim_HeapAlloc(u64 heap, u64 flags, u64 size)
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_HeapFree(u64 heap, u64 flags, u64 ptr) {
|
||||
(void)heap; (void)flags;
|
||||
shim_heap_free((void *)ptr);
|
||||
/* gp():PE32 野指针收束进 guest 沙盒,避免低地址 host 解引用 */
|
||||
shim_heap_free((void *)gp(ptr));
|
||||
return 1;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_HeapReAlloc(u64 heap, u64 flags, u64 ptr, u64 size) {
|
||||
(void)heap; (void)flags;
|
||||
return (u64)shim_heap_realloc((void *)ptr, size);
|
||||
return (u64)shim_heap_realloc((void *)gp(ptr), size);
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_GetProcessHeap(void) {
|
||||
return 0x10000010;
|
||||
@@ -216,7 +258,7 @@ static void shim_msvcrt_output(const char *s) {
|
||||
static u64 __attribute__((ms_abi)) shim_printf(u64 fmt, u64 a1, u64 a2, u64 a3) {
|
||||
/* 可变参数:MS x64 ABI 下 a1-a3 在 RDX/R8/R9,更多参数在栈上.
|
||||
* 简化处理:只处理前 3 个参数. */
|
||||
const char *f = (const char *)fmt;
|
||||
const char *f = (const char *)gp(fmt);
|
||||
u64 args[3] = {a1, a2, a3};
|
||||
int argi = 0;
|
||||
char outbuf[512];
|
||||
@@ -226,7 +268,7 @@ static u64 __attribute__((ms_abi)) shim_printf(u64 fmt, u64 a1, u64 a2, u64 a3)
|
||||
i++;
|
||||
switch (f[i]) {
|
||||
case 's': {
|
||||
const char *s = (const char *)(args[argi++ & 3]);
|
||||
const char *s = (const char *)gp(args[argi++ & 3]);
|
||||
if (s) while (*s && oi < 510) outbuf[oi++] = *s++;
|
||||
break;
|
||||
}
|
||||
@@ -272,7 +314,7 @@ static u64 __attribute__((ms_abi)) shim_printf(u64 fmt, u64 a1, u64 a2, u64 a3)
|
||||
}
|
||||
|
||||
static u64 __attribute__((ms_abi)) shim_puts(u64 str) {
|
||||
const char *s = (const char *)str;
|
||||
const char *s = (const char *)gp(str);
|
||||
shim_msvcrt_output(s);
|
||||
shim_msvcrt_output("\n");
|
||||
return 0;
|
||||
@@ -292,9 +334,9 @@ static u64 __attribute__((ms_abi)) shim_calloc(u64 count, u64 size) {
|
||||
if (p) for (u64 i = 0; i < total; i++) p[i] = 0;
|
||||
return (u64)p;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_free(u64 ptr) { shim_heap_free((void *)ptr); return 0; }
|
||||
static u64 __attribute__((ms_abi)) shim_free(u64 ptr) { shim_heap_free((void *)gp(ptr)); return 0; }
|
||||
static u64 __attribute__((ms_abi)) shim_realloc(u64 ptr, u64 size) {
|
||||
return (u64)shim_heap_realloc((void *)ptr, size);
|
||||
return (u64)shim_heap_realloc((void *)gp(ptr), size);
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_exit(u64 code) {
|
||||
pe_shim_exit_process(code);
|
||||
@@ -302,38 +344,38 @@ static u64 __attribute__((ms_abi)) shim_exit(u64 code) {
|
||||
}
|
||||
|
||||
static u64 __attribute__((ms_abi)) shim_memcpy(u64 dst, u64 src, u64 n) {
|
||||
u8 *d = (u8 *)dst; const u8 *s = (const u8 *)src;
|
||||
u8 *d = (u8 *)gp(dst); const u8 *s = (const u8 *)gp(src);
|
||||
for (u64 i = 0; i < n; i++) d[i] = s[i];
|
||||
return dst;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_memset(u64 dst, u64 val, u64 n) {
|
||||
u8 *d = (u8 *)dst; u8 v = (u8)(val & 0xFF);
|
||||
u8 *d = (u8 *)gp(dst); u8 v = (u8)(val & 0xFF);
|
||||
for (u64 i = 0; i < n; i++) d[i] = v;
|
||||
return dst;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_memmove(u64 dst, u64 src, u64 n) {
|
||||
u8 *d = (u8 *)dst; const u8 *s = (const u8 *)src;
|
||||
u8 *d = (u8 *)gp(dst); const u8 *s = (const u8 *)gp(src);
|
||||
if (d < s) { for (u64 i = 0; i < n; i++) d[i] = s[i]; }
|
||||
else { for (u64 i = n; i > 0; i--) d[i-1] = s[i-1]; }
|
||||
return dst;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_strlen(u64 str) {
|
||||
const char *s = (const char *)str;
|
||||
const char *s = (const char *)gp(str);
|
||||
u64 n = 0; while (s[n]) n++; return n;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_strcmp(u64 a, u64 b) {
|
||||
const char *pa = (const char *)a; const char *pb = (const char *)b;
|
||||
const char *pa = (const char *)gp(a); const char *pb = (const char *)gp(b);
|
||||
while (*pa && *pa == *pb) { pa++; pb++; }
|
||||
return (u64)(i64)(*pa - *pb);
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_strcpy(u64 dst, u64 src) {
|
||||
char *d = (char *)dst; const char *s = (const char *)src;
|
||||
char *d = (char *)gp(dst); const char *s = (const char *)gp(src);
|
||||
while (*s) *d++ = *s++;
|
||||
*d = 0;
|
||||
return dst;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_strncmp(u64 a, u64 b, u64 n) {
|
||||
const char *pa = (const char *)a; const char *pb = (const char *)b;
|
||||
const char *pa = (const char *)gp(a); const char *pb = (const char *)gp(b);
|
||||
for (u64 i = 0; i < n; i++) {
|
||||
if (pa[i] != pb[i]) return (u64)(i64)(pa[i] - pb[i]);
|
||||
if (pa[i] == 0) return 0;
|
||||
@@ -343,12 +385,12 @@ static u64 __attribute__((ms_abi)) shim_strncmp(u64 a, u64 b, u64 n) {
|
||||
static u64 __attribute__((ms_abi)) shim_fwrite(u64 ptr, u64 size, u64 nmemb, u64 stream) {
|
||||
(void)stream;
|
||||
u64 total = size * nmemb;
|
||||
shim_output((const char *)ptr, total);
|
||||
shim_output((const char *)gp(ptr), total);
|
||||
return nmemb;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_fputs(u64 str, u64 stream) {
|
||||
(void)stream;
|
||||
shim_output_str((const char *)str);
|
||||
shim_output_str((const char *)gp(str));
|
||||
return 0;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_fputc(u64 ch, u64 stream) {
|
||||
@@ -372,11 +414,23 @@ static u64 __attribute__((ms_abi)) shim_initterm_e(u64 start, u64 end) {
|
||||
*
|
||||
* Rust std 的 GlobalAlloc 走 GetProcessHeap/HeapAlloc/HeapReAlloc/
|
||||
* HeapFree.arena 只增不减,GUI 主循环每帧的 Vec 分配会耗尽内存,
|
||||
* 因此 shim 从 arena carve 一块 8MB 区域自行管理:
|
||||
* 32B 块头 {size,used,next_free,pad},首次适应 + 分割 + 前向合并.
|
||||
* 因此 shim 从 arena carve 一块 32MB 区域自行管理:
|
||||
* 32B 块头 {size,used,next_free,pad},首次适应 + 分割.
|
||||
*
|
||||
* 合并策略(2026-08-02 修复):空闲链表按地址升序维护,free 时做
|
||||
* 前向 + 后向双向合并.旧实现是 LIFO 头插 + 仅前向合并,egui 每帧
|
||||
* 内部存在非 LIFO 的 alloc/free 序列,物理相邻的空闲块永远无法
|
||||
* 后向合并,堆逐渐碎成小片,实测 ~27KB/帧的速度劣化,第 ~1200 帧
|
||||
* (BitBlt frames=0x4b0)连 0x2a80 的连续块都分不出而 OOM,随后
|
||||
* Rust panic 递归损坏 String,NtWriteFile 野性转储 55MB 踩穿
|
||||
* 未映射页 #PF(rip=shim_NtWriteFile+0x30).地址有序空闲链表使
|
||||
* "物理相邻"等价于"链相邻",前后向合并即可完备回收.
|
||||
*
|
||||
* (egui 字体 + 每帧 tessellation 缓冲实测 8MB 会在第 3 帧 OOM,扩到 32MB;
|
||||
* arena 总 64MB,PE 镜像~3MB + 栈 1MB + DIB ~6MB,余量充足.)
|
||||
* ========================================================== */
|
||||
|
||||
#define SHIM_HEAP_BYTES (8ULL * 1024ULL * 1024ULL)
|
||||
#define SHIM_HEAP_BYTES (32ULL * 1024ULL * 1024ULL)
|
||||
#define HEAP_HDR 32
|
||||
|
||||
typedef struct heap_blk {
|
||||
@@ -388,6 +442,7 @@ typedef struct heap_blk {
|
||||
|
||||
static u8 *g_heap_region;
|
||||
static heap_blk *g_heap_free_head;
|
||||
static u64 g_heap_live; /* 已分配字节合计(诊断:区分真泄漏与碎片化) */
|
||||
|
||||
static void shim_heap_init_once(void) {
|
||||
if (g_heap_region) return;
|
||||
@@ -404,57 +459,88 @@ static void shim_heap_init_once(void) {
|
||||
g_heap_free_head = b;
|
||||
}
|
||||
|
||||
/* 大/小块阈值:egui 长寿命缓存块多为几十字节,每帧 tessellation 等
|
||||
* 瞬态缓冲为 10-30KB.大块专取地址最高的适配块,使顶部连续区域免于
|
||||
* 被小长寿块钉碎(实测无此策略时 2400 帧后 29.9MB 空闲碎成 6 万个
|
||||
* 平均 500B 的片,free_max 只剩 10.2KB,10.8KB 请求即 OOM). */
|
||||
#define SHIM_HEAP_LARGE 2048ULL
|
||||
|
||||
static void *shim_heap_alloc(u64 size) {
|
||||
shim_heap_init_once();
|
||||
if (!g_heap_region) return 0;
|
||||
if (size == 0) size = 16;
|
||||
size = (size + 15) & ~15ULL;
|
||||
heap_blk *prev = 0, *b = g_heap_free_head;
|
||||
while (b) {
|
||||
if (b->size >= size) {
|
||||
if (prev) prev->next_free = b->next_free;
|
||||
else g_heap_free_head = (heap_blk *)b->next_free;
|
||||
/* 剩余足够再分一块则分割 */
|
||||
if (b->size >= size + HEAP_HDR + 16) {
|
||||
heap_blk *nb = (heap_blk *)((u8 *)b + HEAP_HDR + size);
|
||||
nb->size = b->size - size - HEAP_HDR;
|
||||
nb->used = 0;
|
||||
nb->pad = 0;
|
||||
nb->next_free = (u64)g_heap_free_head;
|
||||
g_heap_free_head = nb;
|
||||
b->size = size;
|
||||
}
|
||||
b->used = 1;
|
||||
b->next_free = 0;
|
||||
return (u8 *)b + HEAP_HDR;
|
||||
heap_blk *cand = 0, *cand_prev = 0;
|
||||
if (size < SHIM_HEAP_LARGE) {
|
||||
/* 小块:首次适应(低地址优先)填碎洞,保护顶部大区域 */
|
||||
while (b) {
|
||||
if (b->size >= size) { cand = b; cand_prev = prev; break; }
|
||||
prev = b; b = (heap_blk *)b->next_free;
|
||||
}
|
||||
prev = b;
|
||||
b = (heap_blk *)b->next_free;
|
||||
} else {
|
||||
/* 大块:取地址最高的适配块(地址有序链,最后一个适配者) */
|
||||
while (b) {
|
||||
if (b->size >= size) { cand = b; cand_prev = prev; }
|
||||
prev = b; b = (heap_blk *)b->next_free;
|
||||
}
|
||||
}
|
||||
b = cand; prev = cand_prev;
|
||||
if (b) {
|
||||
/* 摘除 b;分割时余量 nb 占据 b 原链位即可保持地址有序
|
||||
* (nb=b+HEAP_HDR+size < 原后继:后继不与 b 重叠 => 后继>=b 块尾 > nb) */
|
||||
if (prev) prev->next_free = b->next_free;
|
||||
else g_heap_free_head = (heap_blk *)b->next_free;
|
||||
if (b->size >= size + HEAP_HDR + 16) {
|
||||
heap_blk *nb = (heap_blk *)((u8 *)b + HEAP_HDR + size);
|
||||
nb->size = b->size - size - HEAP_HDR;
|
||||
nb->used = 0;
|
||||
nb->pad = 0;
|
||||
nb->next_free = b->next_free;
|
||||
if (prev) prev->next_free = (u64)nb;
|
||||
else g_heap_free_head = nb;
|
||||
b->size = size;
|
||||
}
|
||||
b->used = 1;
|
||||
b->next_free = 0;
|
||||
g_heap_live += b->size;
|
||||
return (u8 *)b + HEAP_HDR;
|
||||
}
|
||||
/* OOM 诊断:live 持续增长=真泄漏;live 平稳而 free_max 萎缩=碎片化 */
|
||||
u64 ft = 0, fm = 0, fc = 0;
|
||||
for (heap_blk *f = g_heap_free_head; f; f = (heap_blk *)f->next_free) {
|
||||
ft += f->size; if (f->size > fm) fm = f->size; fc++;
|
||||
}
|
||||
log_error("[shim] heap OOM");
|
||||
log_hex64("[shim] heap req=", size);
|
||||
log_hex64("[shim] heap live=", g_heap_live);
|
||||
log_hex64("[shim] heap free_total=", ft);
|
||||
log_hex64("[shim] heap free_max=", fm);
|
||||
log_hex64("[shim] heap free_blocks=", fc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void shim_heap_free(void *ptr) {
|
||||
if (!ptr) return;
|
||||
heap_blk *b = (heap_blk *)((u8 *)ptr - HEAP_HDR);
|
||||
g_heap_live -= b->size;
|
||||
b->used = 0;
|
||||
/* 前向合并:物理下一块若空闲则吸收 */
|
||||
heap_blk *nb = (heap_blk *)((u8 *)b + HEAP_HDR + b->size);
|
||||
if ((u8 *)nb >= g_heap_region && (u8 *)nb < g_heap_region + SHIM_HEAP_BYTES
|
||||
&& !nb->used && nb->size) {
|
||||
/* 从空闲链摘除 nb */
|
||||
heap_blk *prev = 0, *x = g_heap_free_head;
|
||||
while (x && x != nb) { prev = x; x = (heap_blk *)x->next_free; }
|
||||
if (x == nb) {
|
||||
if (prev) prev->next_free = nb->next_free;
|
||||
else g_heap_free_head = (heap_blk *)nb->next_free;
|
||||
b->size += HEAP_HDR + nb->size;
|
||||
}
|
||||
/* 地址升序插入空闲链表:prev < b < x(x 可为 0 表尾) */
|
||||
heap_blk *prev = 0, *x = g_heap_free_head;
|
||||
while (x && x < b) { prev = x; x = (heap_blk *)x->next_free; }
|
||||
b->next_free = (u64)x;
|
||||
if (prev) prev->next_free = (u64)b;
|
||||
else g_heap_free_head = b;
|
||||
/* 前向合并:x 与 b 物理相邻则吸收(地址有序 => 物理相邻即链相邻) */
|
||||
if (x && (u8 *)b + HEAP_HDR + b->size == (u8 *)x) {
|
||||
b->size += HEAP_HDR + x->size;
|
||||
b->next_free = x->next_free;
|
||||
}
|
||||
/* 后向合并:prev 与 b 物理相邻则把 b 并入 prev */
|
||||
if (prev && (u8 *)prev + HEAP_HDR + prev->size == (u8 *)b) {
|
||||
prev->size += HEAP_HDR + b->size;
|
||||
prev->next_free = b->next_free;
|
||||
}
|
||||
b->next_free = (u64)g_heap_free_head;
|
||||
g_heap_free_head = b;
|
||||
}
|
||||
|
||||
static void *shim_heap_realloc(void *ptr, u64 size) {
|
||||
@@ -503,11 +589,13 @@ static void shim_tsc_init(void) {
|
||||
|
||||
static u64 __attribute__((ms_abi)) shim_QueryPerformanceFrequency(u64 freq_ptr) {
|
||||
shim_tsc_init();
|
||||
freq_ptr = gp(freq_ptr);
|
||||
if (freq_ptr) *(u64 *)freq_ptr = g_tsc_per_ms * 1000;
|
||||
return 1;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_QueryPerformanceCounter(u64 count_ptr) {
|
||||
shim_tsc_init();
|
||||
count_ptr = gp(count_ptr);
|
||||
if (count_ptr) *(u64 *)count_ptr = shim_rdtsc();
|
||||
return 1;
|
||||
}
|
||||
@@ -524,6 +612,7 @@ static u64 __attribute__((ms_abi)) shim_Sleep(u64 ms) {
|
||||
|
||||
/* SYSTEM_INFO(x64,48 字节) */
|
||||
static u64 __attribute__((ms_abi)) shim_GetSystemInfo(u64 si_p) {
|
||||
si_p = gp(si_p);
|
||||
if (!si_p) return 0;
|
||||
u8 *p = (u8 *)si_p;
|
||||
for (int i = 0; i < 48; i++) p[i] = 0;
|
||||
@@ -548,7 +637,7 @@ static u64 __attribute__((ms_abi)) shim_WriteConsoleW(u64 handle, u64 buf, u64 n
|
||||
u64 written_ptr, u64 reserved) {
|
||||
(void)reserved;
|
||||
if (handle != SHIM_STDOUT_HANDLE && handle != SHIM_STDERR_HANDLE) return 0;
|
||||
const u16 *s = (const u16 *)buf;
|
||||
const u16 *s = (const u16 *)gp(buf);
|
||||
for (u64 i = 0; i < nchars; i++) {
|
||||
u32 c = s[i];
|
||||
if (c < 0x80) {
|
||||
@@ -562,12 +651,14 @@ static u64 __attribute__((ms_abi)) shim_WriteConsoleW(u64 handle, u64 buf, u64 n
|
||||
serial_putc((char)(0x80 | (c & 0x3F)));
|
||||
}
|
||||
}
|
||||
written_ptr = gp(written_ptr);
|
||||
if (written_ptr) *(u32 *)written_ptr = (u32)nchars;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u64 __attribute__((ms_abi)) shim_GetCurrentDirectoryW(u64 buflen, u64 buf) {
|
||||
static const u16 cwd[] = { 'C', ':', '\\', 0 };
|
||||
buf = gp(buf);
|
||||
if (!buf || buflen < 4) return 4; /* 需要 4 个字符(含 NUL) */
|
||||
u16 *out = (u16 *)buf;
|
||||
for (int i = 0; i < 4; i++) out[i] = cwd[i];
|
||||
@@ -603,6 +694,7 @@ static u64 __attribute__((ms_abi)) shim_FormatMessageW(u64 flags, u64 src, u64 m
|
||||
}
|
||||
|
||||
static u64 __attribute__((ms_abi)) shim_LoadLibraryA(u64 name_p) {
|
||||
name_p = gp(name_p);
|
||||
if (!name_p) return 0;
|
||||
const char *n = (const char *)name_p;
|
||||
/* 已知 DLL 返回伪句柄(大小写不敏感子串匹配) */
|
||||
@@ -625,7 +717,7 @@ static u64 __attribute__((ms_abi)) shim_LoadLibraryA(u64 name_p) {
|
||||
}
|
||||
|
||||
static u64 __attribute__((ms_abi)) shim_lstrlenW(u64 str_p) {
|
||||
const u16 *s = (const u16 *)str_p;
|
||||
const u16 *s = (const u16 *)gp(str_p);
|
||||
u64 n = 0;
|
||||
if (s) while (s[n]) n++;
|
||||
return n;
|
||||
@@ -636,6 +728,7 @@ static u64 __attribute__((ms_abi)) shim_lstrlenW(u64 str_p) {
|
||||
* Rust std 用它动态探测 Rtl 系列/dbghelp API,命中返回真实 shim 地址,miss 返回 0。 */
|
||||
static u64 __attribute__((ms_abi)) shim_GetProcAddress(u64 module, u64 name_p) {
|
||||
(void)module;
|
||||
name_p = gp(name_p);
|
||||
if (!name_p) return 0;
|
||||
if ((name_p >> 16) == 0) { /* ordinal 导入 */
|
||||
g_last_error = 127; /* ERROR_PROC_NOT_FOUND */
|
||||
@@ -660,12 +753,13 @@ static u64 __attribute__((ms_abi)) shim_GetProcAddress(u64 module, u64 name_p) {
|
||||
|
||||
/* Rtl* — Rust panic/unwind 机制引用(panic=abort 下不会真正走到) */
|
||||
static u64 __attribute__((ms_abi)) shim_RtlCaptureContext(u64 ctx_p) {
|
||||
u8 *p = (u8 *)ctx_p;
|
||||
u8 *p = (u8 *)gp(ctx_p);
|
||||
if (p) for (int i = 0; i < 1232; i++) p[i] = 0; /* x64 CONTEXT ≈ 1232B */
|
||||
return 0;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_RtlLookupFunctionEntry(u64 rip, u64 base_p, u64 table_p) {
|
||||
(void)rip;
|
||||
base_p = gp(base_p); table_p = gp(table_p);
|
||||
if (base_p) *(u64 *)base_p = 0;
|
||||
if (table_p) *(u64 *)table_p = 0;
|
||||
return 0;
|
||||
@@ -680,8 +774,8 @@ static u64 __attribute__((ms_abi)) shim_RtlVirtualUnwind(u64 a, u64 b, u64 c, u6
|
||||
static u64 __attribute__((ms_abi)) shim_MultiByteToWideChar(u64 cp, u64 flags, u64 mbs_p,
|
||||
u64 cbmb, u64 wbuf_p, u64 cchw) {
|
||||
(void)flags;
|
||||
const u8 *s = (const u8 *)mbs_p;
|
||||
u16 *out = (u16 *)wbuf_p;
|
||||
const u8 *s = (const u8 *)gp(mbs_p);
|
||||
u16 *out = (u16 *)gp(wbuf_p);
|
||||
i64 remain = (i64)cbmb; /* -1 = NUL 结尾 */
|
||||
u64 need = 0;
|
||||
if (!s) return 0;
|
||||
@@ -730,10 +824,11 @@ static u64 __attribute__((ms_abi)) shim_WideCharToMultiByte(u64 cp, u64 flags, u
|
||||
u64 cchw, u64 mbs_p, u64 cbmb,
|
||||
u64 defc, u64 used_p) {
|
||||
(void)flags; (void)defc;
|
||||
const u16 *s = (const u16 *)ws_p;
|
||||
u8 *out = (u8 *)mbs_p;
|
||||
const u16 *s = (const u16 *)gp(ws_p);
|
||||
u8 *out = (u8 *)gp(mbs_p);
|
||||
i64 remain = (i64)cchw; /* -1 = NUL 结尾 */
|
||||
u64 need = 0;
|
||||
used_p = gp(used_p);
|
||||
if (used_p) *(u32 *)used_p = 0;
|
||||
if (!s) return 0;
|
||||
for (;;) {
|
||||
@@ -785,8 +880,9 @@ static u64 __attribute__((ms_abi)) shim_NtWriteFile(u64 fh, u64 ev, u64 apc_r, u
|
||||
u64 off, u64 key) {
|
||||
(void)ev; (void)apc_r; (void)apc_c; (void)off; (void)key;
|
||||
if (fh == SHIM_STDOUT_HANDLE || fh == SHIM_STDERR_HANDLE) {
|
||||
shim_output((const char *)buf, len);
|
||||
shim_output((const char *)gp(buf), len);
|
||||
}
|
||||
iosb = gp(iosb);
|
||||
if (iosb) { /* IO_STATUS_BLOCK: {NTSTATUS Status; ULONG_PTR Information} */
|
||||
((u64 *)iosb)[0] = 0;
|
||||
((u64 *)iosb)[1] = len;
|
||||
@@ -799,7 +895,7 @@ static u64 __attribute__((ms_abi)) shim_RtlNtStatusToDosError(u64 status) {
|
||||
|
||||
static u64 __attribute__((ms_abi)) shim_ProcessPrng(u64 buf, u64 len) {
|
||||
u64 s = shim_rdtsc() ^ 0x9E3779B97F4A7C15ULL;
|
||||
u8 *p = (u8 *)buf;
|
||||
u8 *p = (u8 *)gp(buf);
|
||||
for (u64 i = 0; i < len; i++) {
|
||||
s ^= s << 13; s ^= s >> 7; s ^= s << 17; /* xorshift64 */
|
||||
p[i] = (u8)(s & 0xFF);
|
||||
@@ -815,7 +911,7 @@ static u64 __attribute__((ms_abi)) shim_WaitOnAddress(u64 addr, u64 expected, u6
|
||||
static u64 __attribute__((ms_abi)) shim_WakeByAddress(u64 addr) { (void)addr; return 0; }
|
||||
|
||||
/* ============================================================
|
||||
* user32.dll — 单窗口 + 消息泵(PS/2 键盘轮询)
|
||||
* user32.dll — 单窗口 + 消息泵(PS/2 键盘 + AUX 鼠标轮询)
|
||||
* ========================================================== */
|
||||
|
||||
#define SHIM_HWND 0x20000001ULL
|
||||
@@ -827,6 +923,19 @@ static u64 __attribute__((ms_abi)) shim_WakeByAddress(u64 addr) { (void)addr; re
|
||||
#define SHIM_WM_KEYDOWN 0x0100u
|
||||
#define SHIM_WM_KEYUP 0x0101u
|
||||
#define SHIM_WM_CHAR 0x0102u
|
||||
#define SHIM_WM_MOUSEMOVE 0x0200u
|
||||
#define SHIM_WM_LBUTTONDOWN 0x0201u
|
||||
#define SHIM_WM_LBUTTONUP 0x0202u
|
||||
#define SHIM_WM_RBUTTONDOWN 0x0204u
|
||||
#define SHIM_WM_RBUTTONUP 0x0205u
|
||||
#define SHIM_WM_MBUTTONDOWN 0x0207u
|
||||
#define SHIM_WM_MBUTTONUP 0x0208u
|
||||
/* wparam MK_* 修饰位 */
|
||||
#define SHIM_MK_LBUTTON 0x0001u
|
||||
#define SHIM_MK_RBUTTON 0x0002u
|
||||
#define SHIM_MK_SHIFT 0x0004u
|
||||
#define SHIM_MK_CONTROL 0x0008u
|
||||
#define SHIM_MK_MBUTTON 0x0010u
|
||||
|
||||
static u64 g_wnd_proc = 0;
|
||||
static int g_wnd_created = 0;
|
||||
@@ -846,8 +955,13 @@ typedef struct {
|
||||
static shim_msg_t g_msgq[SHIM_MSGQ_CAP];
|
||||
static int g_mq_head = 0, g_mq_count = 0;
|
||||
|
||||
static void shim_msg_push(u32 msg, u64 w, u64 l) {
|
||||
if (g_mq_count >= SHIM_MSGQ_CAP) return; /* 满则丢弃 */
|
||||
static u32 g_mq_drops = 0; /* 队列满丢弃计数(诊断) */
|
||||
|
||||
static void shim_msg_push_pt(u32 msg, u64 w, u64 l, i32 ptx, i32 pty) {
|
||||
if (g_mq_count >= SHIM_MSGQ_CAP) {
|
||||
if (g_mq_drops < 4) { g_mq_drops++; log_hex64("[shim] msgq drop msg=", msg); }
|
||||
return;
|
||||
}
|
||||
int idx = (g_mq_head + g_mq_count) % SHIM_MSGQ_CAP;
|
||||
g_msgq[idx].hwnd = SHIM_HWND;
|
||||
g_msgq[idx].message = msg;
|
||||
@@ -856,11 +970,15 @@ static void shim_msg_push(u32 msg, u64 w, u64 l) {
|
||||
g_msgq[idx].lparam = l;
|
||||
g_msgq[idx].time = 0;
|
||||
g_msgq[idx].pad1 = 0;
|
||||
g_msgq[idx].ptx = 0;
|
||||
g_msgq[idx].pty = 0;
|
||||
g_msgq[idx].ptx = ptx;
|
||||
g_msgq[idx].pty = pty;
|
||||
g_mq_count++;
|
||||
}
|
||||
|
||||
static void shim_msg_push(u32 msg, u64 w, u64 l) {
|
||||
shim_msg_push_pt(msg, w, l, 0, 0);
|
||||
}
|
||||
|
||||
/* PS/2 键盘状态 */
|
||||
static int s_kbd_shift = 0, s_kbd_ctrl = 0, s_kbd_alt = 0, s_kbd_e0 = 0;
|
||||
|
||||
@@ -895,9 +1013,15 @@ static u32 shim_scan_vk(u8 sc, int e0) {
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
if (sc >= 0x10 && sc <= 0x19) return 'Q' + (sc - 0x10);
|
||||
if (sc >= 0x1E && sc <= 0x26) return 'A' + (sc - 0x1E);
|
||||
if (sc >= 0x2C && sc <= 0x32) return 'Z' + (sc - 0x2C);
|
||||
/* 字母三行:scancode 顺序与 ASCII 序不一致(QWERTY 布局),且 'Z'+1='['
|
||||
* 在 ASCII 中不连续,必须用显式表,不能算术偏移(此前 shift-b 出 0x5E). */
|
||||
static const u8 vk_row_q[10] = { 'Q','W','E','R','T','Y','U','I','O','P' };
|
||||
static const u8 vk_row_a[9] = { 'A','S','D','F','G','H','J','K','L' };
|
||||
static const u8 vk_row_z[7] = { 'Z','X','C','V','B','N','M' };
|
||||
if (sc >= 0x10 && sc <= 0x19) return vk_row_q[sc - 0x10];
|
||||
if (sc >= 0x1E && sc <= 0x26) return vk_row_a[sc - 0x1E];
|
||||
if (sc >= 0x2C && sc <= 0x32) return vk_row_z[sc - 0x2C];
|
||||
/* 数字行 scancode 与 ASCII 均连续,算术偏移正确 */
|
||||
if (sc >= 0x02 && sc <= 0x0A) return '1' + (sc - 0x02);
|
||||
if (sc == 0x0B) return '0';
|
||||
switch (sc) {
|
||||
@@ -913,13 +1037,134 @@ static u32 shim_scan_vk(u8 sc, int e0) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 前向声明(shim_fb 定义在下方 RegisterClassExW 之后) */
|
||||
static const struct limine_framebuffer *shim_fb(void);
|
||||
|
||||
/* ============================================================
|
||||
* PS/2 AUX 鼠标(轮询模式,无 IRQ12)
|
||||
*
|
||||
* 初始化:on-demand(首次消息泵轮询时执行),启用 AUX 端口 +
|
||||
* 清配置字节 bit5(启用 AUX 时钟) + 发 0xF4 启用数据报告.
|
||||
* 全程超时保护:任何单步失败只记日志并永久禁用鼠标,绝不阻塞
|
||||
* GUI 主循环.包解码:3 字节(同步位 bit3 校验,9 位有符号位移,
|
||||
* 溢出位丢弃位移),坐标钳位到帧缓冲,边沿合成鼠标消息.
|
||||
* ========================================================== */
|
||||
|
||||
static int s_mouse_inited = 0;
|
||||
static int s_mouse_ok = 0;
|
||||
static i32 s_mouse_x = 512, s_mouse_y = 384; /* 屏幕坐标 */
|
||||
static u8 s_mouse_btn = 0; /* bit0=L bit1=R bit2=M */
|
||||
static u8 s_mpkt[3];
|
||||
static int s_mpkt_n = 0;
|
||||
static int s_dbg_aux = 0; /* 调试:前 16 个 AUX 字节计数 */
|
||||
static int s_dbg_pkt = 0; /* 调试:前 8 个完整包计数 */
|
||||
static int s_dbg_key = 0; /* 调试:前 16 个键盘消息计数(KEYDOWN/KEYUP/CHAR) */
|
||||
|
||||
static int ps2_wait_ibf_clear(u32 spins) {
|
||||
while (spins--) { if (!(inb(0x64) & 2)) return 0; }
|
||||
return -1;
|
||||
}
|
||||
static int ps2_wait_obf_set(u32 spins) {
|
||||
while (spins--) { if (inb(0x64) & 1) return 0; }
|
||||
return -1;
|
||||
}
|
||||
/* 经 0xD4 转发命令到鼠标并等 ACK(0xFA) */
|
||||
static int ps2_aux_cmd(u8 v) {
|
||||
if (ps2_wait_ibf_clear(100000) < 0) return -1;
|
||||
outb(0x64, 0xD4);
|
||||
if (ps2_wait_ibf_clear(100000) < 0) return -1;
|
||||
outb(0x60, v);
|
||||
if (ps2_wait_obf_set(100000) < 0) return -1;
|
||||
return inb(0x60) == 0xFA ? 0 : -1;
|
||||
}
|
||||
|
||||
static void shim_mouse_init_once(void) {
|
||||
if (s_mouse_inited) return;
|
||||
s_mouse_inited = 1;
|
||||
/* 排空控制器残留输出字节 */
|
||||
for (int i = 0; i < 16 && (inb(0x64) & 1); i++) (void)inb(0x60);
|
||||
/* 启用 AUX 端口 */
|
||||
if (ps2_wait_ibf_clear(100000) < 0) { log_warn("[shim] mouse: ctrl busy"); return; }
|
||||
outb(0x64, 0xA8);
|
||||
/* 读配置字节:清 bit5(启用 AUX 时钟),置 bit1(IRQ12 允许,轮询下无害) */
|
||||
if (ps2_wait_ibf_clear(100000) < 0) { log_warn("[shim] mouse: ctrl busy2"); return; }
|
||||
outb(0x64, 0x20);
|
||||
if (ps2_wait_obf_set(100000) < 0) { log_warn("[shim] mouse: no cfg byte"); return; }
|
||||
u8 cfg = inb(0x60);
|
||||
cfg = (u8)((cfg & ~0x20u) | 0x02u);
|
||||
if (ps2_wait_ibf_clear(100000) < 0) { log_warn("[shim] mouse: cfg wbusy"); return; }
|
||||
outb(0x64, 0x60);
|
||||
if (ps2_wait_ibf_clear(100000) < 0) { log_warn("[shim] mouse: cfg wbusy2"); return; }
|
||||
outb(0x60, cfg);
|
||||
/* 启用流模式数据报告 */
|
||||
if (ps2_aux_cmd(0xF4) < 0) { log_warn("[shim] mouse: enable-report nack"); return; }
|
||||
s_mouse_ok = 1;
|
||||
log_info("[shim] mouse init ok (AUX streaming, polled)");
|
||||
}
|
||||
|
||||
/* 鼠标 1 字节 -> 3 字节包 -> Win32 消息 */
|
||||
static void shim_mouse_byte(u8 b) {
|
||||
if (s_mpkt_n == 0 && !(b & 0x08)) return; /* byte0 同步位(bit3)必须为 1,否则丢弃重同步 */
|
||||
s_mpkt[s_mpkt_n++] = b;
|
||||
if (s_mpkt_n < 3) return;
|
||||
s_mpkt_n = 0;
|
||||
|
||||
u8 f = s_mpkt[0];
|
||||
i32 dx = (i32)s_mpkt[1] - ((f & 0x10) ? 0x100 : 0); /* 9 位有符号位移 */
|
||||
i32 dy = (i32)s_mpkt[2] - ((f & 0x20) ? 0x100 : 0);
|
||||
if (f & 0x40) dx = 0; /* X 溢出:位移不可信,只取按钮 */
|
||||
if (f & 0x80) dy = 0; /* Y 溢出 */
|
||||
if (s_dbg_pkt < 8) {
|
||||
s_dbg_pkt++;
|
||||
log_hex64("[shim] mpkt f=", f);
|
||||
log_hex64("[shim] mpkt dx=", (u64)(i64)dx);
|
||||
log_hex64("[shim] mpkt dy=", (u64)(i64)dy);
|
||||
}
|
||||
|
||||
const struct limine_framebuffer *fb = shim_fb();
|
||||
i32 fw = fb ? (i32)fb->width : 1024;
|
||||
i32 fh = fb ? (i32)fb->height : 768;
|
||||
i32 nx = s_mouse_x + dx;
|
||||
i32 ny = s_mouse_y - dy; /* PS/2 Y 向前(屏幕上方向)为正 -> 屏幕坐标取反 */
|
||||
if (nx < 0) nx = 0; else if (nx >= fw) nx = fw - 1;
|
||||
if (ny < 0) ny = 0; else if (ny >= fh) ny = fh - 1;
|
||||
|
||||
u8 nbtn = (u8)(f & 0x07);
|
||||
i32 cx = nx - g_wnd_x, cy = ny - g_wnd_y; /* 客户区坐标(消息 lparam) */
|
||||
u64 lp = (u64)(u32)(((u32)(u16)cy << 16) | (u32)(u16)cx);
|
||||
u64 mk = 0;
|
||||
if (nbtn & 1) mk |= SHIM_MK_LBUTTON;
|
||||
if (nbtn & 2) mk |= SHIM_MK_RBUTTON;
|
||||
if (nbtn & 4) mk |= SHIM_MK_MBUTTON;
|
||||
if (s_kbd_shift) mk |= SHIM_MK_SHIFT;
|
||||
if (s_kbd_ctrl) mk |= SHIM_MK_CONTROL;
|
||||
|
||||
if (nx != s_mouse_x || ny != s_mouse_y) {
|
||||
s_mouse_x = nx; s_mouse_y = ny;
|
||||
shim_msg_push_pt(SHIM_WM_MOUSEMOVE, mk, lp, nx, ny);
|
||||
}
|
||||
/* 按钮边沿(按钮事件罕见,逐一打点:msg/客户区坐标) */
|
||||
if ((nbtn & 1) && !(s_mouse_btn & 1)) { shim_msg_push_pt(SHIM_WM_LBUTTONDOWN, mk, lp, nx, ny); log_hex64("[shim] LDOWN lp=", lp); }
|
||||
if (!(nbtn & 1) && (s_mouse_btn & 1)) { shim_msg_push_pt(SHIM_WM_LBUTTONUP, mk, lp, nx, ny); log_hex64("[shim] LUP lp=", lp); }
|
||||
if ((nbtn & 2) && !(s_mouse_btn & 2)) shim_msg_push_pt(SHIM_WM_RBUTTONDOWN, mk, lp, nx, ny);
|
||||
if (!(nbtn & 2) && (s_mouse_btn & 2)) shim_msg_push_pt(SHIM_WM_RBUTTONUP, mk, lp, nx, ny);
|
||||
if ((nbtn & 4) && !(s_mouse_btn & 4)) shim_msg_push_pt(SHIM_WM_MBUTTONDOWN, mk, lp, nx, ny);
|
||||
if (!(nbtn & 4) && (s_mouse_btn & 4)) shim_msg_push_pt(SHIM_WM_MBUTTONUP, mk, lp, nx, ny);
|
||||
s_mouse_btn = nbtn;
|
||||
}
|
||||
|
||||
/* 排空 PS/2 输出缓冲,转成 Win32 消息入队 */
|
||||
static void shim_kbd_poll(void) {
|
||||
shim_mouse_init_once();
|
||||
for (;;) {
|
||||
u8 st = inb(0x64);
|
||||
if (!(st & 1)) break;
|
||||
u8 sc = inb(0x60);
|
||||
if (st & 0x20) continue; /* 鼠标字节丢弃 */
|
||||
if (st & 0x20) {
|
||||
if (s_dbg_aux < 16) { s_dbg_aux++; log_hex64("[shim] aux byte=", sc); }
|
||||
if (s_mouse_ok) shim_mouse_byte(sc);
|
||||
continue;
|
||||
}
|
||||
if (sc == 0xE0) { s_kbd_e0 = 1; continue; }
|
||||
int e0 = s_kbd_e0;
|
||||
if (!e0) {
|
||||
@@ -933,18 +1178,25 @@ static void shim_kbd_poll(void) {
|
||||
s_kbd_e0 = 0;
|
||||
if (sc & 0x80) {
|
||||
u32 vk = shim_scan_vk(sc & 0x7F, e0);
|
||||
if (vk) shim_msg_push(SHIM_WM_KEYUP, vk, sc);
|
||||
if (vk) {
|
||||
shim_msg_push(SHIM_WM_KEYUP, vk, sc);
|
||||
if (s_dbg_key < 16) { s_dbg_key++; log_hex64("[shim] KEYUP vk=", vk); }
|
||||
}
|
||||
continue;
|
||||
}
|
||||
u32 vk = shim_scan_vk(sc, e0);
|
||||
if (vk) {
|
||||
shim_msg_push(SHIM_WM_KEYDOWN, vk, sc);
|
||||
if (s_dbg_key < 16) { s_dbg_key++; log_hex64("[shim] KEYDOWN vk=", vk); }
|
||||
/* Esc -> 额外合成 WM_CLOSE:无鼠标环境下关闭窗口的唯一途径 */
|
||||
if (vk == 0x1B && !e0) shim_msg_push(SHIM_WM_CLOSE, 0, 0);
|
||||
}
|
||||
if (!e0) {
|
||||
char c = shim_scan_ascii(sc, s_kbd_shift);
|
||||
if (c >= ' ' && c < 0x7F) shim_msg_push(SHIM_WM_CHAR, (u64)(u8)c, sc);
|
||||
if (c >= ' ' && c < 0x7F) {
|
||||
shim_msg_push(SHIM_WM_CHAR, (u64)(u8)c, sc);
|
||||
if (s_dbg_key < 16) { s_dbg_key++; log_hex64("[shim] CHAR ch=", (u64)(u8)c); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1005,7 +1257,7 @@ static u64 __attribute__((ms_abi)) shim_SetCursor(u64 cur) {
|
||||
(void)cur; return SHIM_HCURSOR;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_GetCursorPos(u64 pt_p) {
|
||||
if (pt_p) { ((i32 *)pt_p)[0] = g_wnd_w / 2; ((i32 *)pt_p)[1] = g_wnd_h / 2; }
|
||||
if (pt_p) { ((i32 *)pt_p)[0] = s_mouse_x; ((i32 *)pt_p)[1] = s_mouse_y; }
|
||||
return 1;
|
||||
}
|
||||
static u64 __attribute__((ms_abi)) shim_ScreenToClient(u64 hwnd, u64 pt_p) {
|
||||
@@ -1163,6 +1415,50 @@ static u64 __attribute__((ms_abi)) shim_GdiFlush(void) { return 1; }
|
||||
|
||||
static u64 g_blit_count = 0;
|
||||
|
||||
/* 12x12 箭头软件光标:'X'=黑边 '#'=白心 '.'=透明.
|
||||
* 设计:BitBlt 上屏后叠画;egui 每帧全量重绘,下一帧 BitBlt 自动覆盖旧光标,
|
||||
* 无需保存/恢复背景.只在窗口矩形内绘制(越界裁剪),避免屏外残影. */
|
||||
static const char *const g_cursor_bmp[12] = {
|
||||
"X...........",
|
||||
"XX..........",
|
||||
"X#X.........",
|
||||
"X##X........",
|
||||
"X###X.......",
|
||||
"X####X......",
|
||||
"X#####X.....",
|
||||
"X######X....",
|
||||
"X#######X...",
|
||||
"X########X..",
|
||||
"X####XXXX...",
|
||||
"XXXX........",
|
||||
};
|
||||
|
||||
static void shim_draw_cursor(const struct limine_framebuffer *fb) {
|
||||
if (!s_mouse_ok || !g_wnd_created) return;
|
||||
i32 wx1 = g_wnd_x + g_wnd_w, wy1 = g_wnd_y + g_wnd_h;
|
||||
if (s_mouse_x < g_wnd_x || s_mouse_y < g_wnd_y) return;
|
||||
if (s_mouse_x >= wx1 || s_mouse_y >= wy1) return;
|
||||
u8 *fbb = (u8 *)fb->address;
|
||||
for (i32 r = 0; r < 12; r++) {
|
||||
i32 fy = s_mouse_y + r;
|
||||
if (fy >= wy1 || fy >= (i32)fb->height) break;
|
||||
u8 *rowb = fbb + (u64)fy * fb->pitch;
|
||||
for (i32 c = 0; c < 12; c++) {
|
||||
char ch = g_cursor_bmp[r][c];
|
||||
if (ch != 'X' && ch != '#') continue;
|
||||
i32 fx = s_mouse_x + c;
|
||||
if (fx >= wx1 || fx >= (i32)fb->width) break;
|
||||
if (fb->bpp == 32) {
|
||||
((u32 *)rowb)[fx] = (ch == '#') ? 0xFFFFFFFFu : 0xFF000000u;
|
||||
} else {
|
||||
u8 v = (ch == '#') ? 0xFF : 0x00;
|
||||
u8 *p = rowb + (u64)fx * 3;
|
||||
p[0] = v; p[1] = v; p[2] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u64 __attribute__((ms_abi)) shim_BitBlt(u64 dstdc, u64 x, u64 y, u64 cx, u64 cy,
|
||||
u64 srcdc, u64 x1, u64 y1, u64 rop) {
|
||||
(void)rop;
|
||||
@@ -1216,9 +1512,19 @@ static u64 __attribute__((ms_abi)) shim_BitBlt(u64 dstdc, u64 x, u64 y, u64 cx,
|
||||
}
|
||||
}
|
||||
}
|
||||
shim_draw_cursor(fb);
|
||||
g_blit_count++;
|
||||
if (g_blit_count == 1) log_info("[shim] BitBlt first frame on screen");
|
||||
if ((g_blit_count % 600) == 0) log_hex64("[shim] BitBlt frames=", g_blit_count);
|
||||
if ((g_blit_count % 600) == 0) {
|
||||
log_hex64("[shim] BitBlt frames=", g_blit_count);
|
||||
/* 堆健康度:live 逐周期线性增长=真泄漏;free_max 持续萎缩=碎片化 */
|
||||
u64 fm = 0;
|
||||
for (heap_blk *f = g_heap_free_head; f; f = (heap_blk *)f->next_free) {
|
||||
if (f->size > fm) fm = f->size;
|
||||
}
|
||||
log_hex64("[shim] heap live=", g_heap_live);
|
||||
log_hex64("[shim] heap free_max=", fm);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+23
-3
@@ -49,9 +49,22 @@ void pe_shim_init(const char *cmdline);
|
||||
* 64位和32位共用。exit_code = 进程退出码。 */
|
||||
void pe_shim_exit_process(u64 exit_code);
|
||||
|
||||
/* pe_run 调用:保存上下文(__builtin_setjmp)。
|
||||
* 返回 0 = 首次(继续执行 PE),1 = ExitProcess 触发(longjmp 回来)。 */
|
||||
int pe_shim_setup_exit(void);
|
||||
/* 获取 ExitProcess longjmp 目标 jmpbuf,并重置退出状态。
|
||||
*
|
||||
* BUG-20260801-006:__builtin_setjmp 必须出现在 PE 执行全程存活的
|
||||
* 栈帧内(pe_service_run 本体)。此前由 pe_shim_setup_exit() 包装
|
||||
* 调用——该函数调用 setjmp 后即返回,C 标准明确规定"含 setjmp 的
|
||||
* 函数已返回后再 longjmp"为未定义行为(实测 longjmp 尾声从脏栈
|
||||
* pop 出 log_hex64 缓冲区 ASCII 残片充当 R12-R15/RBP → 野指针
|
||||
* call → 三重故障)。
|
||||
*
|
||||
* 调用方标准写法(setjmp 直接出现在调用方函数本体内):
|
||||
* if (__builtin_setjmp(pe_shim_exit_jmpbuf()) == 0) {
|
||||
* ... 执行 PE ...
|
||||
* } else {
|
||||
* code = pe_shim_get_exit_code();
|
||||
* } */
|
||||
void **pe_shim_exit_jmpbuf(void);
|
||||
|
||||
/* 获取上次 ExitProcess 的退出码 */
|
||||
u64 pe_shim_get_exit_code(void);
|
||||
@@ -62,4 +75,11 @@ int pe_shim_is_exit_call(u64 target);
|
||||
/* 获取 cmdline(shim 内部用) */
|
||||
const char *pe_shim_get_cmdline(void);
|
||||
|
||||
/* BUG-20260801-007:设置 PE32 解释器内存基址(guest→host 指针转换)。
|
||||
* PE32 解释器路径下,fn64 回退 shim 收到的指针参数是 32 位 guest VA
|
||||
* (相对解释器 mem 基址的偏移),直接当 host 指针解引用 → 低地址 #PF
|
||||
* → 三重故障。执行 PE32 前设置 base=mem,shim 内部 gp() 在指针解引用点
|
||||
* 完成转换;base=0 表示非解释器上下文(原生 PE32+ 路径),gp() 恒等映射。 */
|
||||
void pe_shim_set_emu_base(u64 base);
|
||||
|
||||
#endif /* PE_SHIM_H */
|
||||
|
||||
@@ -310,8 +310,15 @@ static int try_iat_call(x86emu_state *emu, u32 target) {
|
||||
ret = se->fn32(esp_ptr);
|
||||
} else if (se->fn64) {
|
||||
/* 回退:调用 64位 shim(最多 4 个参数)。
|
||||
* MS x64 ABI 由 C 编译器处理寄存器分配。 */
|
||||
typedef u64 (*fn4_t)(u64, u64, u64, u64);
|
||||
* BUG-20260801-007 后续修正:fn4_t 必须显式 ms_abi。
|
||||
* UTSM 以 clang x86_64-unknown-none 编译,C 默认 ABI 是 SysV
|
||||
* (arg1=RDI/RSI/RDX/RCX),而 shim 全部 __attribute__((ms_abi))
|
||||
* (arg1=RCX/RDX/R8/R9)。typedef 不带 ms_abi 时编译器生成 SysV
|
||||
* 调用序列 → shim 在 RCX 读到的是 SysV 第4参数 a3(=0) → gp(0)=0
|
||||
* → shim_printf 解引用 NULL → #PF cr2=0(实测寄存器现场:
|
||||
* rdi=0x401017 即 SysV arg1=fmt, rcx=0, cr2=0)。
|
||||
* 原生 PE32+ 路径 guest 是 Windows 程序天生 MS ABI,不受影响。 */
|
||||
typedef u64 (__attribute__((ms_abi)) *fn4_t)(u64, u64, u64, u64);
|
||||
fn4_t f = (fn4_t)se->fn64;
|
||||
ret = f(a0, a1, a2, a3);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user