handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes
Source changes (dormant parity infra, retained from iterate 2.AI/2.AO): - xenia-kernel/exports.rs: nt_create_event manual_reset polarity + related event wiring - xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps (.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as regenerable local artifacts — see memory + HANDOFF for the running findings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
451
audit-runs/canary-boot-state-inventory/inventory.md
Normal file
451
audit-runs/canary-boot-state-inventory/inventory.md
Normal file
@@ -0,0 +1,451 @@
|
||||
# Xenia-Canary Boot State — Comprehensive Inventory Immediately Before Guest XEX EntryPoint
|
||||
|
||||
## Context
|
||||
|
||||
This document is a research deliverable: a precise, source-verified inventory of *every* observable subsystem state that the guest XEX sees at the moment its `EntryPoint` is about to receive control. Driving motivation is RE on Project Sylpheed, where divergence vs. real hardware (or vs. canary) at boot can mask the actual root cause of a wedge. Existing notes/memory may be wrong; this report is built bottom-up from current source in [xenia-canary/](xenia-canary/).
|
||||
|
||||
All citations are markdown links into the repo. Where an agent claim was wrong it has been corrected and called out.
|
||||
|
||||
---
|
||||
|
||||
## 0. The Boot Sequence (one-screen overview)
|
||||
|
||||
The order in which xenia reaches the guest entrypoint, from [src/xenia/emulator.cc:280-360](xenia-canary/src/xenia/emulator.cc#L280-L360):
|
||||
|
||||
1. `Emulator::Initialize` constructs `graphics_system_` (factory). [emulator.cc:283](xenia-canary/src/xenia/emulator.cc#L283)
|
||||
2. `InputSystem` created + `Setup()`. [emulator.cc:295-307](xenia-canary/src/xenia/emulator.cc#L295-L307)
|
||||
3. `VirtualFileSystem` created. [emulator.cc:317](xenia-canary/src/xenia/emulator.cc#L317)
|
||||
4. `KernelState` created (this runs `InitializeKernelGuestGlobals()` from its ctor, [kernel_state.h:68](xenia-canary/src/xenia/kernel/kernel_state.h#L68)).
|
||||
5. HLE kernel modules loaded in order: `XboxkrnlModule`, `XamModule`, `XbdmModule`. [emulator.cc:327-329](xenia-canary/src/xenia/emulator.cc#L327-L329) — each ctor allocates guest-visible export variables.
|
||||
6. `graphics_system_->Setup(...)` — register file, gamma ramps, MMIO range, presenter, CP thread. [emulator.cc:336-339](xenia-canary/src/xenia/emulator.cc#L336-L339)
|
||||
7. `audio_system_->Setup(...)` — XMA decoder, worker thread parked. [emulator.cc:347](xenia-canary/src/xenia/emulator.cc#L347)
|
||||
8. `ExceptionHandler::Install`. [emulator.cc:358](xenia-canary/src/xenia/emulator.cc#L358)
|
||||
9. *(later, on title launch)* `Emulator::LaunchTitle` builds the VFS device + symlinks, calls `KernelState::LaunchModule` which calls `SetExecutableModule` (which spawns the kernel dispatch worker) and then `XThread::Create` for the main thread with `X_CREATE_SUSPENDED`. [kernel_state.cc:403-430](xenia-canary/src/xenia/kernel/kernel_state.cc#L403-L430)
|
||||
10. `processor()->PreLaunch()` (optional debugger wait). [kernel_state.cc:427](xenia-canary/src/xenia/kernel/kernel_state.cc#L427)
|
||||
11. Main thread is resumed → host thread lambda runs `Execute()` → backend dispatches to PC = `entry_point_`. [xthread.cc:421-445, 469-471](xenia-canary/src/xenia/kernel/xthread.cc#L421-L471)
|
||||
|
||||
Steps 1–8 occur **once per emulator instance**. Steps 9–11 occur **once per title** and are the immediate prelude to the guest's first PPC instruction. Everything below describes the state at the boundary between step 11 and the first guest insn.
|
||||
|
||||
---
|
||||
|
||||
## 1. PPC CPU State (entry thread)
|
||||
|
||||
All values from [src/xenia/cpu/thread_state.cc:66-112](xenia-canary/src/xenia/cpu/thread_state.cc#L66-L112), unless otherwise noted.
|
||||
|
||||
### 1.1 GPRs
|
||||
|
||||
| Reg | Value | Source |
|
||||
|----|----|----|
|
||||
| r0 | 0 (memset) | [thread_state.cc:84](xenia-canary/src/xenia/cpu/thread_state.cc#L84) |
|
||||
| **r1** (SP) | `stack_base` (top of stack, see §2) | [thread_state.cc:95](xenia-canary/src/xenia/cpu/thread_state.cc#L95) |
|
||||
| **r2** | `0x20000000` (constant — comment: "used by hv only i think") | [thread_state.cc:98](xenia-canary/src/xenia/cpu/thread_state.cc#L98) |
|
||||
| **r3** | `start_context` argument; for the main thread this is `0` (LaunchModule passes 0 as `start_context`, see [kernel_state.cc:414](xenia-canary/src/xenia/kernel/kernel_state.cc#L414)) | [processor.cc Execute() arg setup] |
|
||||
| r4..r12 | 0 (memset) | — |
|
||||
| **r13** | `pcr_address` — host pointer into KPCR (see §1.3 / §3.4) | [thread_state.cc:100](xenia-canary/src/xenia/cpu/thread_state.cc#L100) |
|
||||
| r14..r31 | 0 (memset) | — |
|
||||
|
||||
Note that the XEX ABI does NOT receive its entry args in r3 for the main thread: the main thread invokes the XEX directly with `start_context = 0`. Worker / kernel threads created via `ExCreateThread` go through `xapi_thread_startup` and pass `start_context` in r3.
|
||||
|
||||
### 1.2 Special-purpose registers
|
||||
|
||||
| SPR | Value | Note |
|
||||
|----|----|----|
|
||||
| LR | 0 | (memset) |
|
||||
| CTR | 0 | (memset) |
|
||||
| **MSR** | `0x9030` | Quoted comment: *"dumped from a real 360, 0x8000"* — [thread_state.cc:104](xenia-canary/src/xenia/cpu/thread_state.cc#L104) |
|
||||
| XER (ca/ov/so) | 0 | Split fields, all zeroed |
|
||||
| FPSCR | 0 | (memset; no explicit rounding-mode setup — default is RN=00 round-to-nearest) |
|
||||
| CR0..CR7 | 0 | (memset) |
|
||||
| **VSCR** | `0x00010000` (NJ bit = 1, Non-Java IEEE mode) | [thread_state.cc:103](xenia-canary/src/xenia/cpu/thread_state.cc#L103) — **Correction: Agent #1's claim of `0x00010016` was wrong; actual constant is `vec128i(0,0,0,0x00010000)`** |
|
||||
| **VRSAVE** | `0xFFFFFFFF` | [thread_state.cc:111](xenia-canary/src/xenia/cpu/thread_state.cc#L111) — "closer to correct than 0" |
|
||||
| DEC, TBL/TBU | 0 (memset) | — |
|
||||
| PC | `entry_point_` extracted from XEX | [user_module.cc:230](xenia-canary/src/xenia/kernel/user_module.cc#L230), passed via [kernel_state.cc:415](xenia-canary/src/xenia/kernel/kernel_state.cc#L415) |
|
||||
|
||||
### 1.3 FPRs, VMX/VR
|
||||
|
||||
- All 32 FPRs zeroed by memset of `PPCContext` ([thread_state.cc:84](xenia-canary/src/xenia/cpu/thread_state.cc#L84)).
|
||||
- All 128 vector registers (VMX128) zeroed by the same memset.
|
||||
- `vrsave = 0xFFFFFFFF` is the only non-zero vector-related slot.
|
||||
|
||||
### 1.4 Host-side stash bound to the context
|
||||
|
||||
Beyond architectural state, the `PPCContext` carries pointers used by JIT-generated code and trampolines ([thread_state.cc:87-92](xenia-canary/src/xenia/cpu/thread_state.cc#L87-L92)):
|
||||
|
||||
- `context->global_mutex` = `&xe::global_critical_region::mutex()`
|
||||
- `context->virtual_membase` / `physical_membase`
|
||||
- `context->processor` / `thread_state` / `thread_id`
|
||||
- (set later by `XThread::Create`) `context->kernel_state` — [xthread.cc:393](xenia-canary/src/xenia/kernel/xthread.cc#L393)
|
||||
|
||||
The context buffer itself is *guest-VA-aligned* so its low 32 bits end in `0xE0000000` — clever trick at [thread_state.cc:26-56](xenia-canary/src/xenia/cpu/thread_state.cc#L26-L56) gives the backend room to use int8 displacements into a preceding granule for backend-specific data.
|
||||
|
||||
---
|
||||
|
||||
## 2. Memory Layout & Heaps
|
||||
|
||||
### 2.1 Guest VA partitioning
|
||||
|
||||
The 2 GiB guest VA is shared across heaps managed by `Memory` ([src/xenia/memory.cc](xenia-canary/src/xenia/memory.cc), [memory.h](xenia-canary/src/xenia/memory.h)). Notable named ranges:
|
||||
|
||||
- **Default user VA heap** for small / large allocations.
|
||||
- **Stack range** `0x70000000 – 0x7F000000` — hardcoded constants `kStackAddressRangeBegin`/`kStackAddressRangeEnd` at [xthread.h:362-363](xenia-canary/src/xenia/kernel/xthread.h#L362-L363).
|
||||
- **Physical mirrors** at the A0/C0/E0 high-VA aliases of physical memory (multiple VA views of the same backing pages — required for GPU/audio DMA semantics).
|
||||
- **System heap** — kernel-side allocator backing the `SystemHeapAlloc` calls used by `XboxkrnlModule`, `XamModule`, `KernelState`, and per-thread bookkeeping. Backs PCR, TLS, KTHREAD, kernel guest globals, kernel exports listed in §3.
|
||||
- **Reserved high range** for kernel objects / object table.
|
||||
|
||||
### 2.2 Stack (boot thread)
|
||||
|
||||
Per [xthread.cc:275-301](xenia-canary/src/xenia/kernel/xthread.cc#L275-L301):
|
||||
- Requested size from XEX `XEX_HEADER_DEFAULT_STACK_SIZE` (rounded up to heap page size, default 4 KiB or 64 KiB depending on XEX page-size flag).
|
||||
- Allocated as `actual_size = size + 2*page_size` (one guard page top, one bottom).
|
||||
- Guard pages set to `kMemoryProtectNoAccess`. Body is RW.
|
||||
- `stack_limit_ = base + page_size` (low water), `stack_base_ = stack_limit_ + size` (high water; this is what r1 is set to).
|
||||
|
||||
### 2.3 TLS block
|
||||
|
||||
Per [xthread.cc:327-361](xenia-canary/src/xenia/kernel/xthread.cc#L327-L361):
|
||||
- Slots from `xex2_opt_tls_info.slot_count` if present, else **1024** (`kDefaultTlsSlotCount` [xthread.cc:335](xenia-canary/src/xenia/kernel/xthread.cc#L335)).
|
||||
- Layout: `[extended TLS image | slot_count*4 bytes of slots]`. `tls_static_address_` = base, `tls_dynamic_address_ = base + extended_size`.
|
||||
- Initial state: zeroed via `Memory::Fill`, then game-provided TLS image copied from `raw_data_address` if non-zero.
|
||||
- Accessed at guest runtime through `r13 + 0` (KPCR's `tls_ptr` field).
|
||||
|
||||
### 2.4 KPCR (Processor Control Region) — what r13 actually points at
|
||||
|
||||
Per [xthread.cc:379, 401-411](xenia-canary/src/xenia/kernel/xthread.cc#L379-L411): 0x2D8 bytes allocated from system heap; the fields set before entry are:
|
||||
|
||||
| Offset | Field | Value at entry |
|
||||
|----|----|----|
|
||||
| 0x000 | `tls_ptr` | `tls_static_address_` |
|
||||
| 0x030 | `pcr_ptr` | self (`pcr_address_`) |
|
||||
| 0x038 | `host_stash` | `(uint64_t)thread_state_->context()` (host pointer punned into u64) |
|
||||
| 0x070 | `stack_base_ptr` | `stack_base_` |
|
||||
| 0x074 | `stack_end_ptr` | `stack_limit_` |
|
||||
| 0x100 | `prcb_data.current_thread` | guest KTHREAD object guest VA |
|
||||
| 0x104 | `prcb` | `pcr_address + offsetof(X_KPCR, prcb_data)` |
|
||||
| `prcb_data.dpc_active` | 0 |
|
||||
|
||||
Everything else in the KPCR is zero at entry.
|
||||
|
||||
### 2.5 XEX image & sections
|
||||
|
||||
Loaded by `XexModule` (`src/xenia/cpu/xex_module.cc` plus `src/xenia/kernel/user_module.cc`):
|
||||
- Header copied into the system heap, accessible as `guest_xex_header_` ([user_module.cc:224](xenia-canary/src/xenia/kernel/user_module.cc#L224)).
|
||||
- Entry point + stack/tls/workspace sizes pulled via `GetOptHeader` ([user_module.cc:230-234](xenia-canary/src/xenia/kernel/user_module.cc#L230-L234)).
|
||||
- PE sections mapped at their declared VAs with section flags; `.text` is X+R (or X+R+W if `writable_code_segments` cvar set).
|
||||
- Import tables resolved during `LoadContinue` — each import slot is patched to invoke the host kernel export trampoline directly (no guest thunk).
|
||||
- Title workspace heap created at the XEX-declared address if `XEX_HEADER_TITLE_WORKSPACE_SIZE` is set ([user_module.cc:237](xenia-canary/src/xenia/kernel/user_module.cc#L237)).
|
||||
|
||||
---
|
||||
|
||||
## 3. Kernel / xboxkrnl Guest-Visible State
|
||||
|
||||
Verified directly against [src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc) and [src/xenia/kernel/kernel_state.cc](xenia-canary/src/xenia/kernel/kernel_state.cc).
|
||||
|
||||
### 3.1 Pre-initialized exported variables (xboxkrnl.exe)
|
||||
|
||||
Created at `XboxkrnlModule` ctor — these are visible *before* entry because the ctor runs at step 5 of §0.
|
||||
|
||||
| Export | Size | Initial bytes | Source |
|
||||
|----|----|----|----|
|
||||
| **KeDebugMonitorData** | 4 (or 4+sizeof(X_KEDEBUGMONITORDATA) if cvar on) | `0` (off path); points to struct w/ callback fn ptr (on path) | [xboxkrnl_module.cc:80-102](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc#L80-L102) |
|
||||
| **KeCertMonitorData** | same | `0` / struct + callback | [xboxkrnl_module.cc:104-123](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc#L104-L123) |
|
||||
| **XboxHardwareInfo** | 16 | `[0]=0x20` (HDD bit), `[4]=0x06` (CPU count), rest 0 | [xboxkrnl_module.cc:136-141](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc#L136-L141) |
|
||||
| **ExConsoleGameRegion** | 4 | `0xFFFFFFFF` | [xboxkrnl_module.cc:146-150](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc#L146-L150) |
|
||||
| **XexExecutableModuleHandle** | 4 | uninit at ctor; populated later when `SetExecutableModule` runs | [xboxkrnl_module.cc:161-164](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc#L161-L164) |
|
||||
| **ExLoadedImageName** | `kExLoadedImageNameSize` (1024-aligned) | uninit at ctor; filled later with module path | [xboxkrnl_module.cc:171-174](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc#L171-L174) |
|
||||
| **ExLoadedCommandLine** | aligned(strlen+1, 1024) | `"default.xex"` + optional `cvars::cl`, NUL-padded | [xboxkrnl_module.cc:181-194](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc#L181-L194) |
|
||||
| **XboxKrnlVersion** | 8 | `kernel_state_->GetKernelVersion()` (verify exact bytes in `kernel_state.h`) | [xboxkrnl_module.cc:199-204](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc#L199-L204) |
|
||||
| **KeTimeStampBundle** | 24 | populated lazily on first read via `GetKeTimestampBundle()` — see §3.2 | [xboxkrnl_module.cc:206-208](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc#L206-L208) |
|
||||
| **ExThreadObjectType**, **ExEventObjectType**, **ExMutantObjectType**, **ExSemaphoreObjectType**, **ExTimerObjectType**, **IoCompletionObjectType**, **IoDeviceObjectType**, **IoFileObjectType**, **ObDirectoryObjectType**, **ObSymbolicLinkObjectType**, **UsbdBootEnumerationDoneEvent** | each → offset within `KernelGuestGlobals` block | populated by `InitializeKernelGuestGlobals()` (§3.3) | [xboxkrnl_module.cc:214-225](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc#L214-L225) |
|
||||
|
||||
> **Correction vs Agent #3**: KeTimeStampBundle initialization does NOT happen in `xboxkrnl_module.cc`; it lives in `KernelState::CreateKeTimestampBundle` at [kernel_state.cc:1272-1295](xenia-canary/src/xenia/kernel/kernel_state.cc#L1272-L1295) and is created lazily on first call to `GetKeTimestampBundle()`. A `HighResolutionTimer::CreateRepeating` is then armed to call `UpdateKeTimestampBundle()` periodically.
|
||||
|
||||
### 3.2 KeTimeStampBundle layout (`X_TIME_STAMP_BUNDLE`)
|
||||
|
||||
Initialized in [kernel_state.cc:1272-1295](xenia-canary/src/xenia/kernel/kernel_state.cc#L1272-L1295):
|
||||
|
||||
| Offset | Field | Initial value |
|
||||
|----|----|----|
|
||||
| +0x00 | `interrupt_time` (u64) | current interrupt-time value |
|
||||
| +0x08 | `system_time` (u64) | current system time |
|
||||
| +0x10 | `tick_count` (u32) | `Clock::QueryGuestUptimeMillis()` |
|
||||
| +0x14 | `padding` (u32) | 0 |
|
||||
|
||||
A repeating `HighResolutionTimer` updates these fields every tick ([kernel_state.cc:1292-1294](xenia-canary/src/xenia/kernel/kernel_state.cc#L1292-L1294)).
|
||||
|
||||
### 3.3 KernelGuestGlobals — the big preinitialized blob
|
||||
|
||||
Allocated and zeroed at [kernel_state.cc:1511-1516](xenia-canary/src/xenia/kernel/kernel_state.cc#L1511-L1516); see the `KernelGuestGlobals` struct definition at [kernel_state.h:115-...](xenia-canary/src/xenia/kernel/kernel_state.h#L115). Fields include:
|
||||
|
||||
- `ExThreadObjectType`, `ExEventObjectType`, `ExMutantObjectType`, `ExSemaphoreObjectType`, `ExTimerObjectType`, `IoCompletionObjectType`, `IoDeviceObjectType`, `IoFileObjectType`, `ObDirectoryObjectType`, `ObSymbolicLinkObjectType`
|
||||
- `UsbdBootEnumerationDoneEvent`
|
||||
- `OddObj` (referenced [kernel_state.cc:1527](xenia-canary/src/xenia/kernel/kernel_state.cc#L1527))
|
||||
- `system_process`, `title_process`, `idle_process` (accessor methods at [kernel_state.h:212, 216, 220](xenia-canary/src/xenia/kernel/kernel_state.h#L212-L220))
|
||||
|
||||
Each guest object type is filled with the kernel's view of how dispatcher headers / object headers look. Bytes at these offsets are observable as `dq` constants by the guest before entry.
|
||||
|
||||
### 3.4 ProcessInfoBlock
|
||||
|
||||
Filled by `InitializeProcess` (called from `SetExecutableModule`). Notable preinit fields per Agent #3's report:
|
||||
- +0x0C: `0x0000007F`
|
||||
- +0x10: `0x001F0000`
|
||||
- +0x14: `thread_count = 0`
|
||||
- +0x1B: `0x06`
|
||||
- +0x1C: `kernel_stack_size = 16384`
|
||||
- +0x20: `process_type = X_PROCTYPE_USER` (or `X_PROCTYPE_TITLE` for title)
|
||||
- +0x24..+0x4F: TLS info copy from XEX header
|
||||
|
||||
### 3.5 Object table
|
||||
|
||||
- Empty before `LaunchModule`. As soon as `SetExecutableModule` runs, the executable module's handle is the first allocation.
|
||||
- The kernel dispatch worker thread handle is the second.
|
||||
- The main XThread handle is the third. All allocated from `object_table()` ([xthread.cc:317](xenia-canary/src/xenia/kernel/xthread.cc#L317) via `CreateNative`).
|
||||
- `XObject::kHandleBase = 0xF8000000`; handles spaced by 4.
|
||||
|
||||
---
|
||||
|
||||
## 4. XAM State
|
||||
|
||||
### 4.1 User profile
|
||||
|
||||
Created in `KernelState` ctor ([kernel_state.cc:52](xenia-canary/src/xenia/kernel/kernel_state.cc#L52)). Single profile preconfigured at `src/xenia/kernel/xam/user_profile.cc`:
|
||||
- XUID: `0xB13EBABEBABEBABE` (hardcoded)
|
||||
- Gamertag: `"User"`
|
||||
- 18 default profile settings (per Agent #3's enumeration — XPROFILE_GAMER_YAXIS_INVERSION=0, XPROFILE_OPTION_CONTROLLER_VIBRATION=3, XPROFILE_GAMERCARD_REGION=0, XPROFILE_GAMERCARD_CRED=0xFA, etc.).
|
||||
|
||||
> Spot-check note: I did not re-verify each of the 18 settings by direct read; cite by file before depending on any single value.
|
||||
|
||||
### 4.2 App manager / content manager
|
||||
|
||||
- `AppManager` instantiated, `RegisterApps()` called from KernelState ctor — registers known XAM apps. No launch data at entry.
|
||||
- `ContentManager` rooted at `emulator_->content_root()` (see `Emulator` ctor). Title-specific save/DLC mounts are not yet established at entrypoint; they are established lazily.
|
||||
|
||||
### 4.3 Notification listeners
|
||||
|
||||
Empty list at entry ([kernel_state.h:219](xenia-canary/src/xenia/kernel/kernel_state.h#L219)). On first listener registration with mask bit 1 set, the system synthesizes startup notifications (XN_SYS_UI, XN_SYS_SIGNINCHANGED, XN_SYS_INPUTDEVICESCHANGED, XN_SYS_INPUTDEVICECONFIGCHANGED — [kernel_state.cc:657-671](xenia-canary/src/xenia/kernel/kernel_state.cc#L657-L671)).
|
||||
|
||||
---
|
||||
|
||||
## 5. Filesystem State
|
||||
|
||||
### 5.1 Devices mounted at entrypoint
|
||||
|
||||
In `Emulator::LaunchTitle` / `Emulator::CreateVfsDevice` ([emulator.cc:376-...](xenia-canary/src/xenia/emulator.cc#L376)):
|
||||
|
||||
| Game source | Device(s) registered | Symlinks |
|
||||
|----|----|----|
|
||||
| `.xex` (loose folder) | `HostPathDevice(\Device\Harddisk0\Partition1, parent_dir, read_only=!allow_game_relative_writes)` | `game:`, `d:` → same |
|
||||
| `.iso` (XISO) | `DiscImageDevice(\Device\Cdrom0, path)` | `game:`, `d:` → same |
|
||||
| LIVE/CON/PIRS (STFS) | `XContentContainerDevice::CreateContentDevice(...)` | `game:`, `d:` → same |
|
||||
| ZAR | `DiscZarchiveDevice(...)` | same |
|
||||
|
||||
Plus optional mounts driven by cvars (from `xenia_main.cc`):
|
||||
- `mount_scratch` → `\SCRATCH`, symlink `scratch:`
|
||||
- `mount_cache` → `\CACHE0`, `\CACHE1`, `\CACHE` with `cache0:`, `cache1:`, `cache:`
|
||||
|
||||
No files are open at entry; the guest opens what it needs.
|
||||
|
||||
### 5.2 Cache & temp
|
||||
|
||||
- No CACHE partition data is fabricated. If `mount_cache` is on, host directories `cache/`, `cache0/`, `cache1/` back the partitions; otherwise they don't exist for the guest at all.
|
||||
- No `STFS` content packages are pre-mounted unless the title was launched from an STFS package.
|
||||
|
||||
---
|
||||
|
||||
## 6. GPU State (Xenos / Vulkan or D3D12 backend)
|
||||
|
||||
### 6.1 RegisterFile
|
||||
|
||||
Allocated as host memory in `GraphicsSystem` ctor at [src/xenia/gpu/graphics_system.cc:79-81](xenia-canary/src/xenia/gpu/graphics_system.cc#L79-L81):
|
||||
```cpp
|
||||
register_file_ = reinterpret_cast<RegisterFile*>(memory::AllocFixed(
|
||||
nullptr, sizeof(RegisterFile), kReserveCommit, kReadWrite));
|
||||
```
|
||||
This zero-fills the entire register file. **No registers are preloaded with non-zero values before entry.** `XE_GPU_REG_D1MODE_V_COUNTER` is later incremented asynchronously by the frame-limiter thread once that thread starts (graphics_system.cc:~177).
|
||||
|
||||
### 6.2 Gamma ramps (the one notable pre-initialized GPU data)
|
||||
|
||||
In `CommandProcessor::Initialize` at [src/xenia/gpu/command_processor.cc:130-148](xenia-canary/src/xenia/gpu/command_processor.cc#L130-L148): a 256-entry sRGB-table-like ramp `(i * 0x3FF / 0xFF)` per channel and a 128-entry PWL ramp with delta `0x200` are loaded. These are observable if guest code reads gamma registers before writing them.
|
||||
|
||||
### 6.3 Command Processor / ringbuffer
|
||||
|
||||
- The CP **thread** is spawned in `GraphicsSystem::Setup` ([graphics_system.cc:135](xenia-canary/src/xenia/gpu/graphics_system.cc#L135)), at step 6 of §0. It blocks on `write_ptr_index_event_` waiting for PM4 work.
|
||||
- The **ringbuffer itself is NOT allocated before entry.** The guest allocates and registers it via `VdInitializeRingBuffer` ([xboxkrnl_video.cc:313-319](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc#L313-L319)).
|
||||
|
||||
### 6.4 MMIO
|
||||
|
||||
The GPU MMIO range `[0x7FC80000, 0xFFFF0000]` is hooked via `Memory::AddVirtualMappedRange` from `GraphicsSystem::Setup` ([graphics_system.cc:141-144](xenia-canary/src/xenia/gpu/graphics_system.cc#L141-L144)). Guest reads/writes route to GPU register file handlers.
|
||||
|
||||
### 6.5 Presenter & backend device
|
||||
|
||||
- Presenter and the actual Vulkan/D3D12 device + swapchain are created in `GraphicsSystem::Setup` when `with_presentation=true` ([graphics_system.cc:116-128](xenia-canary/src/xenia/gpu/graphics_system.cc#L116-L128)).
|
||||
- `VdInitializeEngines` is stubbed to return 1 — xenia uses no real microcode ([xboxkrnl_video.cc:271-280](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc#L271-L280)).
|
||||
- EDRAM/tile allocator, surface info, swap counters: not initialized to guest-visible state pre-entry.
|
||||
|
||||
### 6.6 Reported video mode (queried by guest after entry but driven by config)
|
||||
|
||||
`VdQueryVideoMode` at [xboxkrnl_video.cc:203-219](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc#L203-L219) reports cvar-driven values: default `1280×720`, `widescreen=true`, `is_interlaced=false`, `refresh_rate=60.0f`, `video_standard=1` (NTSC), `pixel_rate=0x8A`, `widescreen_flag=0x01`. Gamma type=2 (BT.709), power ≈ 2.222.
|
||||
|
||||
---
|
||||
|
||||
## 7. Audio (APU) State
|
||||
|
||||
In `AudioSystem::Setup` ([src/xenia/apu/audio_system.cc:48-97](xenia-canary/src/xenia/apu/audio_system.cc#L48-L97)) called at step 7 of §0:
|
||||
|
||||
- `queued_frames_` clamped to `[4, 64]` from cvar `apu_max_queued_frames` (default 8).
|
||||
- **256 client semaphores** allocated, initial count 0, max count = `queued_frames_`.
|
||||
- `shutdown_event_`, `resume_event_` created.
|
||||
- `XmaDecoder` instantiated; its `Setup()` runs.
|
||||
- Worker thread spawned, executing `WorkerThreadMain`, immediately parked on `WaitAny(wait_handles_)`. **No audio is being submitted, no frames queued.**
|
||||
|
||||
The XMA guest-memory window (typically observed near `0x42500000` per RE notes) has no pre-populated context state — the guest must call `XAudioRegisterRenderDriverClient` and provide context VAs.
|
||||
|
||||
---
|
||||
|
||||
## 8. HID / Input
|
||||
|
||||
`InputSystem::Setup` ([emulator.cc:307](xenia-canary/src/xenia/emulator.cc#L307)) initializes the input layer; per [src/xenia/hid/input_system.h:82-85](xenia-canary/src/xenia/hid/input_system.h#L82-L85):
|
||||
- `connected_slots = bitset<XUserMaxUserCount>(0)` — **no controller is plugged in** at the moment of entry. The driver layer wires up *on demand* as controllers connect.
|
||||
- `last_used_slot = 0`.
|
||||
- `Portal` (MCP bridge) created.
|
||||
- `XInputGetCapabilities` on disconnected slots returns `X_ERROR_DEVICE_NOT_CONNECTED` ([input_system.cc:179](xenia-canary/src/xenia/hid/input_system.cc#L179)).
|
||||
|
||||
Vibration state, battery, etc.: nothing reported until a controller is connected.
|
||||
|
||||
---
|
||||
|
||||
## 9. Networking / XNet / Sockets
|
||||
|
||||
No network init occurs before entry; the guest must call `NetDll_XNetStartup` to populate `xnet_startup_params` (zero-initialized at [xam_net.cc:173](xenia-canary/src/xenia/kernel/xam/xam_net.cc#L173)).
|
||||
|
||||
When queried via `NetDll_XNetGetTitleXnAddr` ([xam_net.cc:476-499](xenia-canary/src/xenia/kernel/xam/xam_net.cc#L476-L499)):
|
||||
- IP `ina` = `127.0.0.1` (loopback)
|
||||
- Online IP `inaOnline` = `0.0.0.0`
|
||||
- Online port = 0
|
||||
- MAC `abEnet` = `CC CC CC CC CC CC`
|
||||
- `abOnline` = 20 zeros
|
||||
- Return code = `XNET_GET_XNADDR_STATIC` (0x00000004)
|
||||
|
||||
`NetDll_XNetGetDebugXnAddr` returns `XNET_GET_XNADDR_NONE` (0x00000001).
|
||||
|
||||
No sockets, no system-link, no NIC enumeration at entry.
|
||||
|
||||
---
|
||||
|
||||
## 10. Real-Time Clock & Timebase
|
||||
|
||||
- `Clock::guest_tick_frequency()` ([src/xenia/base/clock.cc:39](xenia-canary/src/xenia/base/clock.cc#L39)) returns the host CPU tick frequency unless overridden by `clock_no_scaling`. Reported to guest by `KeQueryPerformanceFrequency_entry` ([xboxkrnl_threading.cc:438-443](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc#L438-L443)).
|
||||
- `KeQuerySystemTime_entry` ([xboxkrnl_threading.cc:483-497](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc#L483-L497)) reads `Clock::QueryGuestSystemTime` — wall clock as of emulator startup epoch.
|
||||
- `KeTimeStampBundle` (§3.2) is the cheap shared mailbox; updated by a repeating `HighResolutionTimer`.
|
||||
|
||||
---
|
||||
|
||||
## 11. Threading at Entry
|
||||
|
||||
At the boundary, the threads that exist are:
|
||||
|
||||
1. **Main XThread** (`is_main_thread=true`, `guest_thread=true`) — currently suspended, about to be resumed. Stack range `0x70000000-0x7F000000`, host stack 16 MiB ([xthread.cc:420](xenia-canary/src/xenia/kernel/xthread.cc#L420)), priority/affinity set via `GetFakeCpuNumber` derived from `(creation_flags >> 24)` ([xthread.cc:395-396](xenia-canary/src/xenia/kernel/xthread.cc#L395-L396)). CPU index assigned via `SetActiveCpu(cpu_index)` ([xthread.cc:464](xenia-canary/src/xenia/kernel/xthread.cc#L464)).
|
||||
2. **Kernel dispatch worker thread** — spawned in `SetExecutableModule` to handle guest async callbacks ([kernel_state.cc:368-391 ~](xenia-canary/src/xenia/kernel/kernel_state.cc#L368-L391)). Host-side; consumes from a host queue, does not appear in the guest object table.
|
||||
3. **GPU command processor thread** — already running (parked on `write_ptr_index_event_`).
|
||||
4. **Audio worker thread** — already running (parked on its semaphore set).
|
||||
5. **Optional**: frame-limiter thread, presenter thread, KeTimeStampBundle update timer thread.
|
||||
|
||||
**Worker threads (sub_825070F0-style XAudio/render workers, secondary game workers, XAM threads) do NOT exist yet** — they are spawned by guest code post-entry.
|
||||
|
||||
Scheduler state:
|
||||
- No IRQL machinery; guest code runs at PASSIVE-equivalent.
|
||||
- Quantum / preemption is approximated; ours uses cooperative-ish per-thread quanta.
|
||||
- DPC list empty ([kernel_state.h:233](xenia-canary/src/xenia/kernel/kernel_state.h#L233)).
|
||||
|
||||
---
|
||||
|
||||
## 12. JIT / Codegen State
|
||||
|
||||
- Backend (x64 or AArch64) initialized at `Processor` construction.
|
||||
- `backend_->AllocThreadData()` and `InitializeBackendContext(context_)` called from `ThreadState` ctor ([thread_state.cc:77, 82](xenia-canary/src/xenia/cpu/thread_state.cc#L77-L82)).
|
||||
- Code cache empty — entry-point block JITs on first execution unless `enable_early_precompilation` cvar pre-compiled it.
|
||||
- Import-table call sites already patched to direct host trampolines (resolved during `XexModule::LoadContinue`).
|
||||
- Syscall / MMIO handlers wired up.
|
||||
|
||||
---
|
||||
|
||||
## 13. Misc Peripherals
|
||||
|
||||
- **DVD / Disc drive**: no separate drive state — backed by the VFS device created at title launch. Tray/laser not modeled.
|
||||
- **USB**: no enumeration. `UsbdBootEnumerationDoneEvent` is allocated as a guest event in `KernelGuestGlobals` but its signaled-state at entry is the field default (verify against `KernelGuestGlobals` struct).
|
||||
- **Cache partition**: present only if `mount_cache` cvar set.
|
||||
- **System link / bridged LAN**: not initialized.
|
||||
- **Hypervisor surfaces / KdNet / DmEvents**: KD/network debug is not implemented. `DebugPrint` redirects into xenia's logger.
|
||||
- **Emulator-detection signals (intentional or accidental)**: `KeDebugMonitorData` always nonzero or known-zero (vs. real-HW behavior), missing/unimplemented kernel exports, exact MSR value `0x9030`, the constant r2=`0x20000000`, the fake XUID `0xB13EBABEBABEBABE`.
|
||||
|
||||
---
|
||||
|
||||
## 14. Single-Page "What is in the registers right now?" Quick Card
|
||||
|
||||
For Sylpheed RE workflows where you need to set a breakpoint at the first guest insn:
|
||||
|
||||
```
|
||||
PC = <XEX entry_point> ; from XEX optional header
|
||||
r0..r12 = 0
|
||||
r1 = stack_base (top of 0x70000000–0x7F000000 region, page-aligned)
|
||||
r2 = 0x20000000
|
||||
r3 = 0
|
||||
r13 = pcr_address (KPCR, has tls_ptr at [r13+0])
|
||||
r14..r31= 0
|
||||
LR=0 CTR=0 XER=0 CR=0 FPSCR=0
|
||||
MSR = 0x9030
|
||||
VSCR = 0x00010000 ; NJ=1
|
||||
VRSAVE = 0xFFFFFFFF
|
||||
FPRs = +0.0 (zero bit pattern)
|
||||
VR0..127= zero
|
||||
DEC, TB = 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15. Verification (how to confirm the above for a specific Sylpheed boot)
|
||||
|
||||
The deliverable above is a static read of the source. To validate dynamically for a specific run:
|
||||
|
||||
1. **Quick canary smoke test** — run xenia-canary against Sylpheed with logging set high enough to catch `XELOGI("XThread{:08X} ({:X}) Stack: {:08X}-{:08X}", ...)` from [xthread.cc:389](xenia-canary/src/xenia/kernel/xthread.cc#L389). That confirms `stack_base_`, `stack_limit_`, `thread_id_`.
|
||||
2. **Drop into JIT entry breakpoint** — set a JIT-store probe (per AUDIT-067 pattern in memory) on the guest PC = `entry_point_` and dump the `PPCContext` once. Compare GPRs/VSCR/MSR against the table above.
|
||||
3. **Pre-entry kernel-export dump** — print the contents of guest VAs for `XboxHardwareInfo`, `KeDebugMonitorData`, `KeTimeStampBundle`, `ExLoadedCommandLine` immediately before resuming the main thread; verify §3.1 expected bytes.
|
||||
4. **VFS sanity** — `KernelState::file_system_->ResolvePath("game:\\default.xex")` should succeed; `D:` should resolve to the same device.
|
||||
5. **GPU pre-state** — assert no PM4 packets have been dispatched (`command_processor_->paused()` / ringbuffer write-ptr == read-ptr) and gamma table contains the linear ramp from §6.2.
|
||||
6. **Audio pre-state** — assert 256 client semaphores all have count=0, worker thread parked, no XMA contexts registered.
|
||||
7. **Cross-engine sanity** — run xenia-rs against the same XEX with the same cvars; the values that should match between engines: r1/r2/r13/MSR/VSCR/VRSAVE, PC, `XboxHardwareInfo`, `ExConsoleGameRegion`, `XexExecutableModuleHandle`, the 1024-entry default TLS slot count, stack range, KPCR layout, default profile XUID.
|
||||
|
||||
---
|
||||
|
||||
## 16. Known Unknowns / Things Not Verified in This Pass
|
||||
|
||||
- Exact byte contents of `KernelGuestGlobals` *object-type* sub-structs (`ExThreadObjectType` etc.) — these are populated in `InitializeKernelGuestGlobals()`; full byte-level dump would require reading [kernel_state.cc:1511 onward](xenia-canary/src/xenia/kernel/kernel_state.cc#L1511) in full.
|
||||
- `XboxKrnlVersion` exact 8 bytes — held in `KernelVersion` static, not spot-checked in this pass.
|
||||
- The 18 default profile setting values were taken from Agent #3's report and not individually re-read.
|
||||
- Exact `xex2_opt_tls_info` fields for Sylpheed (slot_count, raw_data_size) — title-specific.
|
||||
- Per-backend (Vulkan vs. D3D12) device-state nuances.
|
||||
|
||||
These are noted explicitly so this doc is not mistaken for full coverage.
|
||||
|
||||
---
|
||||
|
||||
## 17. Critical Files Index
|
||||
|
||||
For quick navigation:
|
||||
|
||||
- [src/xenia/cpu/thread_state.cc](xenia-canary/src/xenia/cpu/thread_state.cc) — PPC context init (canonical truth for GPRs/MSR/VSCR/VRSAVE).
|
||||
- [src/xenia/kernel/xthread.cc](xenia-canary/src/xenia/kernel/xthread.cc) — stack, TLS, KPCR, KTHREAD, host-thread creation, `Execute` dispatch.
|
||||
- [src/xenia/kernel/kernel_state.cc](xenia-canary/src/xenia/kernel/kernel_state.cc) — LaunchModule, SetExecutableModule, KernelGuestGlobals, KeTimeStampBundle.
|
||||
- [src/xenia/kernel/kernel_state.h](xenia-canary/src/xenia/kernel/kernel_state.h) — `KernelGuestGlobals` struct.
|
||||
- [src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_module.cc) — preinit'd guest-visible kernel-exported variables.
|
||||
- [src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_video.cc) — Vd* stubs.
|
||||
- [src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc](xenia-canary/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc) — KeQueryPerformanceFrequency / KeQuerySystemTime.
|
||||
- [src/xenia/kernel/xam/xam_net.cc](xenia-canary/src/xenia/kernel/xam/xam_net.cc) — XNADDR and network defaults.
|
||||
- [src/xenia/kernel/xam/user_profile.cc](xenia-canary/src/xenia/kernel/xam/user_profile.cc) — default user profile.
|
||||
- [src/xenia/kernel/user_module.cc](xenia-canary/src/xenia/kernel/user_module.cc) — XEX header parsing, entry_point extraction.
|
||||
- [src/xenia/emulator.cc](xenia-canary/src/xenia/emulator.cc) — Initialize/LaunchTitle subsystem order; VFS device factory.
|
||||
- [src/xenia/gpu/graphics_system.cc](xenia-canary/src/xenia/gpu/graphics_system.cc) — GPU setup.
|
||||
- [src/xenia/gpu/command_processor.cc](xenia-canary/src/xenia/gpu/command_processor.cc) — gamma ramp init, CP thread.
|
||||
- [src/xenia/apu/audio_system.cc](xenia-canary/src/xenia/apu/audio_system.cc) — audio worker, client semaphores.
|
||||
- [src/xenia/hid/input_system.h](xenia-canary/src/xenia/hid/input_system.h), [.cc](xenia-canary/src/xenia/hid/input_system.cc) — controller slots empty at entry.
|
||||
- [src/xenia/memory.cc](xenia-canary/src/xenia/memory.cc), [.h](xenia-canary/src/xenia/memory.h) — heap topology, physical mirrors.
|
||||
- [src/xenia/base/clock.cc](xenia-canary/src/xenia/base/clock.cc) — tick frequency, system time.
|
||||
Reference in New Issue
Block a user