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>
69 lines
2.6 KiB
Rust
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,
|
|
})
|
|
}
|