aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp18
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h1
-rw-r--r--src/core/hle/service/nx2_gsv.h19
-rw-r--r--tools/nx2/gdbrsp.py40
4 files changed, 74 insertions, 4 deletions
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 3c9e42f602..0733b5afaf 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -94,7 +94,7 @@ void nvhost_as_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
void nvhost_as_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
- LOG_DEBUG(Service_NVDRV, "called, big_page_size={:#x}", params.big_page_size);
+ LOG_DEBUG(Service_NVDRV, "called, flags={:#x} big_page_size={:#x} va_range_start={:#x} va_range_end={:#x} va_range_split={:#x}", params.flags, params.big_page_size, params.va_range_start, params.va_range_end, params.va_range_split);
std::scoped_lock lock(mutex);
@@ -125,6 +125,15 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
vm.va_range_start = params.va_range_start;
vm.va_range_split = params.va_range_split;
vm.va_range_end = params.va_range_end;
+ if (vm.va_range_split == 0) {
+ // Switch 2 NvRm (flags 5, no big page size) asks for small pages over the whole range
+ // and then reserves 4 KiB VA up to 512 GiB. Keep a small big-page region past the end
+ // so both allocators stay valid.
+ vm.va_range_split = vm.va_range_end;
+ vm.va_range_end += 4ULL << 30;
+ vm.absolute_big_page_limit = true;
+ LOG_WARNING(Service_NVDRV, "No VA split given; small pages up to {:#x}", vm.va_range_split);
+ }
}
const u64 max_big_page_bits = Common::Log2Ceil(vm.va_range_end);
@@ -134,8 +143,11 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
vm.small_page_allocator.emplace(start_pages, end_pages);
const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)};
- const auto end_big_pages{
- static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
+ // The Switch 1 layout passes the big-page region length as the limit (so the limit stays above
+ // the start there); a split past the region length needs the absolute end instead.
+ const auto end_big_pages{static_cast<u32>(
+ (vm.absolute_big_page_limit ? vm.va_range_end : vm.va_range_end - vm.va_range_split) >>
+ vm.big_page_size_bits)};
vm.big_page_allocator.emplace(start_big_pages, end_big_pages);
gmmu = std::make_unique<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, vm.big_page_size_bits, VM::PAGE_SIZE_BITS);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index b13b1c9efa..9102252c96 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -218,6 +218,7 @@ private:
std::optional<Allocator> small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel
bool initialised{};
+ bool absolute_big_page_limit{}; //!< Switch 2 layout: big-page limit is va_range_end
} vm;
std::unique_ptr<Tegra::MemoryManager> gmmu;
};
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h
index 5494b46497..a7d9817dd2 100644
--- a/src/core/hle/service/nx2_gsv.h
+++ b/src/core/hle/service/nx2_gsv.h
@@ -110,6 +110,8 @@ public:
{12, &INx2GpuSession::GetIdEvent, "GetIdEvent (guessed)"},
{21, &INx2GpuSession::Start, "Start (guessed)"},
{22, &INx2GpuSession::OpenSubSession, "OpenSubSession (guessed)"},
+ {33, &INx2GpuSession::GetCount33, "Unknown33 (u32 count)"},
+ {203, &INx2GpuSession::GetVaRange, "GetVaRange (guessed)"},
});
}
@@ -153,6 +155,23 @@ private:
rb.Push(ResultSuccess);
}
+ void GetCount33(HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) nx2:gpu TIPC 33 [2]={:#x}, returning 1", ctx.CommandBuffer()[2]);
+ TipcReplyWords(ctx, {1});
+ }
+
+ // Used by nn::gpu::sdk::CreateGpuAddressSpace: three u64s after the result, a kind (< 5, 0 =
+ // none) and what looks like a GPU VA range. Report kind 1, [0x10000000, +64 GiB).
+ void GetVaRange(HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) nx2:gpu TIPC 203 [2]={:#x}", ctx.CommandBuffer()[2]);
+ constexpr u64 kind = 1;
+ constexpr u64 start = 0x10000000ULL;
+ constexpr u64 size = 64ULL << 30;
+ TipcReplyWords(ctx, {static_cast<u32>(kind), static_cast<u32>(kind >> 32),
+ static_cast<u32>(start), static_cast<u32>(start >> 32),
+ static_cast<u32>(size), static_cast<u32>(size >> 32)});
+ }
+
void OpenSubSession(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) nx2:gpu TIPC 22, returning a nx2:gpu-sub session");
ReturnRawSession(system, ctx,
diff --git a/tools/nx2/gdbrsp.py b/tools/nx2/gdbrsp.py
index 03ffbd00f5..ba81c33c1a 100644
--- a/tools/nx2/gdbrsp.py
+++ b/tools/nx2/gdbrsp.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# Minimal GDB remote-serial-protocol client for Eden's gdbstub (AArch64 guests).
# Usage: gdbrsp.py <port> <script>
-# Script lines: break <hexaddr> | unbreak <hexaddr> | continue | regs | mem <hexaddr|xN> <len> | bt <hexaddr|sp> <count>
+# Script lines: break <hexaddr> | unbreak <hexaddr> | continue | regs | x0 | mem <hexaddr|xN> <len> | bt <hexaddr|sp> <count>
# | quit
import socket
import struct
@@ -76,10 +76,36 @@ def main():
op = parts[0]
if op == "break":
print("break", parts[1], "->", rsp.cmd(f"Z0,{int(parts[1], 16):x},4"))
+ elif op == "breaklr":
+ # Break at the current return address (x30 from the last regs/x0).
+ lr = regs.get("x30", 0) & 0xFFFFFFFFFFFF
+ print("breaklr", hex(lr), "->", rsp.cmd(f"Z0,{lr:x},4"))
+ elif op == "unbreakpc":
+ pc = regs.get("pc", 0)
+ print("unbreakpc", hex(pc), "->", rsp.cmd(f"z0,{pc:x},4"))
elif op == "unbreak":
print("unbreak", parts[1], "->", rsp.cmd(f"z0,{int(parts[1], 16):x},4"))
+ elif op == "step":
+ print("step:", rsp.cmd("s"))
+ elif op == "trace":
+ # Single-step N instructions, printing pc and x0 (stop early at an optional pc).
+ n = int(parts[1])
+ stop_at = int(parts[2], 16) if len(parts) > 2 else None
+ lo = int(parts[3], 16) if len(parts) > 4 else 0
+ hi = int(parts[4], 16) if len(parts) > 4 else 1 << 64
+ for _ in range(n):
+ rsp.cmd("s")
+ r = rsp.regs()
+ if lo <= r["pc"] < hi:
+ print(f" t pc={r['pc']:#x} x0={r['x0']:#x} x8={r['x8']:#x}")
+ if stop_at is not None and r["pc"] == stop_at:
+ break
elif op == "continue":
print("stop:", rsp.cmd("c"))
+ elif op == "x0":
+ r = rsp.regs()
+ regs = r
+ print(f" pc={r.get('pc', 0):#x} x0={r.get('x0', 0):#x} x1={r.get('x1', 0):#x} lr={r.get('x30', 0):#x}")
elif op == "regs":
regs = rsp.regs()
print(" ".join(f"{k}={v:#x}" for k, v in regs.items()))
@@ -89,6 +115,18 @@ def main():
for i in range(0, len(data), 16):
words = [struct.unpack_from("<Q", data, i + j)[0] for j in range(0, min(16, len(data) - i) - 7, 8)]
print(f" {a + i:#x}: " + " ".join(f"{w:016x}" for w in words))
+ elif op == "fp":
+ # Walk the frame-pointer chain: print return addresses.
+ fp = regs.get("x29", 0)
+ for _ in range(int(parts[1]) if len(parts) > 1 else 12):
+ d = rsp.mem(fp, 16)
+ if len(d) < 16:
+ break
+ nfp, ret = struct.unpack("<QQ", d)
+ print(f" frame fp={fp:#x} ret={ret & 0xFFFFFFFFFFFF:#x}")
+ if nfp <= fp:
+ break
+ fp = nfp
elif op == "quit":
break
rsp.s.close()