Initial commit: xenia-rs workspace for Xbox 360 RE

Rust reimplementation of the xenia Xbox 360 emulator targeting reverse-
engineering and preservation, initially scoped to Project Sylpheed.

Includes:
- XEX2 loader (LZX decompression, AES decryption, PE parsing)
- XISO / XGD2 disc image VFS
- PPC interpreter with 200+ opcodes and VMX128 decoding
- Static analyzer: functions, cross-references, labels, asm + SQLite output
- HLE kernel covering the xboxkrnl/xam subset used by Sylpheed init
- Debugger with in-memory and SQLite-backed execution tracing
- `xenia-rs` CLI with extract/dis/exec commands that produce cumulative,
  superset SQLite databases and opt-in instruction/import/branch traces

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-04-16 23:11:49 +02:00
commit c694bb3f43
63 changed files with 13456 additions and 0 deletions

View File

@@ -0,0 +1,159 @@
use std::collections::HashMap;
use xenia_cpu::PpcContext;
use xenia_memory::GuestMemory;
use crate::objects::KernelObject;
/// Function signature for HLE kernel exports.
pub type KernelExportFn = fn(&mut PpcContext, &mut GuestMemory, &mut KernelState);
/// Module identifier for kernel exports.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ModuleId {
Xboxkrnl,
Xam,
Xbdm,
}
/// Central kernel state tracking all guest OS state.
pub struct KernelState {
exports: HashMap<(ModuleId, u32), (&'static str, KernelExportFn)>,
next_handle: u32,
pub tls_slots: HashMap<u32, u64>,
next_tls_index: u32,
/// Kernel object table: handle → object
pub objects: HashMap<u32, KernelObject>,
/// Bump allocator for guest heap (NtAllocateVirtualMemory etc.)
pub heap_cursor: u32,
/// Stack allocator cursor for MmCreateKernelStack
pub stack_cursor: u32,
/// GPU command buffer address (set by VdGetSystemCommandBuffer)
pub gpu_command_buffer: u32,
/// Image base of the loaded XEX (for XexExecutableModuleHandle etc.)
pub image_base: u32,
/// Next thread ID
pub next_thread_id: u32,
}
impl KernelState {
pub fn new() -> Self {
let mut state = Self {
exports: HashMap::new(),
next_handle: 0x1000,
tls_slots: HashMap::new(),
next_tls_index: 0,
objects: HashMap::new(),
heap_cursor: 0x4000_0000, // Start of user heap region
stack_cursor: 0x7100_0000, // Above main stack
gpu_command_buffer: 0,
image_base: 0,
next_thread_id: 1,
};
crate::exports::register_exports(&mut state);
crate::xam::register_exports(&mut state);
state
}
pub fn register_export(
&mut self,
module: ModuleId,
ordinal: u32,
name: &'static str,
func: KernelExportFn,
) {
self.exports.insert((module, ordinal), (name, func));
}
pub fn call_export(
&mut self,
module: ModuleId,
ordinal: u32,
ctx: &mut PpcContext,
mem: &mut GuestMemory,
) -> bool {
if let Some(&(name, func)) = self.exports.get(&(module, ordinal)) {
tracing::info!(
"Kernel call: {:?}:{:#x} ({}) args=[{:#x}, {:#x}, {:#x}, {:#x}]",
module, ordinal, name,
ctx.gpr[3], ctx.gpr[4], ctx.gpr[5], ctx.gpr[6]
);
func(ctx, mem, self);
tracing::info!(" -> returned {:#x}", ctx.gpr[3]);
true
} else {
tracing::warn!(
"Unimplemented kernel export: {:?}:{:#x}",
module, ordinal
);
// Return 0 (STATUS_SUCCESS) by default for unimplemented calls
ctx.gpr[3] = 0;
false
}
}
pub fn export_name(&self, module: ModuleId, ordinal: u32) -> Option<&'static str> {
self.exports.get(&(module, ordinal)).map(|&(name, _)| name)
}
pub fn alloc_handle(&mut self) -> u32 {
let h = self.next_handle;
self.next_handle += 4;
h
}
pub fn alloc_handle_for(&mut self, obj: KernelObject) -> u32 {
let h = self.alloc_handle();
self.objects.insert(h, obj);
h
}
pub fn tls_get(&self, index: u32) -> u64 {
self.tls_slots.get(&index).copied().unwrap_or(0)
}
pub fn tls_set(&mut self, index: u32, value: u64) {
self.tls_slots.insert(index, value);
}
pub fn tls_alloc(&mut self) -> u32 {
let idx = self.next_tls_index;
self.next_tls_index += 1;
idx
}
/// Allocate guest memory from the heap bump allocator.
/// Returns the base address of the allocated region.
pub fn heap_alloc(&mut self, size: u32, mem: &mut GuestMemory) -> Option<u32> {
let aligned_size = (size + 0xFFF) & !0xFFF; // Page-align
let base = self.heap_cursor;
if base.checked_add(aligned_size).is_none() || base + aligned_size > 0x6FFF_FFFF {
return None;
}
let protect = xenia_memory::page_table::MemoryProtect::READ
| xenia_memory::page_table::MemoryProtect::WRITE;
if mem.alloc(base, aligned_size, protect).is_err() {
return None;
}
self.heap_cursor += aligned_size;
Some(base)
}
/// Allocate a kernel stack.
pub fn stack_alloc(&mut self, size: u32, mem: &mut GuestMemory) -> Option<u32> {
let aligned_size = (size + 0xFFF) & !0xFFF;
let base = self.stack_cursor;
let protect = xenia_memory::page_table::MemoryProtect::READ
| xenia_memory::page_table::MemoryProtect::WRITE;
if mem.alloc(base, aligned_size, protect).is_err() {
return None;
}
self.stack_cursor += aligned_size;
Some(base + aligned_size) // Return top of stack
}
}
impl Default for KernelState {
fn default() -> Self {
Self::new()
}
}