agc: initialize function table in tracked driver memory

This commit is contained in:
nmzik
2026-10-09 15:40:08 +02:00
parent 973809cde0
commit 165cd884ca
2 changed files with 130 additions and 0 deletions
+81
View File
@@ -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 <typename... Args>
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<uint32_t, 16> 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<FunctionShaderBindings, 8> functions {};
functions[2].code = code_address;
const std::array<uint32_t, 4> descriptor {
static_cast<uint32_t>(function_table),
static_cast<uint32_t>(function_table >> 32u) | 0x00100000u,
static_cast<uint32_t>(functions.size()), 0x5204u,
};
std::memcpy(reinterpret_cast<void*>(function_table), functions.data(), sizeof(functions));
std::memcpy(reinterpret_cast<void*>(code_address), validator.data(), sizeof(validator));
std::memcpy(reinterpret_cast<void*>(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<void*>(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<uint64_t>(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;
+49
View File
@@ -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<uint32_t, 16> 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<void *>(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<uint64_t, 16> functions{};
std::array<uint32_t, 16> 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<uint64_t *>(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());