fix(kernel): KRNBUG-KE-001 — real KeResumeThread per canary mirror
Replace the no-op cookie-returner with a real impl per canary xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc:216-227 (XObject::GetNativeObject<XThread>()->Resume()). Mirrors nt_resume_thread plumbing two functions below: resolve_pseudo_handle -> scheduler.find_by_handle -> resume_ref. Returns STATUS_SUCCESS if the KTHREAD-pointer-as-handle resolves, STATUS_INVALID_HANDLE otherwise — matches canary's Resume()/!thread return semantics. Cascade-prediction scorecard (audit-018 -> post-fix): - A PASS: tids 9 (entry=0x824D2878) and 10 (entry=0x824D2940) leave Suspended -> run prologue -> park on audio buffer-completion semaphores 0x828A3254 / 0x828A3230. - B PARTIAL FAIL: NtSetEvent 667->3334; KeReleaseSemaphore=0; XAudioSubmitRenderDriverFrame=0. - C FAIL (predicted 2->1, actual 2->2): both ExTerminateThread + KeReleaseSemaphore still canary-only. - D FAIL: gamma-cluster blocker unchanged — pc-probe at 0x82184318/0x82184374 no fires; dump-addr 0x828F4070 no DUMP; signal_attempts on 0x1004/0x100c/0x1020/0x15e4 still 0. Necessary-but-not-sufficient: workers unsuspend but park on a downstream gate that's part of the audit-009/-016/-017 gamma cluster. Tests 600 -> 601 (+ke_resume_thread_unblocks_suspended_worker). Lockstep instructions=100000003 imports=987516 deterministic x2. Goldens re-baselined: sylpheed_n50m.json instructions 50000003->50000011, imports 407255->407247. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3656,11 +3656,16 @@ fn nt_yield_execution(ctx: &mut PpcContext, _mem: &GuestMemory, _state: &mut Ker
|
||||
}
|
||||
|
||||
fn ke_resume_thread(ctx: &mut PpcContext, _mem: &GuestMemory, state: &mut KernelState) {
|
||||
// r3 = thread_ptr (KTHREAD). We don't track KTHREAD ↔ HW mapping through
|
||||
// guest memory addresses, so accept and succeed. Real NtResumeThread
|
||||
// below handles the handle-based path properly.
|
||||
ctx.gpr[3] = 0;
|
||||
let _ = state;
|
||||
let handle = resolve_pseudo_handle(state, ctx.gpr[3] as u32);
|
||||
match state.scheduler.find_by_handle(handle) {
|
||||
Some(r) => {
|
||||
state.scheduler.resume_ref(r);
|
||||
ctx.gpr[3] = STATUS_SUCCESS;
|
||||
}
|
||||
None => {
|
||||
ctx.gpr[3] = STATUS_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nt_resume_thread(ctx: &mut PpcContext, mem: &GuestMemory, state: &mut KernelState) {
|
||||
@@ -3983,6 +3988,52 @@ mod tests {
|
||||
assert_eq!(ctx.gpr[3], 7);
|
||||
}
|
||||
|
||||
/// `KeResumeThread` resolves the KTHREAD-pointer-as-handle, decrements the
|
||||
/// target's suspend count, and unblocks once it hits zero. Mirrors
|
||||
/// xboxkrnl_threading.cc:216-227 (XObject::GetNativeObject<XThread> +
|
||||
/// thread->Resume()).
|
||||
#[test]
|
||||
fn ke_resume_thread_unblocks_suspended_worker() {
|
||||
use xenia_cpu::scheduler::{BlockReason, HwState, SpawnParams};
|
||||
let (mut ctx, mut mem, mut state) = fresh();
|
||||
let pcr_base = SCRATCH_BASE + 0x500;
|
||||
let params = SpawnParams {
|
||||
entry: 0x8200_0000,
|
||||
start_context: 0,
|
||||
stack_base: 0x7200_0000,
|
||||
stack_size: 0x10000,
|
||||
pcr_base,
|
||||
tls_base: 0,
|
||||
thread_handle: 0x2000,
|
||||
guest_tid: 42,
|
||||
create_suspended: true,
|
||||
is_initial: false,
|
||||
tls_slot_count: 0,
|
||||
affinity_mask: 0b0000_0010,
|
||||
priority: 0,
|
||||
ideal_processor: None,
|
||||
};
|
||||
state
|
||||
.scheduler
|
||||
.spawn(params, &mut crate::state::GuestMemoryPcr(&mut mem))
|
||||
.unwrap();
|
||||
let r = state.scheduler.find_by_handle(0x2000).expect("spawned");
|
||||
assert_eq!(
|
||||
state.scheduler.thread(r).state,
|
||||
HwState::Blocked(BlockReason::Suspended)
|
||||
);
|
||||
|
||||
ctx.gpr[3] = 0x2000;
|
||||
ke_resume_thread(&mut ctx, &mem, &mut state);
|
||||
assert_eq!(ctx.gpr[3], STATUS_SUCCESS);
|
||||
let r = state.scheduler.find_by_handle(0x2000).expect("still alive");
|
||||
assert_eq!(state.scheduler.thread(r).state, HwState::Ready);
|
||||
|
||||
ctx.gpr[3] = 0xDEAD_BEEF;
|
||||
ke_resume_thread(&mut ctx, &mem, &mut state);
|
||||
assert_eq!(ctx.gpr[3], STATUS_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
/// The regression we're guarding against: Sylpheed parks a thread on the
|
||||
/// event it handed to `NtReadFile`. Historically our HLE ignored r4 and
|
||||
/// left the event unsignaled — the wait never released. Completion must
|
||||
|
||||
Reference in New Issue
Block a user