diff --git a/VKernel/Kernel/intr/isr.s b/VKernel/Kernel/intr/isr.s index e2d48d9..3e88b07 100644 --- a/VKernel/Kernel/intr/isr.s +++ b/VKernel/Kernel/intr/isr.s @@ -15,13 +15,6 @@ # (读 RIP/CS/RFLAGS、算 is_user),若不保存就会破坏被中断代码在这些寄存器里的 # 活跃值 → 概率性内存破坏/异常。出口(iretq 前)恢复。 .comm _isr_scratch, 48, 8 -# 【诊断·临时,定位后必须删除】被中断代码的 RIP/CS/RSP。ASR_NOERR/ISR_ERR 在 -# _frame_ready 里写入(此时 r8=RIP / r9=CS / rax=RSP / r11=SS)。timer.vp 的心跳 -# 探针读它们,用来定位"某个线程卡在用户态哪条指令上"。定时器 1kHz 触发,所以这三个 -# 值几乎总是最后一次定时中断的现场。 -.comm _intr_rip, 8, 8 -.comm _intr_cs, 8, 8 -.comm _intr_rsp, 8, 8 .section .text .intel_syntax noprefix @@ -113,10 +106,6 @@ isr\num\()_from_user: isr\num\()_frame_ready: # r8=RIP, r9=CS, r10=RFLAGS, rax=RSP(0 if kernel), r11=SS(0 if kernel) - # 【诊断·临时】记下被中断代码的 RIP/CS/RSP(只读 r8/r9/rax,不破坏后面要压栈的值) - mov [_intr_rip], r8 - mov [_intr_cs], r9 - mov [_intr_rsp], rax # Build uniform frame on stack for iretq later push r11 # SS push rax # RSP @@ -315,10 +304,6 @@ isr\num\()_from_user: isr\num\()_frame_ready: # rdx=error_code, r8=RIP, r9=CS, r10=RFLAGS, rax=RSP, r11=SS - # 【诊断·临时】同 ISR_NOERR:记下被中断代码的 RIP/CS/RSP - mov [_intr_rip], r8 - mov [_intr_cs], r9 - mov [_intr_rsp], rax # Build uniform frame on stack push r11 # SS push rax # RSP @@ -569,8 +554,17 @@ isr80: # 取 CS/RSP 都要拿寄存器当临时,所以先把 **调用号(rax) 与 arg4(r10)** 压栈保住: # 原实现直接用它们当临时(`mov rax,[rsp+8]` 取 CS、`mov r10,[rsp+16]` 取 RFLAGS), # 后面 `push rax`/`push r10` 存进保存块的其实是 CS/RFLAGS → handler 收到乱掉的 n 与 a4。 - push rax # [S+0] = 调用号 + # + # 【顺序绝对不能反】栈向下增长,先压的落在**高**地址。本块要的布局(S = 两次 push + # 后的 rsp = 入口 rsp-16,与文件头/下面的注释一致)是 [S+0]=调用号(rax)、[S+8]=arg4(r10), + # 所以必须**先 push r10、后 push rax**。若反了:下面 `mov rax,[rsp+40]`(= [S+0])取到 + # arg4 当调用号、`mov r8,[rsp+168]`(= [S+8])取到调用号当 arg4 ⇒ syscall_handler 拿到 + # 错误的 n。本路径在修好之前**没有任何调用者**(vpsdk 全走 `syscall` 指令),所以从未暴露; + # 现场是用户线程出口跳板(alloc_user_thread 映射的 `mov eax,34; int 0x80; jmp $`) + # rax=34(THREAD_EXIT) 但 n 错 ⇒ 不匹配任何分支 ⇒ 返回 0 ⇒ iretq 回用户态 ⇒ 卡在 `jmp $` + # 空转 ⇒ 该线程永不再 yield ⇒ needs_reschedule 无人消费 ⇒ 全系统不再切换(桌面饿死)。 push r10 # [S+8] = arg4 + push rax # [S+0] = 调用号 test byte ptr [rsp + 24], 3 # 入口帧 CS([S+24])的 RPL;内存操作数,不占用寄存器 jz isr80_frame_ready # 内核态入口:无需 swapgs swapgs diff --git a/VKernel/Kernel/platform/pch/timer.vp b/VKernel/Kernel/platform/pch/timer.vp index 51e9b7f..e479f0d 100644 --- a/VKernel/Kernel/platform/pch/timer.vp +++ b/VKernel/Kernel/platform/pch/timer.vp @@ -12,78 +12,7 @@ define MILLISECONDS_PER_TICK = (1000 / TIMER_FREQUENCY) static ticks: volatile uint64 = 0 static seconds: volatile uint32 = 0 static milliseconds: volatile uint32 = 0 -static timer_running: volatile bool = False - -# ============================================================================ -# 【诊断·临时,定位后必须删除】IRQ0 心跳 + 调度器快照。 -# -# 症状:某个 app 退出([syscall] exit called)之后 [perf]/[chk] 永不再打印。 -# 桌面线程的渲染/打印完全依赖调度器切它回来,而调度器只在 needs_reschedule -# 被 sched_tick(IRQ0)置位、且有线程调用 try_reschedule 时才切换 ⇒ 三种可能: -# A) 本探针也一起停 → IRQ0 / EOI 死了(定时器不再推进) -# B) 探针继续、cur 恒定且该 tid 的 st=1(RUNNING) → 那个线程死循环且不再调用带 -# try_reschedule 的 syscall ⇒ needs_reschedule 无人消费 ⇒ 全系统不再切换 -# C) 探针继续、cur 在变 → 调度器活着,问题在桌面线程自身 -# -# 直接写 COM1 0x3F8(自带 LSR 超时,绝不依赖 serial 驱动、绝不挂住 ISR)。 -# 每行格式(`_intr_*` 由 isr.s 在每次中断入口写入 = 最后一次定时中断打断的那条指令): -# `[t] <各线程 state 1 位hex> ` -# 卡死线程的现场就是 intr_rip/intr_cs:cs=2B 表示卡在用户态该 rip 处,cs=08 表示卡在内核。 -# ============================================================================ -_dbg_tick: uint64 = 0 - -def _dbg_putc(ch: uint8): - tmo: int = 200000 - while tmo > 0: - if (asm.inb(0x3FD) & 0x20) != 0: - break - tmo -= 1 - asm.outb(0x3F8, ch) - -def _dbg_hex32(v: uint64): - sh: int = 28 - while sh >= 0: - d: int = int((v >> uint64(sh)) & uint64(15)) - if d < 10: - _dbg_putc(uint8(48 + d)) - else: - _dbg_putc(uint8(55 + d)) - sh -= 4 - -def _dbg_snapshot(): - s: sched.Scheduler* = sched._sched_ptr - _dbg_putc(uint8(10)) # \n - _dbg_putc(uint8(91)) # [ - _dbg_putc(uint8(116)) # t - _dbg_putc(uint8(93)) # ] - _dbg_putc(uint8(32)) # ' ' - _dbg_hex32(uint64(ticks)) - _dbg_putc(uint8(32)) - _dbg_hex32(uint64(s.current_tid)) - _dbg_putc(uint8(32)) - _dbg_hex32(uint64(s.thread_count)) - _dbg_putc(uint8(32)) - i: int - for i in range(s.thread_count): - st: int = s.threads[i].state - if st < 10: - _dbg_putc(uint8(48 + st)) - else: - _dbg_putc(uint8(55 + st)) - # 被中断代码的 RIP/CS/RSP(isr.s 写入 _intr_*)——定位"卡在哪条指令"的关键。 - ir: uint64 = 0 - ic: uint64 = 0 - iu: uint64 = 0 - __asm__(f"mov {asmout(ir, ASM_DESCR.OUTPUT_REG)}, [_intr_rip]", op=[ASM_DESCR.CLOBBER_MEMORY]) - __asm__(f"mov {asmout(ic, ASM_DESCR.OUTPUT_REG)}, [_intr_cs]", op=[ASM_DESCR.CLOBBER_MEMORY]) - __asm__(f"mov {asmout(iu, ASM_DESCR.OUTPUT_REG)}, [_intr_rsp]", op=[ASM_DESCR.CLOBBER_MEMORY]) - _dbg_putc(uint8(32)) - _dbg_hex32(ir) - _dbg_putc(uint8(32)) - _dbg_hex32(ic) - _dbg_putc(uint8(32)) - _dbg_hex32(iu) - _dbg_putc(uint8(10)) +static timer_running: volatile bool = False def timer_init(): global ticks, seconds, milliseconds, timer_running @@ -96,7 +25,7 @@ def timer_init(): pit.pit_init(TIMER_FREQUENCY) def timer_handler(): - global ticks, seconds, milliseconds, timer_running, _dbg_tick + global ticks, seconds, milliseconds, timer_running if not timer_running: pic.eoi(0) apic.eoi() @@ -114,10 +43,6 @@ def timer_handler(): # 只发 PIC EOI 不清 LAPIC ISR → 首个定时中断后后续中断被屏蔽 → # ticks 不推进 → msleep 永久挂起(QEMU/OVMF 下 APIC 必启用)。 apic.eoi() - # 【诊断·临时】每 1000 tick(≈1s)打一行心跳+调度器快照,见文件头说明。 - _dbg_tick = _dbg_tick + 1 - if (_dbg_tick % 1000) == 0: - _dbg_snapshot() def timer_start(): global timer_running