aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-27 17:46:02 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-27 17:46:02 -0400
commit06cc8292f85e507d6456532550a95016785a6695 (patch)
tree6d6e1e63226d8d81440f5ccc450b6e1aec8bd10c
parent4af363d798adf3b906fcfa5cba37ec9304f27dab (diff)
Switch 2 (title 1): syncpt handshake, gpu user/gsv partitions
Newer SDKs open the syncpt session with TIPC 0(u64) and need it echoed, and query the GPU address space partitions with gpu 201/202 instead of 203. With these and the larger memory budget, title 1 reaches its main loop and the same empty-command-buffer stop as title 4.
-rw-r--r--NX2_NOTES.md11
-rw-r--r--src/core/hle/service/nx2_gsv.h27
2 files changed, 34 insertions, 4 deletions
diff --git a/NX2_NOTES.md b/NX2_NOTES.md
index 034e185d24..ee9cc8dd8f 100644
--- a/NX2_NOTES.md
+++ b/NX2_NOTES.md
@@ -97,6 +97,8 @@ With the patched decoder, games 1/3/4 only leave ~60-110 rare SIMD encodings in
| 39 | every audio `RequestUpdate` fails | updates tagged `REv?` (lowercase v), renderer opened with `REV?` | compare revision numbers (2e6c39c) |
| 40 | main thread parked in `ExecuteVirtualMachineMapBufferEx` | gpu sub 211's job slot is request word 17, not word 2 | signal word 17's event (2e6c39c) |
| 41 | "too many files open" reading RomFS | eden-cli never raised the CRT stdio limit | `_setmaxstdio(8192)` (2e6c39c) |
+| 43 | (title 1) `nn::syncpt::sdk::Initialize` abort | newer SDK opens syncpt with TIPC 0(u64 token) and aborts unless it is echoed | handshake echo |
+| 44 | (title 1) NVN closes the GPU device after GetVARegions | newer SDK asks gpu 201/202(id) for the user/gsv partitions (two u64s each, stored as min/max) instead of using 203 | 201 = [0x8000000, 0xC000000000), 202 empty |
| 42 | GPU memory model | gpu sub 101 = `CreateGpuAccessibleMemory` (reply word 3 = GpuMemoryHandle); 211 = `NormalMapGpuMemory4K` (words 3-4 first VA, 5-6 last, 7 handle, 8-9 offset, 17 slot), also into sparse regions at 0x81_xxxx_xxxx; 102/213 (`NormalUnmapGpuMemory4K`)/214 (`CreateSparseRegion`) complete through slot events (words 3/8/11) | real handles, `Nx2GpuMemoryRegistry::MapAt` with SMMU range reuse, slot signals |
Run settings used (in `build/bin/user/config/sdl2-config.ini`): `memory_layout_mode=4`
@@ -125,10 +127,11 @@ audio renderer -> Gyml sequence files -> main loop.
**Title 3**: last checked before the graphics work; should now track 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 and calls
-through its null function pointer (`main+0xF04E8`). Worth retrying now that Switch 2 processes
-get the larger memory budget (it sets a fixed 0x1BD600000 budget at `main+0xCF25A8`).
+**Title 1** (`first switch game\exefs`, 0400C3F00006E000, `Drive.nss`) now gets as far as
+title 4: the larger memory budget (it sees ~9.5 GB) cleared the old null-allocator stop, then
+blockers 43-44. It boots graphics, NVN and several GPU channels, polls input every frame, and
+hits the same empty NVN command buffer at ~14 s (identical writes to address 0), so that stop is
+shared, not title-specific.
**Title 2**: main/sdk/subsdk0 fail LZ4 and SHA-256 checks; can't be run from these files.
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h
index 0a85ef5926..ff1fe219e8 100644
--- a/src/core/hle/service/nx2_gsv.h
+++ b/src/core/hle/service/nx2_gsv.h
@@ -86,6 +86,7 @@ public:
// Placeholder for the Switch 2 syncpoint server (nn::syncpt), handed out by gsv 202. TIPC 1
// returns a u32 the SDK requires to be < 0x400 and then passes min(value, 63) to TIPC 21.
// Everything else is auto-stubbed through the nx2: prefix.
+// - TIPC 0(u64): handshake, echo the token (newer SDKs).
// - TIPC 22(u32 slot): an event handle for that slot (copy handle at word 3, result at word 4).
// NVN's NvOsEventPool asks for slots and multi-waits on them.
// - TIPC 201(slot, counter, u64 value, mode): signal the slot's event once the counter reaches
@@ -97,6 +98,7 @@ public:
explicit INx2SyncptSession(Core::System& system_)
: ServiceFramework{system_, "nx2:syncpt"}, m_context{system_, "nx2:syncpt"} {
RegisterHandlersTipc({
+ {0, &INx2SyncptSession::Handshake, "Handshake"},
{1, &INx2SyncptSession::GetCount, "GetCount (guessed)"},
{22, &INx2SyncptSession::GetSlotEvent, "GetSlotEvent (guessed)"},
{201, &INx2SyncptSession::RequestSlotEvent, "RequestSlotEvent (guessed)"},
@@ -114,6 +116,13 @@ public:
}
private:
+ // Newer SDKs (title 1) open with TIPC 0(u64 token) and abort unless the reply echoes it.
+ void Handshake(HLERequestContext& ctx) {
+ const auto* w = ctx.CommandBuffer();
+ LOG_INFO(Service, "nx2:syncpt TIPC 0 handshake {:#x}", u64{w[2]} | (u64{w[3]} << 32));
+ TipcReplyWords(ctx, {w[2], w[3]});
+ }
+
void GetCount(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) nx2:syncpt TIPC 1, returning 32");
TipcReplyWords(ctx, {32});
@@ -212,6 +221,8 @@ public:
{21, &INx2GpuSession::Start, "Start (guessed)"},
{22, &INx2GpuSession::OpenSubSession, "OpenSubSession (guessed)"},
{33, &INx2GpuSession::GetCount33, "Unknown33 (u32 count)"},
+ {201, &INx2GpuSession::GetUserPartition, "GetUserPartition (newer SDKs)"},
+ {202, &INx2GpuSession::GetGsvPartition, "GetGsvPartition (newer SDKs)"},
{203, &INx2GpuSession::GetVaRange, "GetVaRange (guessed)"},
// Sub-session commands (gpu 22 hands out a second session to this same object).
{101, &INx2GpuSession::SubRegisterMemory, "CreateGpuAccessibleMemory"},
@@ -281,6 +292,22 @@ private:
static_cast<u32>(size), static_cast<u32>(size >> 32)});
}
+ // Newer SDKs (title 1) query the address space's user and gsv partitions with 201/202(id):
+ // two u64s each, stored as the partition's min/max. The user value works whether it is read
+ // as (start, size) or (min, max). Title 4 never needed the gsv partition, so it stays empty.
+ void GetUserPartition(HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) nx2:gpu TIPC 201 [2]={:#x}", ctx.CommandBuffer()[2]);
+ constexpr u64 first = 0x8000000ULL;
+ constexpr u64 second = 0xC000000000ULL - first;
+ TipcReplyWords(ctx, {static_cast<u32>(first), static_cast<u32>(first >> 32),
+ static_cast<u32>(second), static_cast<u32>(second >> 32)});
+ }
+
+ void GetGsvPartition(HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) nx2:gpu TIPC 202 [2]={:#x}, empty", ctx.CommandBuffer()[2]);
+ TipcReplyWords(ctx, {0, 0, 0, 0});
+ }
+
void OpenSubSession(HLERequestContext& ctx) {
// Served by this object so sub-session jobs can signal the per-id events.
LOG_WARNING(Service, "(STUBBED) nx2:gpu TIPC 22, returning a sub-session on nx2:gpu");