aboutsummaryrefslogtreecommitdiff
path: root/NX2_NOTES.md
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-27 13:48:52 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-27 13:48:52 -0400
commit5511fa9f9a9225e926a15e8c786e8ef1b8844f8d (patch)
tree5e01723dea544fda88f16eb6ad857253199bac8d /NX2_NOTES.md
parent399d053e4fac2d65d3d2b20f5b358b168b0c09ee (diff)
NX2_NOTES: classification of all titles, graphics boot progress
Diffstat (limited to 'NX2_NOTES.md')
-rw-r--r--NX2_NOTES.md119
1 files changed, 79 insertions, 40 deletions
diff --git a/NX2_NOTES.md b/NX2_NOTES.md
index 1bc145c485..28ee42d839 100644
--- a/NX2_NOTES.md
+++ b/NX2_NOTES.md
@@ -17,6 +17,14 @@ scope for this pass.
`eden-cli.exe -g "<title>\exefs\main"`
- Debug helpers:
- `EDEN_NX2_SVC_TRACE=1` logs every SVC with arguments and results.
+ - `EDEN_NX2_IPC_TRACE=1` logs every dispatched CMIF command and non-zero result words.
+ - `EDEN_NX2_AUTO_STUB=1` answers every unknown command with an empty success (the
+ `use_auto_stub` setting gets reset by eden-cli's config rewrite). Used for all runs below.
+ - `eden-cli --filter "<log filter>"` sets the log filter for one run (e.g. `*:Info Loader:Debug`
+ to get module load addresses).
+ - `tools/nx2/nso_syms.py <nso> addr|got|find ...` maps module offsets to exported symbols, GOT
+ slots to imports, or searches symbol names. Eden's backtrace shows "unknown" modules for some
+ titles; with the `loaded module X @ addr` lines this turns frames into SDK names.
- `tools/nx2/a64_coverage.py <a64.inc> <nso>...` lists encodings in an NSO's .text that
dynarmic's decoder table doesn't match.
- `tools/nx2/npdm_info.py <main.npdm>` dumps the META/ACI0 header, services and kernel caps.
@@ -27,9 +35,22 @@ scope for this pass.
## Titles
-All three titles with a `main.npdm` share the same layout: 64-bit, 39-bit address space,
-ThreadInfo cores 0..5 (core mask 0x3f), priorities 28..59, KernelVersion cap 9.1, SVCs 0x01-0x36
-plus 0x80, Switch 2 ACID layout. The fourth title's ExeFS has no `main.npdm`.
+All titles share the same `main.npdm` layout: 64-bit, 39-bit address space, ThreadInfo cores
+0..5 (core mask 0x3f), priorities 28..59, KernelVersion cap 9.1, SVCs 0x01-0x36 plus 0x80,
+Switch 2 ACID layout.
+
+All four are **Switch 2 builds**: none is a plain Switch 1 sanity check. Instruction counts that
+stock dynarmic did not decode (`a64_coverage.py` against the unpatched `a64.inc`):
+
+| Title (ExeFS) | Program id | RETAA/B | LDAPR | LSE atomics | CAS | SWP | other |
+|---|---|---|---|---|---|---|---|
+| first `exefs` | 0400C3F00006E000 | 114,767 | 37,560 | 24,718 | 6,640 | 2,470 | CASP 6, XPAC 7, PAC 2, STG 1 |
+| second `exefs` and `updates/exefs` | 0400A3A01EE4C000 | 12 (rtld only) | - | - | - | - | main/sdk/subsdk0 don't decompress |
+| third `exefs` (update `updates/65536` ~same) | 04005B902517C000 | 134,287 | 37,052 | 160,741 | 19,987 | 1,623 | XPAC 3, PAC 2, STG 1 |
+| fourth `exefs` (update `updates/65536` ~same) | 0400380020056000 | 134,911 | 100,520 | 32,110 | 8,667 | 3,733 | PACGA 58, PAC 7, CASP 6, XPAC 3, STG 1 |
+
+With the patched decoder, games 1/3/4 only leave ~60-110 rare SIMD encodings in `sdk` undecoded
+(e.g. `5f119f9c`, `4ea05c42`, `0f413044`, FP16/v8.2 forms).
## Blockers hit, in order
@@ -47,6 +68,12 @@ plus 0x80, Switch 2 ACID layout. The fourth title's ExeFS has no `main.npdm`.
| 10 | same | `IApplicationProxy` cmds 60 and 40: new getters | return a generic `nx2:` stub interface (`nx2_stub_interface.h`); calls on it are auto-stubbed and logged |
| 11 | `nn::oe::SetupGpuErrorHandler` -> CreateThread | thread on virtual core 5; Eden emulates 4 cores | fold virtual cores 4/5 onto 0/1 in CreateThread/SetThreadCoreMask |
| 12 | (title 3) `nn::oe` init | `IApplicationFunctions` 320 has no handler | empty-success stub |
+| 13 | (titles 3/4) `nn::fs::QueryMountRomCacheSize` -> `GetFileSystemProxyServiceObject` abort 2002-1002 | fsp-srv `SetCurrentProcess` returned ResultTargetNotFound: the loose-ExeFS loader never registered the process with the fs controller, and never set its RomFS | loader: give loose ExeFS boots a RomFS (sibling `romfs/`) and an fs registration |
+| 14 | (title 4) `IApplicationFunctions` 321 | unknown | empty-success stub |
+| 15 | (title 4) `nn::oe::GetCurrentGraphicsScale` UnexpectedDefault | `IApplicationProxy` 60 is a graphics-scale interface; its cmd 10 must return 10/20/30 | `IGraphicsScaleController` returning 10 |
+| 16 | (title 4) `nn::gs::sdk::BootGraphicsSystem` -> `nn::os::GetRemoteMemoryUsage` abort | `svcGetInfo` 31/32 (remote memory for gsv/nvsrv) | return 0 for both (logged) |
+| 17 | (title 4) `nn::gsv::BootWithRemoteMemory` abort | it sends TIPC on a session handle from `ISelfController` 2000, which had no handler | `ISelfController` 2000 returns a raw session to `INx2GraphicsSystemSession` (`nx2_gsv.h`), which accepts every command |
+| 18 | (title 3) `nn::oe::GetPseudoDeviceId` abort | needs control.nacp, which loose ExeFS lacks | seed from program id |
Also changed: `svcGetInfo` 29/30 now report the upper half of the alias region instead of an
empty region (guess: VAMM address space). It made no visible difference for title 1.
@@ -57,43 +84,55 @@ Both stop at the same point. No GPU/nvdrv call is reached yet in any title.
## Where it stops now
-**Title 1** (`first switch game`, program id 0400C3F00006E000, main module `Drive.nss`):
-rtld -> nn::init -> nnosInitialize -> VAMM -> nn::oe (applet proxy, window, focus) all complete;
-the game's own static initializers run. It then builds an SDK allocator object at address 0
-(writes to 0x0..0xF8; vtable 0x...14d8 inside sdk) and calls through its null function pointer
-(`main+0xF04E8 blr x8`, PC=0). The chain starts at `main+0xCF25A8`, which fills a settings struct
-with a fixed 0x1BD600000 (7.46 GB) budget and 0xB000000/0xC000000 sizes. Whatever should supply
-the object's memory returns 0 without any failing SVC or IPC call in the trace. The next step is to
-find that source, probably an SDK memory API whose Switch 2 behaviour differs (VAMM / address
-region / `nn::mem`).
-
-**Title 2** (`second switch game`): can't be tested with these files. Its rtld is intact, but the
-`main`, `sdk` and `subsdk0` segments fail LZ4 decompression or their SHA-256 check (checked with
-`a64_coverage.py`'s decoder and Eden's `nso.cpp:124`). The dump/extraction needs redoing.
-
-**Title 3** (`third switch game`, 04005B902517C000): same path as title 1 through nn::oe, then
-`IApplicationFunctions` 321 (unknown) and an abort with result 2002-1002 (fs, path not found) from
-game code. Most likely the game's first file/RomFS mount; the title's `romfs/` folder isn't wired up
-for loose-ExeFS boots yet.
-
-**Title 4** (`fourth switch game/base/exefs`): ExeFS has `main` but no `main.npdm`; not run.
+All runs: `EDEN_NX2_AUTO_STUB=1`, 12 GB memory layout, loose ExeFS with the sibling `romfs/`.
+
+**Title 4** (`fourth switch game\exefs`, 0400380020056000) gets furthest. Path:
+rtld -> nn::init -> nnosInitialize -> VAMM -> nn::oe (applet proxy, window, focus, graphics
+scale) -> fsp-srv + RomFS reads -> `nnMain` -> `nn::gs::sdk::BootGraphicsSystem`:
+- `nn::os::GetRemoteMemoryUsage` (svcGetInfo 31/32) -> stubbed 0/0.
+- `nn::gsv::BootWithRemoteMemory`: gets a session from `ISelfController` 2000 and sends TIPC
+ 4 (region, process handle, pid), 11, 101, 1501, 1502. Placeholder `nx2:gsv` accepts all.
+- `nn::nvsrv::BootWithRemoteMemory`: `gsv` 1402 returns the nvsrv session (handle at word 3,
+ result at word 4); nvsrv command 13 follows. Placeholder `nx2:nvsrv` accepts all.
+- `nn::syncpt::sdk::InitializeEx`: `gsv` 201, then 202 returns the syncpt session (same reply
+ layout). Placeholder `nx2:syncpt`. See the end of this section for the latest stop.
+
+**Title 3** (`third switch game\exefs`, 04005B902517C000): same path, plus `GetPseudoDeviceId`
+(fixed), and reaches `nn::nvsrv::BootWithRemoteMemory` like title 4.
+
+**Title 1** (`first switch game\exefs`, 0400C3F00006E000, `Drive.nss`): stops earlier, inside
+`nnMain` (`nnMain+0x40 .. +0xb74`): it builds an SDK allocator object at address 0 (writes to
+0x0..0xF8, sdk vtable) and calls through its null function pointer (`main+0xF04E8`). The chain at
+`main+0xCF25A8` sets a fixed 0x1BD600000 (7.46 GB) budget. No failing SVC or IPC precedes it, so
+some Switch 2 memory API returns a null base without an error.
+
+**Title 2** (`second switch game`, base and `updates`): main/sdk/subsdk0 segments fail LZ4 and
+SHA-256 checks; can't be run from these files.
+
+## What a picture needs
+
+Everything from `gsv` on is placeholder IPC: commands succeed with empty replies. The graphics
+stack is now being booted against servers that do nothing. A title screen needs:
+1. A real `gsv`/`nvsrv`/`syncpt` model: what each TIPC/CMIF command takes and returns (handles,
+ shared memory, events, syncpoint ids). This replaces Switch 1's `nvdrv` ioctl interface.
+2. NVN2 command buffers decoded and executed: Ampere-class 3D/compute/copy engines, which Eden's
+ `video_core` (Maxwell/Pascal) doesn't implement, plus a display/present path (vi/hos-binder or
+ its Switch 2 replacement).
+3. Shader translation for the Ampere SASS the game ships.
+That's the bulk of a Switch 2 emulator and well beyond boot fixes.
## Prioritized next steps
-1. **Title 1 null allocator**: break at `main+0xCF25A8` (cdb or the SVC trace plus a dynarmic
- breakpoint) and find which SDK call should hand back the allocator's memory. Likely a
- VAMM/address-region or `nn::mem` API that relies on the ids 29/30/33 semantics guessed here.
-2. **Pin down the new svcGetInfo ids (29, 30, 33)** from the SDK code that consumes them; they
- decide memory layout for every later step.
-3. **RomFS/save for loose ExeFS** (title 3's 2002-1002): mount `../romfs` next to `exefs`, then
- trace the fs IPC that fails.
-4. **New IPC commands**: `IApplicationProxy` 40/60 (now stub interfaces; log what is called on
- them), `IApplicationFunctions` 321, `appletOE` 10 args. Each needs its real reply layout.
-5. **Six cores**: the folding hack works for boot but changes scheduling; a 6-core kernel/CPU
- manager would be more faithful.
-6. **Rare SIMD encodings** still undecoded (~60 in title 1, e.g. `5f119f9c`, `4ea05c42`,
- `0f413044`): FP16 by-element / v8.2 SIMD forms. They'll trap when reached.
-7. **GPU**: nvdrv/NVN2 (Ampere) is untouched; every title will stop at its first nvdrv ioctl once
- the steps above are done. Start by logging nvdrv ioctls and stubbing what blocks boot.
-8. Upstream-worthy pieces independent of Switch 2: MSVC 14.51 build fixes, eden-cli SDL3
- lifetime fixes, the ExeFS loader fix, FAC/FAH offset fix.
+1. **Keep walking title 4's graphics boot** with the IPC trace: each `nx2:` stub call in the log
+ is a command whose reply layout is needed. Use `nso_syms.py` + `nso_dis.py` on the SDK caller
+ to read the expected reply words (as done for 1402/202).
+2. **Title 1 null allocator**: break at `main+0xCF25A8` and find which SDK memory call returns a
+ null base; likely tied to the guessed svcGetInfo 29/30/33 meanings.
+3. **Pin down svcGetInfo 29/30/31/32/33** from the SDK code that consumes them.
+4. **New app-layer commands**: `IApplicationProxy` 40, `ISelfController` 2400, `IGraphicsScale`
+ 1050, `IApplicationFunctions` 320/321 are auto-stubbed; check their real outputs.
+5. **Six cores** instead of folding 4/5 onto 0/1.
+6. **Rare SIMD encodings** still undecoded (~60-110 per title in sdk).
+7. Upstream-worthy pieces independent of Switch 2: MSVC 14.51 build fixes, eden-cli SDL3
+ lifetime fixes, ExeFS loader fixes (directory detection, RomFS, fs registration), FAC/FAH
+ offset fix, GetPseudoDeviceId without NACP.