Files
xenia-rs/crates/xenia-kernel/src/thread.rs
MechaCat02 5f0d6487ea xenia-kernel: HLE expansion, scheduler integration, audit + UI bridge
Major HLE buildout in exports.rs: KeInitializeSemaphore now seeds
count/limit, XexGet{Module,Procedure}Address use distinct
HMODULE_XBOXKRNL/HMODULE_XAM pseudo-handles with a reverse
(ModuleId,ordinal)→thunk_addr map, plus sweeping additions across
sync primitives, file I/O, semaphores, events, threads, and
allocator paths needed to advance Sylpheed past VdSwap=2.

New modules:
  - thread.rs   — ThreadRef + per-thread suspension/wake plumbing
  - interrupts.rs — IRQ delivery, pending-IRQ slots, IPI helpers
  - path.rs     — guest path normalization (D:\\, game:\\, etc.)
  - audit.rs    — --trace-handles harness backing the handle audit
  - ui_bridge.rs — kernel-side endpoint of the xenia-ui bridge
                   (input snapshots, framebuffer publish handles)

state.rs grows to own the HW-slot scheduler state, the new audit /
UI bridge handles, and the per-handle reverse maps. xam.rs and
objects.rs follow suit for the HLE additions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 16:29:00 +02:00

69 lines
2.6 KiB
Rust

//! Guest-thread image allocation — shared by the initial thread setup in
//! `xenia-app/src/main.rs` and `ExCreateThread`. Stack, PCR, and TLS blocks
//! all come from the existing kernel bump allocators so layout is consistent.
use xenia_memory::{GuestMemory, MemoryAccess};
use crate::state::KernelState;
/// Addresses the caller passes to `Scheduler::spawn` / the initial-thread
/// setup. Matches xenia-canary's per-thread allocations: a stack, a PCR, and
/// a TLS block.
#[derive(Debug, Clone, Copy)]
pub struct ThreadImage {
pub stack_base: u32,
pub stack_size: u32,
pub pcr_base: u32,
pub tls_base: u32,
}
/// Allocate stack + PCR + TLS for one guest thread and initialize the PCR
/// fields that games read in their thread prolog.
///
/// - Stack comes from `KernelState::stack_alloc` (bump allocator at
/// 0x7100_0000 upward). The returned base is the *bottom*; callers
/// compute SP as `base + size`.
/// - PCR and TLS are fixed 4 KiB pages allocated via `heap_alloc` so they
/// land in the user heap region together with other kernel metadata.
/// - `hw_thread_id` is written at PCR+0x2C so `KeGetCurrentProcessorNumber`-
/// style reads from r13 resolve correctly even though we never register
/// that export.
pub fn allocate_thread_image(
kernel: &mut KernelState,
mem: &GuestMemory,
stack_size: u32,
hw_thread_id: u8,
) -> Option<ThreadImage> {
// Round stack size to a page and give games a minimum that matches
// xenia-canary's 16 MiB default when callers request 0 (common for
// ExCreateThread when the caller lets the kernel pick).
let stack_size = if stack_size == 0 {
0x10_0000
} else {
(stack_size + 0xFFF) & !0xFFF
};
// stack_alloc returns top-of-stack; we need the base.
let stack_top = kernel.stack_alloc(stack_size, mem)?;
let stack_base = stack_top - stack_size;
let pcr_base = kernel.heap_alloc(0x1000, mem)?;
let tls_base = kernel.heap_alloc(0x1000, mem)?;
// PCR layout (canary xboxkrnl/xboxkrnl_module.cc, simplified):
// +0x000 tls_ptr → TLS block base
// +0x02C current_processor_id → HW thread id (0..5)
// +0x100 current_thread → placeholder non-zero tag
// +0x150 dpc_active → 0 (no DPC queued)
mem.write_u32(pcr_base, tls_base);
mem.write_u32(pcr_base + 0x2C, hw_thread_id as u32);
mem.write_u32(pcr_base + 0x100, 0x1000);
mem.write_u32(pcr_base + 0x150, 0);
Some(ThreadImage {
stack_base,
stack_size,
pcr_base,
tls_base,
})
}