From 165cd884cace3fef2e15d9e11e85abb892722ab5 Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 9 Oct 2026 15:40:08 +0200 Subject: [PATCH] agc: initialize function table in tracked driver memory --- src/libs/agc.cpp | 81 ++++++++++++++++++++++++++ tests/ShaderRecompilerComputeTests.cpp | 49 ++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index 151c5546e..cbf5b730d 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -20,6 +20,7 @@ #include "graphics/presentation/videoOut.h" #include "graphics/presentation/window.h" #include "graphics/shader/shader.h" +#include "kernel/memory.h" #include "kernel/pthread.h" #include "libs/errno.h" #include "libs/libs.h" @@ -44,6 +45,23 @@ namespace Libs::Graphics { static RenderContext* g_renderer = nullptr; +namespace Gen5Driver { + +static constexpr uint64_t DriverDmemBase = 0x0fe0000000ull; +static constexpr uint32_t DriverDmemSize = 0x200000; +static constexpr uint32_t AgcDmemOffset = 0x40000; + +struct DriverDmem { + std::mutex mutex; + int64_t physical_offset = -1; +}; + +static DriverDmem g_driver_dmem; +static int InitializeDmem(); +static void ReleaseDmem(); + +} // namespace Gen5Driver + template static void AgcTrace(const char* format, const Args&... args) { if (Config::GraphicsDebugDumpEnabled()) { @@ -70,6 +88,7 @@ void Initialize() { void Shutdown() { EXIT_IF(g_renderer == nullptr); g_renderer->ShutdownGpu(); + Gen5Driver::ReleaseDmem(); VideoOut::VideoOutShutdown(); WindowShutdown(); g_renderer = nullptr; @@ -337,6 +356,38 @@ int KYTY_SYSV_ABI AgcInit(uint32_t* state, uint32_t ver) { } printf("version = %u\n", ver); + std::scoped_lock lock {Gen5Driver::g_driver_dmem.mutex}; + if (Gen5Driver::g_driver_dmem.physical_offset >= 0) { + return OK; + } + if (const auto result = Gen5Driver::InitializeDmem(); result != OK) { + return result; + } + struct FunctionShaderBindings { + uint64_t code; + uint32_t user_data[2]; + }; + static_assert(sizeof(FunctionShaderBindings) == 16); + // Ordered-count queue validator, including its return to the caller. + static constexpr std::array validator { + 0xbeeb03ff, 0x00000021, 0xb96a1818, 0xbf06106a, + 0xbf800000, 0x8590807e, 0xb96a0a18, 0xbf066a80, + 0xbf800000, 0x85ea807e, 0x88ea106a, 0xbf870003, + 0xbefc03ff, 0x8a6ca000, 0xbf920009, 0xbefd210e, + }; + const auto base = Gen5Driver::DriverDmemBase + Gen5Driver::AgcDmemOffset; + const auto function_table = base + 0x60; + const auto code_address = base + 0x100; // Shader code requires 256-byte alignment. + std::array functions {}; + functions[2].code = code_address; + const std::array descriptor { + static_cast(function_table), + static_cast(function_table >> 32u) | 0x00100000u, + static_cast(functions.size()), 0x5204u, + }; + std::memcpy(reinterpret_cast(function_table), functions.data(), sizeof(functions)); + std::memcpy(reinterpret_cast(code_address), validator.data(), sizeof(validator)); + std::memcpy(reinterpret_cast(base), descriptor.data(), sizeof(descriptor)); return OK; } @@ -4224,6 +4275,36 @@ namespace Gen5Driver { LIB_NAME("Graphics5Driver", "Graphics5Driver"); +static int InitializeDmem() { + int64_t physical_offset = -1; + auto result = LibKernel::Memory::KernelAllocateDirectMemory( + 0, LibKernel::Memory::KernelGetDirectMemorySize(), DriverDmemSize, DriverDmemSize, + 12, &physical_offset); + if (result != OK) { + return result; + } + void* address = reinterpret_cast(DriverDmemBase); + result = LibKernel::Memory::KernelMapNamedDirectMemory( + &address, DriverDmemSize, 0x33, 0, physical_offset, DriverDmemSize, "SceAgcDriver"); + if (result != OK) { + EXIT_IF(LibKernel::Memory::KernelCheckedReleaseDirectMemory(physical_offset, DriverDmemSize) != OK); + return result; + } + EXIT_IF(reinterpret_cast(address) != DriverDmemBase); + std::memset(address, 0, DriverDmemSize); + g_driver_dmem.physical_offset = physical_offset; + return OK; +} + +static void ReleaseDmem() { + std::scoped_lock lock {g_driver_dmem.mutex}; + if (g_driver_dmem.physical_offset >= 0) { + EXIT_IF(LibKernel::Memory::KernelCheckedReleaseDirectMemory( + g_driver_dmem.physical_offset, DriverDmemSize) != OK); + g_driver_dmem.physical_offset = -1; + } +} + struct TessellationDriverState { uint64_t tf_ring_base = 0; uint32_t tf_ring_size = 0; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index aaa66741e..58c607ebc 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -41326,6 +41326,53 @@ void CheckShaderFusion() { std::printf("[host] %-32s ok\n", "ShaderFusion"); } +void CheckAgcSystemTable(RenderContext &renderer) { + constexpr const char *name = "AgcSystemTable"; + constexpr uint64_t driver_base = 0x0fe0000000ull; + constexpr uint64_t table_address = driver_base + 0x40000; + constexpr std::array native_validator{ + 0xbeeb03ff, 0x00000021, 0xb96a1818, 0xbf06106a, + 0xbf800000, 0x8590807e, 0xb96a0a18, 0xbf066a80, + 0xbf800000, 0x85ea807e, 0x88ea106a, 0xbf870003, + 0xbefc03ff, 0x8a6ca000, 0xbf920009, 0xbefd210e}; + LibKernel::Memory::InstallGpuResources(&renderer); + uint32_t state = 0xabcdef01u; + Require(name, "driver startup", Gen5::AgcInit(&state, 13) == 0 && state == 0xabcdef01u, + "Agc startup failed or changed its ignored public state pointer"); + LibKernel::Memory::VirtualQueryInfo mapping{}; + Require(name, "shared driver backing", + LibKernel::Memory::KernelVirtualQuery(reinterpret_cast(driver_base), 0, + &mapping, sizeof(mapping)) == 0 && + mapping.start == driver_base && mapping.end == driver_base + 0x200000 && + mapping.memory_type == 12 && mapping.protection == 0x33 && mapping.is_direct && + std::strcmp(mapping.name, "SceAgcDriver") == 0 && + renderer.IsMapped(driver_base, 0x200000), + "driver startup bypassed shared DMEM metadata or GPU range registration"); + ShaderBufferResource descriptor{}; + std::array functions{}; + std::array code{}; + Require(name, "ordered-count function backing", + LibKernel::Memory::TryReadBacking(table_address, &descriptor, sizeof(descriptor)) && + descriptor.Base48() == table_address + 0x60 && descriptor.Stride() == 16 && + descriptor.NumRecords() == 8 && descriptor.fields[3] == 0x5204 && + LibKernel::Memory::TryReadBacking(descriptor.Base48(), functions.data(), + sizeof(functions)) && + functions[4] != 0 && (functions[4] & 0xffu) == 0 && functions[5] == 0 && + LibKernel::Memory::TryReadBacking(functions[4], code.data(), sizeof(code)) && + code == native_validator, + "system descriptor did not reach the queue validator and its user data"); + auto *instrumentation = reinterpret_cast(table_address + 0x18); + Require(name, "default instrumentation", *instrumentation == 0, + "unused vertex validation was enabled at startup"); + *instrumentation = 0x12340000u; + Require(name, "repeat startup", Gen5::AgcInit(nullptr, 13) == 0 && + *instrumentation == 0x12340000u, + "repeat Agc initialization reset existing system state"); + *instrumentation = 0; + LibKernel::Memory::InstallGpuResources(nullptr); + std::printf("[host] %-32s ok\n", name); +} + struct CommandBufferLayout { using Callback = KYTY_SYSV_ABI bool (*)(Gen5::CommandBuffer *, uint32_t, void *); @@ -42749,6 +42796,7 @@ int main(int argc, char **argv) { if (argc == 2 && std::strcmp(argv[1], "--gpu-command-lane-only") == 0) { VulkanHarness vulkan; vulkan.CheckGpuCommandLane(); + CheckAgcSystemTable(vulkan.RuntimeRenderer()); CheckPm4IndirectControlFlow(vulkan.RuntimeRenderer()); CheckPm4WaitResume(vulkan.RuntimeRenderer()); CheckPm4RewindResume(vulkan.RuntimeRenderer()); @@ -43181,6 +43229,7 @@ int main(int argc, char **argv) { CheckPm4DepthControlHighBits(vulkan.RuntimeRenderer()); CheckPm4DepthRenderOverride(vulkan.RuntimeRenderer()); CheckShaderFusion(); + CheckAgcSystemTable(vulkan.RuntimeRenderer()); CheckPm4WaitPackets(vulkan.RuntimeRenderer()); CheckPm4DrawIndirectMultiPacket(vulkan.RuntimeRenderer()); CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());