#include "SceTypes.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include "prx/libc/include/GuestArena.hpp" #include "prx/libkernel/Apr/include/AprCommandBuffer.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #endif extern "C" { int APS5_VABI sceAmprCommandBufferConstructor(Apr::CommandBufferObject*); int APS5_VABI sceAmprAprCommandBufferConstructor(Apr::CommandBufferObject*, std::uint64_t*, std::uint64_t*); int APS5_VABI sceAmprCommandBufferSetBuffer(Apr::CommandBufferObject*, void*, std::uint32_t); int APS5_VABI sceAmprCommandBufferReset(Apr::CommandBufferObject*); void* APS5_VABI sceAmprCommandBufferClearBuffer(Apr::CommandBufferObject*); std::uint32_t APS5_VABI sceAmprCommandBufferGetCurrentOffset(const Apr::CommandBufferObject*); std::uint32_t APS5_VABI sceAmprCommandBufferGetNumCommands(const Apr::CommandBufferObject*); int APS5_VABI sceAmprCommandBufferWriteAddressOnCompletion(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint64_t); int APS5_VABI sceAmprCommandBufferPushMarker(Apr::CommandBufferObject*, const char*); int APS5_VABI sceAmprCommandBufferPushMarkerWithColor(Apr::CommandBufferObject*, const char*, std::uint32_t); int APS5_VABI sceAmprCommandBufferPopMarker(Apr::CommandBufferObject*); int APS5_VABI sceAmprCommandBufferSetMarker(Apr::CommandBufferObject*, const char*); int APS5_VABI sceAmprCommandBufferSetMarkerWithColor(Apr::CommandBufferObject*, const char*, const std::uint32_t*); std::uint64_t APS5_VABI sceAmprMeasureCommandSizePushMarker(const char*); std::uint64_t APS5_VABI sceAmprMeasureCommandSizePushMarkerWithColor(const char*, std::uint32_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizePopMarker(); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeSetMarker(const char*); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeSetMarkerWithColor(const char*, std::uint32_t); int APS5_VABI sceKernelAprSubmitCommandBuffer(const Apr::CommandBufferObject*, std::uint32_t); int APS5_VABI sceKernelAprSubmitCommandBufferAndGetId(const Apr::CommandBufferObject*, std::uint32_t, std::uint32_t*); int APS5_VABI sceKernelAprWaitCommandBuffer(std::uint32_t); int APS5_VABI sceAmprCommandBufferWaitOnAddress(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint64_t, std::uint8_t, std::uint8_t); int APS5_VABI sceAmprCommandBufferWaitOnCounter(Apr::CommandBufferObject*, std::uint8_t, std::uint32_t, std::uint8_t, std::uint8_t); int APS5_VABI sceAmprCommandBufferWriteCounterOnCompletion(Apr::CommandBufferObject*, std::uint8_t, std::uint32_t); int APS5_VABI sceAmprCommandBufferWriteAddressFromTimeCounterOnCompletion(Apr::CommandBufferObject*, volatile std::uint64_t*); int APS5_VABI sceAmprCommandBufferWriteAddressFromCounterOnCompletion(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint8_t); int APS5_VABI sceAmprCommandBufferWriteAddressFromCounterPairOnCompletion(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint8_t); int APS5_VABI sceAmprCommandBufferWriteKernelEventQueueOnCompletion(Apr::CommandBufferObject*, std::uint64_t, std::int32_t, std::uint64_t); int APS5_VABI sceAmprCommandBufferNop(Apr::CommandBufferObject*, std::uint32_t); int APS5_VABI sceAmprCommandBufferNopWithData(Apr::CommandBufferObject*, std::uint32_t, const std::uint32_t*); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeNop(std::uint32_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeNopWithData(std::uint32_t); int APS5_VABI sceAmprCommandBufferWaitOnAddress_04_00(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint64_t, std::uint8_t, std::uint8_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWaitOnAddress_04_00(volatile std::uint64_t*, std::uint64_t, std::uint8_t, std::uint8_t); int APS5_VABI sceAmprCommandBufferWriteAddressFromTimeCounter_04_00(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint64_t); int APS5_VABI sceAmprCommandBufferWriteAddressFromCounter_04_00(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint8_t, std::uint64_t); int APS5_VABI sceAmprCommandBufferWriteAddressFromCounterPair_04_00(Apr::CommandBufferObject*, volatile std::uint64_t*, std::uint8_t, std::uint64_t); int APS5_VABI sceAmprCommandBufferWriteKernelEventQueue_04_00(Apr::CommandBufferObject*, std::uint64_t, std::int32_t, std::uint64_t, std::uint64_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWriteAddressFromTimeCounter_04_00(volatile std::uint64_t*); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWriteAddressFromCounter_04_00(volatile std::uint64_t*, std::uint8_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWriteAddressFromCounterPair_04_00(volatile std::uint64_t*, std::uint8_t); int APS5_VABI sceAmprCommandBufferWaitOnCounter_04_00(Apr::CommandBufferObject*, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t); int APS5_VABI sceAmprCommandBufferWriteCounter_04_00(Apr::CommandBufferObject*, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t, std::uint8_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWaitOnCounter_04_00(std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeWriteCounter_04_00(std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t); int APS5_VABI sceAmprCommandBufferConstructNop(Apr::CommandBufferObject*, std::int16_t, const void*, std::uint32_t, const std::uint32_t*); int APS5_VABI sceAmprCommandBufferConstructMarker(Apr::CommandBufferObject*, std::uint32_t, const char*, const std::uint32_t*); int APS5_VABI sceKernelAprResolveFilepathsToIds(const char**, std::uint32_t, std::uint32_t*, std::uint32_t*); int APS5_VABI sceKernelAprResolveFilepathsToIdsAndFileSizes(const char**, std::uint32_t, std::uint32_t*, std::uint64_t*, std::uint32_t*); int APS5_VABI sceKernelAprResolveFilepathsToIdsAndFileSizesForEach(const char**, std::uint32_t, std::uint32_t*, std::uint64_t*, int*); int* APS5_VABI __error_nid_postfix(); int APS5_VABI sceAmprAprCommandBufferReadFile(Apr::CommandBufferObject*, std::uint64_t*, std::uint64_t*, std::uint32_t, void*, std::uint64_t, std::uint64_t); int APS5_VABI sceAmprAprCommandBufferReadFileGather(Apr::CommandBufferObject*, std::uint64_t*, std::uint64_t*, std::uint64_t, std::uint64_t); int APS5_VABI sceAmprAprCommandBufferReadFileScatter(Apr::CommandBufferObject*, std::uint64_t*, std::uint64_t*, void*, std::uint64_t); int APS5_VABI sceAmprAprCommandBufferReadFileGatherScatter(Apr::CommandBufferObject*, std::uint64_t*, std::uint64_t*, void*, std::uint64_t, std::uint64_t); int APS5_VABI sceAmprAprCommandBufferResetGatherScatterState(Apr::CommandBufferObject*); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeReadFileGather(std::uint64_t, std::uint64_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeReadFileScatter(void*, std::uint64_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeReadFileGatherScatter(void*, std::uint64_t, std::uint64_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeResetGatherScatterState(); std::size_t APS5_VABI sceKernelGetDirectMemorySize(); int APS5_VABI sceAmprAmmGetUsageStatsData(void*); int APS5_VABI sceAmprAmmCommandBufferConstructor(Apr::CommandBufferObject*); int APS5_VABI sceAmprAmmCommandBufferMap(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t); int APS5_VABI sceAmprAmmCommandBufferMapWithGpuMaskId(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::uint8_t); int APS5_VABI sceAmprAmmCommandBufferMapDirect(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t); int APS5_VABI sceAmprAmmCommandBufferMapDirectWithGpuMaskId(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::uint8_t); int APS5_VABI sceAmprAmmCommandBufferUnmap(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMap(std::uint64_t, std::uint64_t, std::int32_t, std::int32_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMapWithGpuMaskId(std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::uint8_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMapDirect(std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMapDirectWithGpuMaskId(std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::uint8_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeUnmap(std::uint64_t, std::uint64_t); int APS5_VABI sceAmprAmmGiveDirectMemory(std::int64_t, std::int64_t, std::size_t, std::size_t, int, std::int64_t*); int APS5_VABI sceAmprAmmGetVirtualAddressRanges(std::uint64_t*, std::uint64_t*, std::uint64_t*, std::uint64_t*); int APS5_VABI sceAmprAmmSubmitCommandBuffer(void*, std::uint32_t, std::uint32_t); int APS5_VABI sceAmprAmmSubmitCommandBuffer2(void*, std::uint32_t, std::uint32_t, std::uint64_t*, std::uint32_t*); int APS5_VABI sceAmprAmmSubmitCommandBuffer3(void*, std::uint32_t, std::uint32_t, std::uint32_t*); int APS5_VABI sceAmprAmmWaitCommandBufferCompletion(std::uint32_t); int APS5_VABI sceAmprAprCommandBufferMapBegin(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t); int APS5_VABI sceAmprAprCommandBufferMapDirectBegin(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t); int APS5_VABI sceAmprAprCommandBufferMapEnd(Apr::CommandBufferObject*); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeMapBegin(std::uint64_t, std::uint64_t, std::uint32_t, std::uint32_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeMapDirectBegin(std::uint64_t, std::uint64_t, std::uint64_t, std::uint32_t, std::uint32_t); std::uint64_t APS5_VABI sceAmprMeasureCommandSizeMapEnd(); int APS5_VABI sceKernelQueryMemoryProtection(void*, void**, void**, int*); int APS5_VABI sceKernelReserveVirtualRange(void**, std::size_t, int, std::size_t); int APS5_VABI sceKernelMapFlexibleMemory(void**, std::size_t, int, int); int APS5_VABI sceKernelMunmap(void*, std::size_t); int APS5_VABI sceAmprAmmCommandBufferMapAsPrt(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t); int APS5_VABI sceAmprAmmCommandBufferAllocatePaForPrt(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t); int APS5_VABI sceAmprAmmCommandBufferRemapIntoPrt(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::uint32_t); int APS5_VABI sceKernelWriteModifyMtypeProtectCommand(void*, std::uint64_t, std::uint64_t, std::uint64_t, std::uint64_t, std::uint64_t, std::uint64_t*); int APS5_VABI sceKernelWriteRemapIntoPrtCommand(std::uint32_t*, std::uint64_t, std::uint64_t, std::uint64_t, int, int, std::uint64_t*); int APS5_VABI sceAmprAmmCommandBufferUnmapToPrt(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMapAsPrt(std::uint64_t, std::uint64_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeAllocatePaForPrt(std::uint64_t, std::uint64_t, std::int32_t, std::int32_t); int APS5_VABI sceAmprAmmCommandBufferRemap(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t); int APS5_VABI sceAmprAmmCommandBufferRemapWithGpuMaskId(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::uint8_t); int APS5_VABI sceAmprAmmCommandBufferMultiMap(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t); int APS5_VABI sceAmprAmmCommandBufferMultiMapWithGpuMaskId(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::uint8_t); int APS5_VABI sceAmprAmmCommandBufferModifyProtect(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t); int APS5_VABI sceAmprAmmCommandBufferModifyProtectWithGpuMaskId(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::uint8_t); int APS5_VABI sceAmprAmmCommandBufferModifyMtypeProtect(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::int32_t); int APS5_VABI sceAmprAmmCommandBufferModifyMtypeProtectWithGpuMaskId(Apr::CommandBufferObject*, std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::int32_t, std::uint8_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeRemap(std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeRemapWithGpuMaskId(std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::uint8_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMultiMap(std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeMultiMapWithGpuMaskId(std::uint64_t, std::uint64_t, std::uint64_t, std::int32_t, std::uint8_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeModifyProtect(std::uint64_t, std::uint64_t, std::int32_t, std::int32_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeModifyProtectWithGpuMaskId(std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::uint8_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeModifyMtypeProtect(std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::int32_t); std::int64_t APS5_VABI sceAmprAmmMeasureAmmCommandSizeModifyMtypeProtectWithGpuMaskId(std::uint64_t, std::uint64_t, std::int32_t, std::int32_t, std::int32_t, std::uint8_t); int APS5_VABI sceKernelCreateEqueue(KernelEqueue*, const char*); int APS5_VABI sceKernelDeleteEqueue(KernelEqueue); int APS5_VABI sceKernelWaitEqueue(KernelEqueue, KernelEvent*, int, int*, const KernelUseconds*); int APS5_VABI sceKernelAddAmprEvent(KernelEqueue, int, void*); int APS5_VABI sceKernelDeleteAmprEvent(KernelEqueue, int); int APS5_VABI sceKernelGetEventFilter(const KernelEvent*); std::uintptr_t APS5_VABI sceKernelGetEventId(const KernelEvent*); std::intptr_t APS5_VABI sceKernelGetEventData(const KernelEvent*); void* APS5_VABI sceKernelGetEventUserData(const KernelEvent*); } static void RequireAt(bool value, int line) { if (value) return; std::fprintf(stderr, "GuestAmpr.cpp:%d: requirement failed\n", line); std::abort(); } #define Require(value) RequireAt((value), __LINE__) namespace { constexpr int invalidArgument = static_cast(0x80020016); constexpr int bufferFull = static_cast(0x8002001C); constexpr std::uint32_t color = 0xFF8040u; struct Recorder { alignas(8) std::array memory{}; Apr::CommandBufferObject buffer{}; std::uint64_t gatherState = 0; std::uint64_t scatterState = 0; explicit Recorder(std::uint32_t size = 4096) { Require(sceAmprCommandBufferConstructor(&buffer) == 0); Require(sceAmprAprCommandBufferConstructor(&buffer, &gatherState, &scatterState) == 0); Require(sceAmprCommandBufferSetBuffer(&buffer, memory.data(), size) == 0); } std::uint32_t Offset() const { return sceAmprCommandBufferGetCurrentOffset(&buffer); } std::uint32_t Commands() const { return sceAmprCommandBufferGetNumCommands(&buffer); } }; void RequireAppended(const Recorder& recorder, std::uint32_t offset, std::uint32_t commands, Apr::Opcode opcode, std::uint64_t measured) { Require(recorder.Offset() == offset + measured); Require(recorder.Commands() == commands + 1); Apr::CommandHeader header; std::memcpy(&header, recorder.memory.data() + offset, sizeof(header)); if (opcode == Apr::Opcode::CompactReadFileGather) { Require((static_cast(header.opcode) & 0xffu) == static_cast(opcode)); Require(measured == sizeof(Apr::CompactReadFileGatherCommand)); } else Require(header.opcode == opcode && header.bytes == measured); } void RequireRecorded(const Recorder& recorder, std::uint32_t offset, std::uint32_t commands, Apr::Opcode opcode, std::uint64_t measured, const std::string& text) { RequireAppended(recorder, offset, commands, opcode, measured); Require(measured >= sizeof(Apr::MarkerCommand) + text.size() + 1); Require(std::memcmp(recorder.memory.data() + offset + sizeof(Apr::MarkerCommand), text.c_str(), text.size() + 1) == 0); } void TestRecording(const std::string& text) { Recorder recorder; const char* marker = text.c_str(); auto offset = recorder.Offset(); auto commands = recorder.Commands(); Require(sceAmprCommandBufferPushMarker(&recorder.buffer, marker) == 0); RequireRecorded(recorder, offset, commands, Apr::Opcode::PushMarker, sceAmprMeasureCommandSizePushMarker(marker), text); offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprCommandBufferPushMarkerWithColor(&recorder.buffer, marker, color) == 0); RequireRecorded(recorder, offset, commands, Apr::Opcode::PushMarker, sceAmprMeasureCommandSizePushMarkerWithColor(marker, color), text); offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprCommandBufferSetMarker(&recorder.buffer, marker) == 0); RequireRecorded(recorder, offset, commands, Apr::Opcode::SetMarker, sceAmprMeasureCommandSizeSetMarker(marker), text); offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprCommandBufferSetMarkerWithColor(&recorder.buffer, marker, &color) == 0); RequireRecorded(recorder, offset, commands, Apr::Opcode::SetMarker, sceAmprMeasureCommandSizeSetMarkerWithColor(marker, color), text); offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprCommandBufferPopMarker(&recorder.buffer) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::PopMarker, sceAmprMeasureCommandSizePopMarker()); } void TestRejectedArguments() { Recorder recorder; Require(sceAmprCommandBufferPushMarker(&recorder.buffer, nullptr) == invalidArgument); Require(sceAmprCommandBufferPushMarkerWithColor(&recorder.buffer, nullptr, color) == invalidArgument); Require(sceAmprCommandBufferSetMarker(&recorder.buffer, nullptr) == invalidArgument); Require(sceAmprCommandBufferSetMarkerWithColor(&recorder.buffer, nullptr, &color) == invalidArgument); Require(sceAmprCommandBufferSetMarkerWithColor(&recorder.buffer, "frame", nullptr) == invalidArgument); Require(sceAmprCommandBufferPushMarker(nullptr, "frame") == invalidArgument); Require(sceAmprCommandBufferPushMarkerWithColor(nullptr, "frame", color) == invalidArgument); Require(sceAmprCommandBufferSetMarker(nullptr, "frame") == invalidArgument); Require(sceAmprCommandBufferSetMarkerWithColor(nullptr, "frame", &color) == invalidArgument); Require(sceAmprCommandBufferPopMarker(nullptr) == invalidArgument); Require(recorder.Offset() == 0 && recorder.Commands() == 0); const auto rejected = static_cast(static_cast(invalidArgument)); Require(sceAmprMeasureCommandSizePushMarker(nullptr) == rejected); Require(sceAmprMeasureCommandSizePushMarkerWithColor(nullptr, color) == rejected); Require(sceAmprMeasureCommandSizeSetMarker(nullptr) == rejected); Require(sceAmprMeasureCommandSizeSetMarkerWithColor(nullptr, color) == rejected); } void TestFullBuffer() { const char* marker = "streaming"; const auto measured = static_cast(sceAmprMeasureCommandSizePushMarker(marker)); Recorder exact(measured); Require(sceAmprCommandBufferPushMarker(&exact.buffer, marker) == 0); Require(exact.Offset() == measured && exact.Commands() == 1); Require(sceAmprCommandBufferPopMarker(&exact.buffer) == bufferFull); Require(sceAmprCommandBufferSetMarker(&exact.buffer, "") == bufferFull); Require(exact.Offset() == measured && exact.Commands() == 1); Recorder small(measured - 4); Require(sceAmprCommandBufferPushMarker(&small.buffer, marker) == bufferFull); Require(sceAmprCommandBufferSetMarkerWithColor(&small.buffer, marker, &color) == bufferFull); Require(small.Offset() == 0 && small.Commands() == 0); Apr::CommandBufferObject unbound{}; Require(sceAmprCommandBufferConstructor(&unbound) == 0); Require(sceAmprCommandBufferPushMarker(&unbound, marker) == bufferFull); Require(sceAmprCommandBufferPopMarker(&unbound) == bufferFull); } void TestOffsetPastBuffer() { constexpr std::array invalidOffsets{33u, 64u, 0xffffffffu}; for (const auto offset : invalidOffsets) { Recorder recorder(32); recorder.memory.fill(0xA5); recorder.buffer.offset = offset; recorder.buffer.numCommands = 7; const auto before = recorder.buffer; const auto memory = recorder.memory; const auto rejected = [&](const auto& record) { bool threw = false; try { record(); } catch (const std::invalid_argument&) { threw = true; } Require(threw); Require(recorder.memory == memory); Require(recorder.buffer.base == before.base && recorder.buffer.size == before.size); Require(recorder.buffer.offset == before.offset && recorder.buffer.numCommands == before.numCommands); Require(recorder.buffer.type == before.type && recorder.buffer.recording == before.recording); }; rejected([&] { sceAmprCommandBufferPopMarker(&recorder.buffer); }); rejected([&] { sceAmprCommandBufferNop(&recorder.buffer, 1); }); rejected([&] { sceAmprCommandBufferPushMarker(&recorder.buffer, "bounds"); }); rejected([&] { sceAmprAmmCommandBufferMap(&recorder.buffer, 0x4000, 0x4000, 0, 3); }); rejected([&] { sceAmprAmmCommandBufferMapAsPrt(&recorder.buffer, 0x4000, 0x4000); }); } } void TestSubmission() { Recorder recorder; std::uint64_t first = 0; std::uint64_t second = 0; Require(sceAmprCommandBufferPushMarker(&recorder.buffer, "level") == 0); Require(sceAmprCommandBufferWriteAddressOnCompletion(&recorder.buffer, &first, 0x1111) == 0); Require(sceAmprCommandBufferSetMarkerWithColor(&recorder.buffer, "textures", &color) == 0); Require(sceAmprCommandBufferPushMarkerWithColor(&recorder.buffer, std::string(200, 'm').c_str(), color) == 0); Require(sceAmprCommandBufferSetMarker(&recorder.buffer, "") == 0); Require(sceAmprCommandBufferPopMarker(&recorder.buffer) == 0); Require(sceAmprCommandBufferPopMarker(&recorder.buffer) == 0); Require(sceAmprCommandBufferWriteAddressOnCompletion(&recorder.buffer, &second, 0x2222) == 0); Require(recorder.Commands() == 8); Require(sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0) == 0); Require(first == 0x1111 && second == 0x2222); } void TestKernelEventQueue() { KernelEqueue eq = 0; int userData = 0; Require(sceKernelCreateEqueue(&eq, "ampr") == 0); Require(sceKernelAddAmprEvent(eq, 7, &userData) == 0); Recorder recorder; Require(sceAmprCommandBufferWriteKernelEventQueue_04_00(&recorder.buffer, static_cast(eq), 7, 0x1234, 0) == 0); Require(sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0) == 0); KernelEvent event{}; int count = 0; const KernelUseconds poll = 0; Require(sceKernelWaitEqueue(eq, &event, 1, &count, &poll) == 0 && count == 1); Require(sceKernelGetEventFilter(&event) == -25); Require(sceKernelGetEventId(&event) == 7 && sceKernelGetEventData(&event) == 0x1234 && sceKernelGetEventUserData(&event) == &userData); Require(sceKernelDeleteAmprEvent(eq, 7) == 0); Require(sceKernelDeleteEqueue(eq) == 0); } void TestWaits() { Recorder recorder; alignas(8) std::uint64_t value = 5; alignas(8) std::uint64_t done = 0; Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &value, 5, 0, 0) == 0); Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &value, 3, 1, 0) == 0); Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &value, 9, 2, 1) == 0); Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &value, 4, 3, 0) == 0); Require(sceAmprCommandBufferWriteAddressOnCompletion(&recorder.buffer, &done, 1) == 0); auto submitted = std::async(std::launch::async, [&]() { return sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0); }); Require(submitted.wait_for(std::chrono::seconds(10)) == std::future_status::ready); Require(submitted.get() == 0 && done == 1); } void TestWaitSatisfiedAfterSubmission() { Recorder first; Recorder second; alignas(8) std::uint64_t flag = 0; alignas(8) std::uint64_t done = 0; alignas(8) std::uint64_t later = 0; Require(sceAmprCommandBufferWaitOnAddress(&first.buffer, &flag, 1, 0, 0) == 0); Require(sceAmprCommandBufferWriteAddressOnCompletion(&first.buffer, &done, 1) == 0); Require(sceAmprCommandBufferWriteAddressOnCompletion(&second.buffer, &later, 2) == 0); std::uint32_t firstId = 0; auto submitted = std::async(std::launch::async, [&]() { return sceKernelAprSubmitCommandBufferAndGetId(&first.buffer, 0, &firstId); }); const bool returned = submitted.wait_for(std::chrono::seconds(5)) == std::future_status::ready; if (!returned) std::atomic_ref(flag).store(1, std::memory_order_release); Require(returned); Require(submitted.get() == 0 && firstId != 0); std::uint32_t secondId = 0; Require(sceKernelAprSubmitCommandBufferAndGetId(&second.buffer, 0, &secondId) == 0 && secondId > firstId); Require(std::atomic_ref(done).load(std::memory_order_acquire) == 0); Require(std::atomic_ref(later).load(std::memory_order_acquire) == 0); std::atomic_ref(flag).store(1, std::memory_order_release); Require(sceKernelAprWaitCommandBuffer(secondId) == 0); Require(done == 1 && later == 2); Require(sceKernelAprWaitCommandBuffer(firstId) == 0); } void TestAmmNotBehindAprWait() { Recorder reader; Recorder maps; alignas(8) std::uint64_t flag = 0; alignas(8) std::uint64_t done = 0; Require(sceAmprCommandBufferWaitOnAddress(&reader.buffer, &flag, 1, 0, 0) == 0); Require(sceAmprCommandBufferWriteAddressOnCompletion(&reader.buffer, &done, 1) == 0); Require(sceAmprAmmCommandBufferConstructor(&maps.buffer) == 0); Require(sceAmprCommandBufferWriteAddressOnCompletion(&maps.buffer, &flag, 1) == 0); std::uint32_t readerId = 0; Require(sceKernelAprSubmitCommandBufferAndGetId(&reader.buffer, 0, &readerId) == 0 && readerId != 0); std::uint32_t mapsId = 0; Require(sceAmprAmmSubmitCommandBuffer3(maps.memory.data(), maps.Offset(), 0, &mapsId) == 0 && mapsId != 0); auto completed = std::async(std::launch::async, [&]() { return sceAmprAmmWaitCommandBufferCompletion(mapsId); }); const bool returned = completed.wait_for(std::chrono::seconds(5)) == std::future_status::ready; if (!returned) std::atomic_ref(flag).store(1, std::memory_order_release); Require(returned); Require(completed.get() == 0); Require(sceKernelAprWaitCommandBuffer(readerId) == 0); Require(done == 1); } void TestCounters() { Recorder recorder; alignas(8) std::uint64_t single = 0; alignas(8) std::uint64_t pair = 0; Require(sceAmprCommandBufferWriteCounterOnCompletion(&recorder.buffer, 6, 7) == 0); Require(sceAmprCommandBufferWriteCounterOnCompletion(&recorder.buffer, 7, 9) == 0); Require(sceAmprCommandBufferWaitOnCounter(&recorder.buffer, 6, 7, 0, 0) == 0); Require(sceAmprCommandBufferWaitOnCounter(&recorder.buffer, 7, 8, 1, 1) == 0); Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, &single, 6) == 0); Require(sceAmprCommandBufferWriteAddressFromCounterPairOnCompletion(&recorder.buffer, &pair, 6) == 0); Require(sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0) == 0); Require(single == 7 && pair == (7ull | (9ull << 32u))); } void TestRejectedWaitsAndCounters() { Recorder recorder; alignas(8) std::uint64_t words[2] = {}; auto* misaligned = reinterpret_cast(reinterpret_cast(words) + 4); Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &words[0], 0, 4, 0) == invalidArgument); Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, &words[0], 0, 0, 2) == invalidArgument); Require(sceAmprCommandBufferWaitOnAddress(&recorder.buffer, misaligned, 0, 0, 0) == invalidArgument); Require(sceAmprCommandBufferWaitOnCounter(&recorder.buffer, 128, 0, 0, 0) == invalidArgument); Require(sceAmprCommandBufferWaitOnCounter(&recorder.buffer, 0, 0, 4, 0) == invalidArgument); Require(sceAmprCommandBufferWaitOnCounter(&recorder.buffer, 0, 0, 0, 2) == invalidArgument); Require(sceAmprCommandBufferWriteCounterOnCompletion(&recorder.buffer, 128, 0) == invalidArgument); Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, &words[0], 128) == invalidArgument); Require(sceAmprCommandBufferWriteAddressFromCounterPairOnCompletion(&recorder.buffer, &words[0], 128) == invalidArgument); Require(sceAmprCommandBufferWriteAddressFromCounterPairOnCompletion(&recorder.buffer, &words[0], 7) == invalidArgument); Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, nullptr, 0) == invalidArgument); Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, misaligned, 0) == invalidArgument); Require(sceAmprCommandBufferWriteAddressFromTimeCounterOnCompletion(&recorder.buffer, nullptr) == invalidArgument); Require(sceAmprCommandBufferWriteAddressOnCompletion(&recorder.buffer, misaligned, 0) == invalidArgument); Require(sceAmprCommandBufferWriteKernelEventQueueOnCompletion(&recorder.buffer, 0, 1, 0) == invalidArgument); Require(recorder.Offset() == 0 && recorder.Commands() == 0); } void TestNops() { Recorder recorder; const std::uint32_t data[3] = {0x11111111u, 0x22222222u, 0x33333333u}; for (std::uint32_t dwords = 1; dwords <= 16; ++dwords) { const auto offset = recorder.Offset(); const auto commands = recorder.Commands(); Require(sceAmprCommandBufferNop(&recorder.buffer, dwords) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNop(dwords)); } const auto offset = recorder.Offset(); const auto commands = recorder.Commands(); Require(sceAmprCommandBufferNopWithData(&recorder.buffer, 3, data) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNopWithData(4)); Require(std::memcmp(recorder.memory.data() + offset + sizeof(Apr::CommandHeader), data, sizeof(data)) == 0); Require(sceAmprCommandBufferNopWithData(&recorder.buffer, 0, nullptr) == 0); Require(sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0) == 0); const auto rejected = static_cast(static_cast(invalidArgument)); Require(sceAmprCommandBufferNop(&recorder.buffer, 0) == invalidArgument); Require(sceAmprCommandBufferNop(&recorder.buffer, 17) == invalidArgument); Require(sceAmprCommandBufferNopWithData(&recorder.buffer, 16, data) == invalidArgument); Require(sceAmprMeasureCommandSizeNop(0) == rejected && sceAmprMeasureCommandSizeNop(17) == rejected); Require(sceAmprMeasureCommandSizeNopWithData(0) == rejected && sceAmprMeasureCommandSizeNopWithData(17) == rejected); } void TestVersionedCommands() { Recorder recorder; alignas(8) std::uint64_t value = 0x8000000000000005ull; alignas(8) std::uint64_t single = 0; alignas(8) std::uint64_t pair = 0; alignas(8) std::uint64_t time = 0; Require(sceAmprCommandBufferWaitOnAddress_04_00(&recorder.buffer, &value, 0x8000000000000003ull, 4, 0) == 0); Require(sceAmprCommandBufferWaitOnAddress_04_00(&recorder.buffer, &value, 1, 6, 1) == 0); Require(sceAmprCommandBufferWaitOnAddress_04_00(&recorder.buffer, &value, 0x8000000000000000ull, 5, 0) == 0); Require(sceAmprCommandBufferWriteCounterOnCompletion(&recorder.buffer, 10, 3) == 0); Require(sceAmprCommandBufferWriteCounterOnCompletion(&recorder.buffer, 11, 4) == 0); Require(sceAmprCommandBufferWriteAddressFromCounter_04_00(&recorder.buffer, &single, 10, 1) == 0); Require(sceAmprCommandBufferWriteAddressFromCounterPair_04_00(&recorder.buffer, &pair, 10, 0) == 0); Require(sceAmprCommandBufferWriteAddressFromTimeCounter_04_00(&recorder.buffer, &time, 1) == 0); auto submitted = std::async(std::launch::async, [&]() { return sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0); }); Require(submitted.wait_for(std::chrono::seconds(10)) == std::future_status::ready); Require(submitted.get() == 0 && single == 3 && pair == (3ull | (4ull << 32u)) && time != 0); const auto rejected = static_cast(static_cast(invalidArgument)); Recorder empty; Require(sceAmprCommandBufferWaitOnAddress_04_00(&empty.buffer, nullptr, 0, 0, 0) == invalidArgument); Require(sceAmprCommandBufferWaitOnAddress_04_00(&empty.buffer, &value, 0, 7, 0) == invalidArgument); Require(sceAmprCommandBufferWaitOnAddress_04_00(&empty.buffer, &value, 0, 0, 2) == invalidArgument); Require(sceAmprCommandBufferWriteAddressFromCounterPair_04_00(&empty.buffer, &pair, 11, 0) == invalidArgument); Require(sceAmprCommandBufferWriteAddressFromTimeCounter_04_00(&empty.buffer, nullptr, 0) == invalidArgument); Require(sceAmprCommandBufferWriteKernelEventQueue_04_00(&empty.buffer, 0, 1, 0, 0) == invalidArgument); Require(empty.Offset() == 0 && empty.Commands() == 0); Require(sceAmprMeasureCommandSizeWaitOnAddress_04_00(nullptr, 0, 0, 0) == rejected); Require(sceAmprMeasureCommandSizeWaitOnAddress_04_00(&value, 0, 7, 0) == rejected); Require(sceAmprMeasureCommandSizeWriteAddressFromTimeCounter_04_00(nullptr) == rejected); Require(sceAmprMeasureCommandSizeWriteAddressFromCounter_04_00(&single, 128) == sizeof(Apr::WriteAddressFromCounterCommand)); Require(sceAmprMeasureCommandSizeWriteAddressFromCounter_04_00(nullptr, 0) == rejected); Require(sceAmprMeasureCommandSizeWriteAddressFromCounterPair_04_00(&pair, 128) == sizeof(Apr::WriteAddressFromCounterCommand)); Require(sceAmprMeasureCommandSizeWriteAddressFromCounterPair_04_00(&pair, 11) == rejected); Require(sceAmprMeasureCommandSizeWaitOnAddress_04_00(&value, 0, 6, 1) == sizeof(Apr::WaitCommand)); Require(sceAmprMeasureCommandSizeWriteAddressFromCounterPair_04_00(&pair, 10) == sizeof(Apr::WriteAddressFromCounterCommand)); } void SubmitWithin10Seconds(const Recorder& recorder) { auto submitted = std::async(std::launch::async, [&]() { return sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0); }); Require(submitted.wait_for(std::chrono::seconds(10)) == std::future_status::ready); Require(submitted.get() == 0); } void TestVersionedCounters() { enum : std::uint8_t { size8, size4, size2Offset0, size2Offset1, size1Offset0, size1Offset1, size1Offset2, size1Offset3 }; enum : std::uint8_t { store, atomicOr, atomicAndComplement, atomicXor, atomicAdd }; Recorder recorder; alignas(8) std::uint64_t fields = 0; alignas(8) std::uint64_t wide = 0; alignas(8) std::uint64_t bits = 0; Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 20, size4, 0x11223344u, store, 0) == 0); Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 20, size1Offset2, 0x1AAu, store, 1) == 0); Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 20, size2Offset0, 0xFFFFu, atomicAdd, 0) == 0); Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 22, size8, 0x0000000500000001ull, store, 0) == 0); Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 22, size8, 0xFFFFFFFFu, atomicAdd, 0) == 0); Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 24, size4, 0xF0u, store, 0) == 0); Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 24, size4, 0x0Fu, atomicOr, 0) == 0); Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 24, size4, 0x3Cu, atomicAndComplement, 0) == 0); Require(sceAmprCommandBufferWriteCounter_04_00(&recorder.buffer, 24, size4, 0xFFu, atomicXor, 0) == 0); Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 20, size1Offset3, 0x11u, 0, 0, 0, 0) == 0); Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 20, size1Offset2, 1u, 6, 0, 0, 1) == 0); Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 20, size2Offset0, 0xF000u, 4, 0, 0, 0) == 0); Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 20, size2Offset1, 0x11ABu, 2, 0, 0, 0) == 0); Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 22, size8, 0x0000000600000000ull, 0, 0, 0, 0) == 0); Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 24, size4, 0xFCu, 0, 1, 0x0Fu, 0) == 0); Require(sceAmprCommandBufferWaitOnCounter_04_00(&recorder.buffer, 24, size1Offset0, 0x3Cu, 0, 0, 0x0Fu, 0) == 0); Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, &fields, 20) == 0); Require(sceAmprCommandBufferWriteAddressFromCounterPairOnCompletion(&recorder.buffer, &wide, 22) == 0); Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&recorder.buffer, &bits, 24) == 0); Require(recorder.Commands() == 19); SubmitWithin10Seconds(recorder); Require(fields == 0x11AA3343u && wide == 0x0000000600000000ull && bits == 0x3Cu); const auto rejected = static_cast(static_cast(invalidArgument)); Recorder empty; Require(sceAmprCommandBufferWaitOnCounter_04_00(&empty.buffer, 0, 8, 0, 0, 0, 0, 0) == invalidArgument); Require(sceAmprCommandBufferWaitOnCounter_04_00(&empty.buffer, 0, size4, 0, 7, 0, 0, 0) == invalidArgument); Require(sceAmprCommandBufferWaitOnCounter_04_00(&empty.buffer, 0, size4, 0, 0, 2, 0, 0) == invalidArgument); Require(sceAmprCommandBufferWaitOnCounter_04_00(&empty.buffer, 0, size4, 0, 0, 0, 0, 2) == invalidArgument); Require(sceAmprCommandBufferWriteCounter_04_00(&empty.buffer, 128, size4, 0, store, 0) == invalidArgument); Require(sceAmprCommandBufferWriteCounter_04_00(&empty.buffer, 0, 8, 0, store, 0) == invalidArgument); Require(sceAmprCommandBufferWriteCounter_04_00(&empty.buffer, 0, size4, 0, 5, 0) == invalidArgument); Require(sceAmprCommandBufferWriteCounter_04_00(nullptr, 0, size4, 0, store, 0) == invalidArgument); Require(empty.Offset() == 0 && empty.Commands() == 0); Require(sceAmprMeasureCommandSizeWaitOnCounter_04_00(200, size1Offset3, 0, 6, 1, 0, 1) == sizeof(Apr::WaitCommand)); Require(sceAmprMeasureCommandSizeWaitOnCounter_04_00(0, 8, 0, 0, 0, 0, 0) == rejected); Require(sceAmprMeasureCommandSizeWaitOnCounter_04_00(0, size4, 0, 7, 0, 0, 0) == rejected); Require(sceAmprMeasureCommandSizeWaitOnCounter_04_00(0, size4, 0, 0, 2, 0, 0) == rejected); Require(sceAmprMeasureCommandSizeWaitOnCounter_04_00(0, size4, 0, 0, 0, 0, 2) == rejected); Require(sceAmprMeasureCommandSizeWriteCounter_04_00(127, size8, 0, atomicAdd) == sizeof(Apr::WriteCounterCommand)); Require(sceAmprMeasureCommandSizeWriteCounter_04_00(128, size4, 0, store) == rejected); Require(sceAmprMeasureCommandSizeWriteCounter_04_00(0, 8, 0, store) == rejected); Require(sceAmprMeasureCommandSizeWriteCounter_04_00(0, size4, 0, 5) == rejected); } void TestClearBuffer() { Recorder recorder; Require(sceAmprCommandBufferNop(&recorder.buffer, 1) == 0); Require(recorder.Offset() != 0 && recorder.Commands() == 1); Require(sceAmprCommandBufferClearBuffer(&recorder.buffer) == recorder.memory.data()); Require(recorder.buffer.base == nullptr && recorder.buffer.size == 0 && recorder.Offset() == 0 && recorder.Commands() == 0); Require(sceAmprCommandBufferClearBuffer(&recorder.buffer) == nullptr); } void TestConstructed() { Recorder recorder; const std::uint8_t payload[5] = {1, 2, 3, 4, 5}; const std::uint32_t word = 0xCAFEF00Du; auto offset = recorder.Offset(); auto commands = recorder.Commands(); Require(sceAmprCommandBufferConstructNop(&recorder.buffer, 7, payload, sizeof(payload), &word) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNopWithData(4)); const std::uint8_t* data = recorder.memory.data() + offset + sizeof(Apr::CommandHeader); const std::uint8_t padding[3] = {}; Require(std::memcmp(data, &word, sizeof(word)) == 0 && std::memcmp(data + 4, payload, sizeof(payload)) == 0 && std::memcmp(data + 9, padding, sizeof(padding)) == 0); const std::array large{}; offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprCommandBufferConstructNop(&recorder.buffer, 0, large.data(), 60, nullptr) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNopWithData(16)); offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprCommandBufferConstructNop(&recorder.buffer, 0, nullptr, 0, nullptr) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNopWithData(1)); const std::pair markers[] = {{1, Apr::Opcode::SetMarker}, {2, Apr::Opcode::PushMarker}, {5, Apr::Opcode::SetMarker}, {6, Apr::Opcode::PushMarker}}; for (const auto& [type, opcode] : markers) { offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprCommandBufferConstructMarker(&recorder.buffer, type, "stream", &color) == 0); RequireRecorded(recorder, offset, commands, opcode, sceAmprMeasureCommandSizeSetMarker("stream"), "stream"); } offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprCommandBufferConstructMarker(&recorder.buffer, 3, nullptr, nullptr) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::PopMarker, sceAmprMeasureCommandSizePopMarker()); Require(sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0) == 0); Recorder empty; Require(sceAmprCommandBufferConstructNop(&empty.buffer, 0, large.data(), 61, nullptr) == invalidArgument); Require(sceAmprCommandBufferConstructNop(&empty.buffer, 0, large.data(), 57, &word) == invalidArgument); Require(sceAmprCommandBufferConstructNop(nullptr, 0, large.data(), 4, nullptr) == invalidArgument); Require(sceAmprCommandBufferConstructMarker(&empty.buffer, 5, "stream", nullptr) == invalidArgument); Require(sceAmprCommandBufferConstructMarker(&empty.buffer, 6, "stream", nullptr) == invalidArgument); Require(sceAmprCommandBufferConstructMarker(&empty.buffer, 1, nullptr, nullptr) == invalidArgument); Require(sceAmprCommandBufferConstructMarker(&empty.buffer, 0, "stream", &color) == invalidArgument); Require(sceAmprCommandBufferConstructMarker(&empty.buffer, 4, "stream", &color) == invalidArgument); Require(empty.Offset() == 0 && empty.Commands() == 0); } std::uint8_t FileByte(std::size_t offset) { return static_cast(offset * 7u + 3u); } bool MatchesFile(const std::uint8_t* data, std::size_t offset, std::size_t bytes) { for (std::size_t index = 0; index < bytes; ++index) { if (data[index] != FileByte(offset + index)) return false; } return true; } void TestZeroFilledBuffer() { const char* path = "ampr_zero_filled.bin"; { std::ofstream file(path, std::ios::binary); for (std::size_t offset = 0; offset < 256; ++offset) file.put(static_cast(FileByte(offset))); } std::uint32_t fileId = 0; std::uint32_t failed = 0; Require(sceKernelAprResolveFilepathsToIds(&path, 1, &fileId, &failed) == 0); KernelEqueue eq = 0; Require(sceKernelCreateEqueue(&eq, "ampr") == 0); Require(sceKernelAddAmprEvent(eq, 0, nullptr) == 0); alignas(8) std::array memory{}; Apr::CommandBufferObject buffer{}; std::uint64_t gatherState = 0; std::uint64_t scatterState = 0; Require(sceAmprCommandBufferSetBuffer(&buffer, memory.data(), static_cast(memory.size())) == 0); Require(sceAmprCommandBufferReset(&buffer) == 0); std::array data{}; std::uint64_t done = 0; Require(sceAmprAprCommandBufferReadFile(&buffer, &gatherState, &scatterState, fileId, data.data(), data.size(), 64) == 0); Require(sceAmprCommandBufferWriteAddressOnCompletion(&buffer, &done, 0x1234567) == 0); Require(sceAmprCommandBufferWriteKernelEventQueue_04_00(&buffer, static_cast(eq), 0, 0, 0) == 0); Require(sceKernelAprSubmitCommandBuffer(&buffer, 1) == 0); Require(MatchesFile(data.data(), 64, data.size()) && done == 0x1234567); KernelEvent event{}; int count = 0; const KernelUseconds poll = 0; Require(sceKernelWaitEqueue(eq, &event, 1, &count, &poll) == 0 && count == 1); Require(sceKernelGetEventFilter(&event) == -25 && sceKernelGetEventId(&event) == 0); Require(sceKernelDeleteAmprEvent(eq, 0) == 0); Require(sceKernelDeleteEqueue(eq) == 0); std::remove(path); } void TestResolveFailures() { const char* paths[]{"ampr_resolve_existing.bin", "ampr_resolve_missing.bin"}; { std::ofstream file(paths[0], std::ios::binary); file << "data"; } std::uint32_t ids[]{123, 123}; std::uint64_t sizes[]{123, 123}; std::uint32_t failed = 99; *__error_nid_postfix() = 0; Require(sceKernelAprResolveFilepathsToIdsAndFileSizes(paths, 2, ids, sizes, &failed) == -1); Require(*__error_nid_postfix() == 2 && failed == 1 && ids[1] == 0xffffffffu && sizes[1] == 0); Require(sizes[0] == 4); int results[]{123, 123}; Require(sceKernelAprResolveFilepathsToIdsAndFileSizesForEach(paths, 2, ids, sizes, results) == 1); Require(results[0] == 0 && results[1] == static_cast(0x80020002)); Require(ids[1] == 0xffffffffu && sizes[0] == 4 && sizes[1] == 0); for (const char* invalid : {"ampr_resolve_missing.bin/../ampr_resolve_existing.bin", "ampr_resolve_existing.bin/../ampr_resolve_existing.bin"}) { Require(sceKernelAprResolveFilepathsToIds(&invalid, 1, ids, &failed) == -1); Require(*__error_nid_postfix() == 2 && failed == 0 && ids[0] == 0xffffffffu); } std::remove(paths[0]); } void TestMalformedCommandSizes() { const auto requireFailure = [](Apr::Opcode opcode, std::uint32_t recordBytes, std::uint32_t bufferBytes, const char* expected) { std::array memory{}; const Apr::CommandHeader header{opcode, recordBytes}; std::memcpy(memory.data(), &header, sizeof(header)); Apr::CommandBufferObject buffer{Apr::BufferType::Generic, 0, bufferBytes, 1, bufferBytes, memory.data()}; bool rejected = false; try { sceKernelAprSubmitCommandBuffer(&buffer, 0); } catch (const std::runtime_error& error) { rejected = std::string(error.what()).starts_with(expected); } Require(rejected); }; requireFailure(Apr::Opcode::ReadFile, sizeof(Apr::CommandHeader), sizeof(Apr::CommandHeader), "APR: truncated command payload"); requireFailure(Apr::Opcode::WriteAddress, sizeof(Apr::CommandHeader), sizeof(Apr::CommandHeader), "APR: truncated command payload"); requireFailure(Apr::Opcode::Nop, std::numeric_limits::max(), 2u * sizeof(Apr::CommandHeader), "APR: malformed command"); std::array memory{}; const Apr::CommandHeader first{Apr::Opcode::Nop, sizeof(Apr::CommandHeader)}; const Apr::CommandHeader overflowing{Apr::Opcode::Nop, std::numeric_limits::max()}; std::memcpy(memory.data(), &first, sizeof(first)); std::memcpy(memory.data() + sizeof(first), &overflowing, sizeof(overflowing)); Apr::CommandBufferObject buffer{Apr::BufferType::Generic, 0, sizeof(memory), 2, sizeof(memory), memory.data()}; bool rejected = false; try { sceKernelAprSubmitCommandBuffer(&buffer, 0); } catch (const std::runtime_error& error) { rejected = std::string(error.what()).starts_with("APR: malformed command"); } Require(rejected); } void TestResolveSymbolicLink() { const std::filesystem::path directory = "ampr_resolve_links"; std::filesystem::remove_all(directory); Require(std::filesystem::create_directory(directory)); { std::ofstream file(directory / "target.bin", std::ios::binary); file << "linked data"; } const auto link = directory / "link.bin"; #ifdef _WIN32 if (CreateSymbolicLinkW(link.c_str(), L"target.bin", SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) == 0) { Require(GetLastError() == ERROR_PRIVILEGE_NOT_HELD); std::fprintf(stderr, "APR symbolic link check skipped: creating symbolic links needs Developer Mode or administrator rights\n"); std::filesystem::remove_all(directory); return; } #else std::filesystem::create_symlink("target.bin", link); #endif const std::string linkPath = link.generic_string(); const char* path = linkPath.c_str(); std::uint32_t id = 0; std::uint64_t size = 0; std::uint32_t failed = 0; Require(sceKernelAprResolveFilepathsToIdsAndFileSizes(&path, 1, &id, &size, &failed) == 0 && size == 11); std::filesystem::remove_all(directory); } void TestGatherScatter() { const char* path = "ampr_gather_scatter.bin"; { std::ofstream file(path, std::ios::binary); for (std::size_t offset = 0; offset < 4096; ++offset) file.put(static_cast(FileByte(offset))); } std::uint32_t fileId = 0; std::uint32_t failed = 0; Require(sceKernelAprResolveFilepathsToIds(&path, 1, &fileId, &failed) == 0); Recorder compact(64); std::array compactData{}; Require(sceAmprAprCommandBufferReadFile(&compact.buffer, &compact.gatherState, &compact.scatterState, fileId, compactData.data(), 8, 100) == 0); Require(sceAmprAprCommandBufferReadFileGather(&compact.buffer, &compact.gatherState, &compact.scatterState, 8, 108) == 0); Require(sceAmprAprCommandBufferReadFileGather(&compact.buffer, &compact.gatherState, &compact.scatterState, 8, 116) == 0); Require(compact.Offset() == 64); Require(sceKernelAprSubmitCommandBuffer(&compact.buffer, 0) == 0); Require(MatchesFile(compactData.data(), 100, compactData.size())); Recorder recorder; auto* buffer = &recorder.buffer; auto* map = &recorder.gatherState; auto* state = &recorder.scatterState; std::array first{}; std::array second{}; Require(sceAmprAprCommandBufferReadFileGather(buffer, map, state, 8, 0) == invalidArgument); Require(sceAmprAprCommandBufferReadFileScatter(buffer, map, state, second.data(), 8) == invalidArgument); Require(sceAmprAprCommandBufferReadFileGatherScatter(buffer, map, state, second.data(), 8, 0) == invalidArgument); Require(recorder.Commands() == 0); Require(sceAmprAprCommandBufferReadFile(buffer, map, state, fileId, first.data(), 16, 100) == 0); auto offset = recorder.Offset(); auto commands = recorder.Commands(); Require(sceAmprAprCommandBufferReadFileGather(buffer, map, state, 8, 500) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::CompactReadFileGather, sceAmprMeasureCommandSizeReadFileGather(8, 500)); offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprAprCommandBufferReadFileScatter(buffer, map, state, second.data(), 8) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::ReadFileScatter, sceAmprMeasureCommandSizeReadFileScatter(second.data(), 8)); offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprAprCommandBufferReadFileGatherScatter(buffer, map, state, second.data() + 32, 4, 1000) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::ReadFileGatherScatter, sceAmprMeasureCommandSizeReadFileGatherScatter(second.data() + 32, 4, 1000)); Require(sceAmprAprCommandBufferReadFileGather(buffer, map, state, 4, 2000) == 0); Require(sceAmprAprCommandBufferReadFileScatter(buffer, map, state, first.data() + 40, 6) == 0); offset = recorder.Offset(); commands = recorder.Commands(); Require(sceAmprAprCommandBufferResetGatherScatterState(buffer) == 0); RequireAppended(recorder, offset, commands, Apr::Opcode::ResetGatherScatterState, sceAmprMeasureCommandSizeResetGatherScatterState()); Require(sceAmprAprCommandBufferReadFileGather(buffer, map, state, 8, 0) == invalidArgument); Require(sceAmprAprCommandBufferReadFileScatter(buffer, map, state, second.data(), 8) == invalidArgument); Require(sceAmprAprCommandBufferReadFile(buffer, map, state, fileId, second.data() + 48, 4, 3000) == 0); Require(sceAmprAprCommandBufferReadFileScatter(buffer, map, state, second.data() + 56, 4) == 0); Require(recorder.Commands() == 9); SubmitWithin10Seconds(recorder); Require(MatchesFile(first.data(), 100, 16) && MatchesFile(first.data() + 16, 500, 8)); Require(MatchesFile(second.data(), 508, 8)); Require(MatchesFile(second.data() + 32, 1000, 4) && MatchesFile(second.data() + 36, 2000, 4)); Require(MatchesFile(first.data() + 40, 2004, 6)); Require(MatchesFile(second.data() + 48, 3000, 4) && MatchesFile(second.data() + 56, 3004, 4)); Require(first[24] == 0 && first[39] == 0 && second[8] == 0 && second[31] == 0 && second[40] == 0); std::remove(path); const auto rejected = static_cast(static_cast(invalidArgument)); auto* high = reinterpret_cast(std::uintptr_t{0xF00000000000ull}); Recorder empty; Require(sceAmprAprCommandBufferReadFile(&empty.buffer, map, state, fileId, first.data(), 0, 0) == invalidArgument); Require(sceAmprAprCommandBufferReadFile(&empty.buffer, map, state, fileId, first.data(), 0x100000001ull, 0) == invalidArgument); Require(sceAmprAprCommandBufferReadFile(&empty.buffer, map, state, fileId, first.data(), 4, 0x10000000000ull) == invalidArgument); Require(sceAmprAprCommandBufferReadFile(&empty.buffer, map, state, fileId, high, 4, 0) == invalidArgument); Require(sceAmprAprCommandBufferReadFile(nullptr, map, state, fileId, first.data(), 4, 0) == invalidArgument); Require(empty.Offset() == 0 && empty.Commands() == 0); Require(sceAmprMeasureCommandSizeReadFileGather(0, 0) == rejected); Require(sceAmprMeasureCommandSizeReadFileGather(0x100000000ull, 0x10000000000ull - 1u) == sizeof(Apr::CompactReadFileGatherCommand)); Require(sceAmprMeasureCommandSizeReadFileGather(4, 0x10000000000ull) == rejected); Require(sceAmprMeasureCommandSizeReadFileScatter(first.data(), 0x100000001ull) == rejected); Require(sceAmprMeasureCommandSizeReadFileScatter(high, 4) == rejected); Require(sceAmprMeasureCommandSizeReadFileGatherScatter(first.data(), 4, 0x10000000000ull) == rejected); Require(sceAmprMeasureCommandSizeReadFileGatherScatter(nullptr, 4, 0) == sizeof(Apr::ReadFileCommand)); } constexpr std::uint64_t page = 0x4000; constexpr int permissionDenied = static_cast(0x80020001); constexpr int busy = static_cast(0x80020010); constexpr int noSuchSubmission = static_cast(0x80020003); constexpr std::int32_t cpuReadWrite = 0x03; constexpr std::int32_t cpuGpuReadWrite = 0x33; constexpr std::int32_t amprReadWrite = 0xC0; volatile std::uint64_t& At(std::uint64_t address) { return *reinterpret_cast(address); } void TestAmmRangesAboveHintedMaps() { constexpr std::uint64_t step = 0x400000; void* span = nullptr; Require(sceKernelReserveVirtualRange(&span, 4 * step, 0, 0) == 0); Require(sceKernelMunmap(span, 4 * step) == 0); const auto heap = reinterpret_cast(span); void* placed = span; Require(sceKernelMapFlexibleMemory(&placed, page, 3, 0) == 0 && placed == span); std::uint64_t start = 0; std::uint64_t end = 0; std::uint64_t multimapStart = 0; std::uint64_t multimapEnd = 0; Require(sceAmprAmmGetVirtualAddressRanges(&start, &end, &multimapStart, &multimapEnd) == 0); std::uintptr_t arenaBase = 0; std::size_t arenaBytes = 0; GuestArena::GuestArenaRange_nid_postfix(&arenaBase, &arenaBytes); Require(start >= arenaBase && multimapEnd <= arenaBase + arenaBytes && heap + 4 * step <= start); for (std::uint64_t hint = heap + step; hint < heap + 4 * step; hint += step) { void* mapped = reinterpret_cast(hint); Require(sceKernelMapFlexibleMemory(&mapped, page, 3, 0) == 0); Require(reinterpret_cast(mapped) == hint); Require(hint + page <= start || hint >= multimapEnd); Require(sceKernelMunmap(mapped, page) == 0); } Require(sceKernelMunmap(span, page) == 0); } void TestAmmUsageStats() { std::array stats{}; Require(sceAmprAmmGetUsageStatsData(nullptr) == static_cast(0x80020016)); stats[0] = 0x19; Require(sceAmprAmmGetUsageStatsData(stats.data()) == static_cast(0x80020016)); stats = {0x18, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef}; Require(sceAmprAmmGetUsageStatsData(stats.data()) == 0); Require(stats[0] == 0x18); Require((stats[1] & 0xffffu) == 512u && (stats[1] >> 16u) == 0u); Require((stats[2] & 0xffffffffu) == 7u && (stats[2] >> 32u) == 0u); stats = {0x8, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef}; Require(sceAmprAmmGetUsageStatsData(stats.data()) == 0); Require(stats[0] == 0x8 && stats[1] == 0xdeadbeef && stats[2] == 0xdeadbeef); stats = {0x0, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef}; Require(sceAmprAmmGetUsageStatsData(stats.data()) == 0); Require(stats[0] == 0 && stats[1] == 0xdeadbeef); } void TestAmm() { std::uint64_t start = 0; std::uint64_t end = 0; std::uint64_t multimapStart = 0; std::uint64_t multimapEnd = 0; Require(sceAmprAmmGetVirtualAddressRanges(&start, &end, &multimapStart, &multimapEnd) == 0); Require(start != 0 && start % page == 0 && start < end && end <= multimapStart && multimapStart < multimapEnd); const auto directMemory = static_cast(sceKernelGetDirectMemorySize()); std::int64_t pool = -1; std::int64_t direct = -1; Require(sceAmprAmmGiveDirectMemory(0, directMemory, 7 * page, page, 1, &pool) == 0 && pool >= 0); Require(sceAmprAmmGiveDirectMemory(0, directMemory, 2 * page, page, 0, &direct) == 0 && direct >= 0); Require(sceAmprAmmGiveDirectMemory(0, directMemory, page, page, 2, &direct) == invalidArgument); Require(sceAmprAmmGiveDirectMemory(0, directMemory, page, page, 1, nullptr) == invalidArgument); const std::uint64_t automatic = start; const std::uint64_t first = start + 0x10000; const std::uint64_t alias = start + 0x20000; const std::uint64_t reused = start + 0x30000; const std::uint64_t region = start + 0x60000; const std::uint64_t directRegion = start + 0x70000; Recorder maps; Require(sceAmprAmmCommandBufferConstructor(&maps.buffer) == 0); auto offset = maps.Offset(); auto commands = maps.Commands(); Require(sceAmprAmmCommandBufferMap(&maps.buffer, automatic, 2 * page, 0, cpuReadWrite) == 0); RequireAppended(maps, offset, commands, Apr::Opcode::AmmMap, sceAmprAmmMeasureAmmCommandSizeMap(automatic, 2 * page, 0, cpuReadWrite)); offset = maps.Offset(); commands = maps.Commands(); Require(sceAmprAmmCommandBufferMapDirect(&maps.buffer, first, direct, 2 * page, 0, cpuGpuReadWrite) == 0); RequireAppended(maps, offset, commands, Apr::Opcode::AmmMapDirect, sceAmprAmmMeasureAmmCommandSizeMapDirect(first, direct, 2 * page, 0, cpuGpuReadWrite)); Require(sceAmprAmmCommandBufferMapDirectWithGpuMaskId(&maps.buffer, alias, direct, 2 * page, 0, amprReadWrite, 3) == 0); std::uint32_t id = 0; Require(sceAmprAmmSubmitCommandBuffer3(maps.memory.data(), maps.Offset(), 0, &id) == 0 && id != 0); Require(sceAmprAmmWaitCommandBufferCompletion(id) == 0); Require(sceAmprAmmWaitCommandBufferCompletion(id + 1000) == noSuchSubmission); At(automatic) = 0x1111; At(automatic + 2 * page - 8) = 0x2222; Require(At(automatic) == 0x1111 && At(automatic + 2 * page - 8) == 0x2222); At(first + page) = 0x3333; Require(At(alias + page) == 0x3333); At(alias) = 0x4444; Require(At(first) == 0x4444); Recorder remaps; Require(sceAmprAmmCommandBufferConstructor(&remaps.buffer) == 0); offset = remaps.Offset(); commands = remaps.Commands(); Require(sceAmprAmmCommandBufferUnmap(&remaps.buffer, automatic, 2 * page) == 0); RequireAppended(remaps, offset, commands, Apr::Opcode::AmmUnmap, sceAmprAmmMeasureAmmCommandSizeUnmap(automatic, 2 * page)); Require(sceAmprAmmCommandBufferMapWithGpuMaskId(&remaps.buffer, reused, 6 * page, 0, cpuReadWrite, 1) == 0); std::uint64_t result = ~0ull; Require(sceAmprAmmSubmitCommandBuffer2(remaps.memory.data(), remaps.Offset(), 0, &result, &id) == 0 && result == 0); Require(sceAmprAmmWaitCommandBufferCompletion(id) == 0); At(reused + 6 * page - 8) = 0x5555; Require(At(reused + 6 * page - 8) == 0x5555); Recorder apr; alignas(8) std::uint64_t done = 0; offset = apr.Offset(); commands = apr.Commands(); Require(sceAmprAprCommandBufferMapBegin(&apr.buffer, region, page, 0, amprReadWrite) == 0); RequireAppended(apr, offset, commands, Apr::Opcode::AmmMap, sceAmprMeasureCommandSizeMapBegin(region, page, 0, amprReadWrite)); Require(sceAmprAprCommandBufferMapBegin(&apr.buffer, region, page, 0, amprReadWrite) == permissionDenied); Require(sceAmprAprCommandBufferMapDirectBegin(&apr.buffer, directRegion, direct, page, 0, amprReadWrite) == permissionDenied); Require(sceAmprCommandBufferWriteAddressOnCompletion(&apr.buffer, &done, 1) == permissionDenied); Require(sceAmprCommandBufferWriteCounterOnCompletion(&apr.buffer, 30, 1) == permissionDenied); Require(sceAmprCommandBufferWriteKernelEventQueueOnCompletion(&apr.buffer, 1, 1, 0) == permissionDenied); Require(sceAmprCommandBufferWriteAddressFromCounter_04_00(&apr.buffer, &done, 30, 0) == permissionDenied); Require(sceAmprCommandBufferWriteCounter_04_00(&apr.buffer, 30, 1, 5, 0, 0) == permissionDenied); Require(sceAmprCommandBufferWriteCounter_04_00(&apr.buffer, 30, 1, 5, 0, 1) == 0); offset = apr.Offset(); commands = apr.Commands(); Require(sceAmprAprCommandBufferMapEnd(&apr.buffer) == 0); RequireAppended(apr, offset, commands, Apr::Opcode::MapEnd, sceAmprMeasureCommandSizeMapEnd()); Require(sceAmprAprCommandBufferMapEnd(&apr.buffer) == permissionDenied); Require(sceAmprAprCommandBufferMapDirectBegin(&apr.buffer, directRegion, direct, page, 0, cpuReadWrite) == 0); Require(sceAmprAprCommandBufferMapEnd(&apr.buffer) == 0); Require(sceAmprCommandBufferWriteAddressFromCounterOnCompletion(&apr.buffer, &done, 30) == 0); SubmitWithin10Seconds(apr); Require(done == 5); At(region + page - 8) = 0x6666; Require(At(region + page - 8) == 0x6666); Require(At(directRegion) == 0x4444); const auto rejected = static_cast(static_cast(invalidArgument)); const std::int64_t ammRejected = invalidArgument; Recorder empty; Require(sceAmprAmmCommandBufferMap(&empty.buffer, start, page, 0, 0x04) == invalidArgument); Require(sceAmprAmmCommandBufferMap(&empty.buffer, start, page, 0, 0x400) == invalidArgument); Require(sceAmprAmmCommandBufferMap(&empty.buffer, start + 0x1000, page, 0, cpuReadWrite) == invalidArgument); Require(sceAmprAmmCommandBufferMap(&empty.buffer, start, page + 0x1000, 0, cpuReadWrite) == invalidArgument); Require(sceAmprAmmCommandBufferMap(&empty.buffer, start, 0, 0, cpuReadWrite) == invalidArgument); Require(sceAmprAmmCommandBufferMap(&empty.buffer, 0, page, 0, cpuReadWrite) == invalidArgument); Require(sceAmprAmmCommandBufferMap(&empty.buffer, ~(page - 1), 2 * page, 0, cpuReadWrite) == invalidArgument); Require(sceAmprAmmCommandBufferMapDirect(&empty.buffer, start, direct + 0x1000, page, 0, cpuReadWrite) == invalidArgument); Require(sceAmprAmmCommandBufferUnmap(&empty.buffer, start + 8, page) == invalidArgument); Require(sceAmprAmmCommandBufferMap(nullptr, start, page, 0, cpuReadWrite) == invalidArgument); Require(sceAmprAprCommandBufferMapBegin(&empty.buffer, start, page, 0, 0x04) == invalidArgument); Require(sceAmprAprCommandBufferMapEnd(nullptr) == invalidArgument); Require(empty.Offset() == 0 && empty.Commands() == 0); Apr::CommandBufferObject unbound{}; Require(sceAmprCommandBufferConstructor(&unbound) == 0); Require(sceAmprAmmCommandBufferMap(&unbound, start, page, 0, cpuReadWrite) == permissionDenied); Require(sceAmprAmmCommandBufferUnmap(&unbound, start, page) == permissionDenied); const auto measured = static_cast(sceAmprAmmMeasureAmmCommandSizeUnmap(start, page)); Recorder exact(measured); Require(sceAmprAmmCommandBufferUnmap(&exact.buffer, start, page) == 0); Require(sceAmprAmmCommandBufferUnmap(&exact.buffer, start, page) == busy); Require(sceAmprAmmCommandBufferMap(&exact.buffer, start, page, 0, cpuReadWrite) == busy); Require(sceAmprAmmSubmitCommandBuffer(nullptr, 0, 0) == permissionDenied); Require(sceAmprAmmSubmitCommandBuffer(exact.memory.data(), 0, 3) == invalidArgument); Require(sceAmprAmmSubmitCommandBuffer3(nullptr, 0, 3, nullptr) == invalidArgument); Require(sceAmprAmmSubmitCommandBuffer(exact.memory.data(), 0, 2) == 0); Require(sceAmprAmmMeasureAmmCommandSizeMap(start, page, 0, 0x04) == ammRejected); Require(sceAmprAmmMeasureAmmCommandSizeMapWithGpuMaskId(start + 8, page, 0, cpuReadWrite, 0) == ammRejected); Require(sceAmprAmmMeasureAmmCommandSizeMapWithGpuMaskId(start, page, 0, cpuReadWrite, 0) == std::int64_t{sizeof(Apr::AmmMapCommand)}); Require(sceAmprAmmMeasureAmmCommandSizeMapDirect(start, direct + 8, page, 0, cpuReadWrite) == ammRejected); Require(sceAmprAmmMeasureAmmCommandSizeMapDirectWithGpuMaskId(start, direct, page, 0, cpuReadWrite, 0) == std::int64_t{sizeof(Apr::AmmMapCommand)}); Require(sceAmprAmmMeasureAmmCommandSizeUnmap(start, 0) == ammRejected); Require(sceAmprMeasureCommandSizeMapBegin(start, page, 0, 0x04) == rejected); Require(sceAmprMeasureCommandSizeMapDirectBegin(start, direct + 8, page, 0, cpuReadWrite) == rejected); Require(sceAmprMeasureCommandSizeMapDirectBegin(start, direct, page, 0, cpuReadWrite) == sizeof(Apr::AmmMapCommand)); } int Protection(std::uint64_t address) { int protection = -1; Require(sceKernelQueryMemoryProtection(reinterpret_cast(address), nullptr, nullptr, &protection) == 0); return protection; } void SubmitAmm(Recorder& recorder) { std::uint32_t id = 0; Require(sceAmprAmmSubmitCommandBuffer3(recorder.memory.data(), recorder.Offset(), 0, &id) == 0); Require(sceAmprAmmWaitCommandBufferCompletion(id) == 0); } void TestAmmRemapAndProtect() { std::uint64_t start = 0; std::uint64_t end = 0; std::uint64_t multimapStart = 0; std::uint64_t multimapEnd = 0; Require(sceAmprAmmGetVirtualAddressRanges(&start, &end, &multimapStart, &multimapEnd) == 0); const auto directMemory = static_cast(sceKernelGetDirectMemorySize()); std::int64_t pool = -1; std::int64_t direct = -1; Require(sceAmprAmmGiveDirectMemory(0, directMemory, 4 * page, page, 1, &pool) == 0); Require(sceAmprAmmGiveDirectMemory(0, directMemory, page, page, 0, &direct) == 0); const std::uint64_t source = start + 0x100000; const std::uint64_t directSource = start + 0x140000; const std::uint64_t moved = start + 0x180000; const std::uint64_t alias = multimapStart + 0x100000; const std::uint64_t directAlias = multimapStart + 0x140000; const std::uint64_t fresh = start + 0x1C0000; const std::uint64_t returned = start + 0x200000; Recorder maps; Require(sceAmprAmmCommandBufferMap(&maps.buffer, source, 2 * page, 0, cpuReadWrite) == 0); Require(sceAmprAmmCommandBufferMapDirect(&maps.buffer, directSource, direct, page, 0, cpuReadWrite) == 0); SubmitAmm(maps); At(source) = 0xA1; At(source + page) = 0xA2; At(directSource) = 0xD1; Recorder moves; auto offset = moves.Offset(); auto commands = moves.Commands(); Require(sceAmprAmmCommandBufferRemap(&moves.buffer, moved, source, 2 * page, cpuReadWrite) == 0); RequireAppended(moves, offset, commands, Apr::Opcode::AmmRemap, sceAmprAmmMeasureAmmCommandSizeRemap(moved, source, 2 * page, cpuReadWrite)); offset = moves.Offset(); commands = moves.Commands(); Require(sceAmprAmmCommandBufferMultiMap(&moves.buffer, alias, moved, 2 * page, cpuReadWrite) == 0); RequireAppended(moves, offset, commands, Apr::Opcode::AmmMultiMap, sceAmprAmmMeasureAmmCommandSizeMultiMap(alias, moved, 2 * page, cpuReadWrite)); Require(sceAmprAmmCommandBufferMultiMapWithGpuMaskId(&moves.buffer, directAlias, directSource, page, amprReadWrite, 2) == 0); SubmitAmm(moves); Require(At(moved) == 0xA1 && At(moved + page) == 0xA2 && At(alias + page) == 0xA2); At(alias) = 0xB1; Require(At(moved) == 0xB1); Require(At(directAlias) == 0xD1); Recorder shared; Require(sceAmprAmmCommandBufferUnmap(&shared.buffer, moved, 2 * page) == 0); Require(sceAmprAmmCommandBufferMap(&shared.buffer, fresh, 2 * page, 0, cpuReadWrite) == 0); SubmitAmm(shared); At(fresh) = 0xC1; At(fresh + page) = 0xC2; Require(At(alias) == 0xB1 && At(alias + page) == 0xA2); Recorder released; Require(sceAmprAmmCommandBufferUnmap(&released.buffer, alias, 2 * page) == 0); Require(sceAmprAmmCommandBufferRemapWithGpuMaskId(&released.buffer, returned, fresh, 2 * page, cpuReadWrite, 1) == 0); Require(sceAmprAmmCommandBufferMap(&released.buffer, fresh, 2 * page, 0, cpuReadWrite) == 0); SubmitAmm(released); Require(At(returned) == 0xC1 && At(returned + page) == 0xC2); At(fresh) = 0xE1; Require(At(returned) == 0xC1); Recorder protects; offset = protects.Offset(); commands = protects.Commands(); Require(sceAmprAmmCommandBufferModifyProtect(&protects.buffer, returned, 2 * page, 0x01, 0x02) == 0); RequireAppended(protects, offset, commands, Apr::Opcode::AmmModifyProtect, sceAmprAmmMeasureAmmCommandSizeModifyProtect(returned, 2 * page, 0x01, 0x02)); offset = protects.Offset(); commands = protects.Commands(); Require(sceAmprAmmCommandBufferModifyMtypeProtect(&protects.buffer, returned, page, 3, 0x22, 0x22) == 0); RequireAppended(protects, offset, commands, Apr::Opcode::AmmModifyMtypeProtect, sceAmprAmmMeasureAmmCommandSizeModifyMtypeProtect(returned, page, 3, 0x22, 0x22)); Require(sceAmprAmmCommandBufferModifyProtectWithGpuMaskId(&protects.buffer, directAlias, page, 0x00, 0x80, 0) == 0); Require(sceAmprAmmCommandBufferModifyMtypeProtectWithGpuMaskId(&protects.buffer, fresh, page, 1, 0x10, 0x13, 0) == 0); SubmitAmm(protects); Require(Protection(returned) == 0x23 && Protection(returned + page) == 0x01); Require(Protection(directAlias) == 0x01); Require(Protection(fresh) == 0x10 && Protection(fresh + page) == 0x03); At(returned) = 0xF1; Require(At(returned) == 0xF1 && At(returned + page) == 0xC2); const std::int64_t ammRejected = invalidArgument; Recorder empty; Require(sceAmprAmmCommandBufferRemap(&empty.buffer, moved, source + 8, page, cpuReadWrite) == invalidArgument); Require(sceAmprAmmCommandBufferRemap(&empty.buffer, moved + 8, source, page, cpuReadWrite) == invalidArgument); Require(sceAmprAmmCommandBufferRemap(&empty.buffer, moved, source, 0, cpuReadWrite) == invalidArgument); Require(sceAmprAmmCommandBufferMultiMap(&empty.buffer, alias, moved, page, 0x04) == invalidArgument); Require(sceAmprAmmCommandBufferModifyProtect(&empty.buffer, returned, page, 0x04, 0x03) == invalidArgument); Require(sceAmprAmmCommandBufferModifyProtect(&empty.buffer, returned, page, 0x03, 0x04) == invalidArgument); Require(sceAmprAmmCommandBufferModifyMtypeProtect(&empty.buffer, returned + 8, page, 0, 0x03, 0x03) == invalidArgument); Require(sceAmprAmmCommandBufferRemap(nullptr, moved, source, page, cpuReadWrite) == invalidArgument); Require(empty.Offset() == 0 && empty.Commands() == 0); Apr::CommandBufferObject unbound{}; Require(sceAmprCommandBufferConstructor(&unbound) == 0); Require(sceAmprAmmCommandBufferMultiMap(&unbound, alias, moved, page, cpuReadWrite) == permissionDenied); Require(sceAmprAmmCommandBufferModifyProtect(&unbound, returned, page, 0x03, 0x03) == permissionDenied); const auto measured = static_cast(sceAmprAmmMeasureAmmCommandSizeRemap(moved, source, page, cpuReadWrite)); Recorder exact(measured); Require(sceAmprAmmCommandBufferRemap(&exact.buffer, moved, source, page, cpuReadWrite) == 0); Require(sceAmprAmmCommandBufferModifyProtect(&exact.buffer, returned, page, 0x03, 0x03) == busy); Require(sceAmprAmmMeasureAmmCommandSizeRemapWithGpuMaskId(moved, source + 8, page, cpuReadWrite, 0) == ammRejected); Require(sceAmprAmmMeasureAmmCommandSizeRemapWithGpuMaskId(moved, source, page, cpuReadWrite, 0) == std::int64_t{sizeof(Apr::AmmRemapCommand)}); Require(sceAmprAmmMeasureAmmCommandSizeMultiMapWithGpuMaskId(alias, moved, page, 0x400, 0) == ammRejected); Require(sceAmprAmmMeasureAmmCommandSizeMultiMapWithGpuMaskId(alias, moved, page, cpuReadWrite, 0) == std::int64_t{sizeof(Apr::AmmRemapCommand)}); Require(sceAmprAmmMeasureAmmCommandSizeModifyProtectWithGpuMaskId(returned, page, 0x03, 0x04, 0) == ammRejected); Require(sceAmprAmmMeasureAmmCommandSizeModifyProtectWithGpuMaskId(returned, page, 0x03, 0x03, 0) == std::int64_t{sizeof(Apr::AmmProtectCommand)}); Require(sceAmprAmmMeasureAmmCommandSizeModifyMtypeProtectWithGpuMaskId(returned, 0, 0, 0x03, 0x03, 0) == ammRejected); Require(sceAmprAmmMeasureAmmCommandSizeModifyMtypeProtectWithGpuMaskId(returned, page, 0, 0x03, 0x03, 0) == std::int64_t{sizeof(Apr::AmmProtectCommand)}); Require(sceAmprAmmMeasureAmmCommandSizeMultiMap(alias, moved, 0, cpuReadWrite) == ammRejected); Require(sceAmprAmmMeasureAmmCommandSizeModifyMtypeProtect(returned, page, 0, 0x08, 0x03) == ammRejected); } void TestAmmPrt() { std::uint64_t start = 0; std::uint64_t end = 0; std::uint64_t multimapStart = 0; std::uint64_t multimapEnd = 0; Require(sceAmprAmmGetVirtualAddressRanges(&start, &end, &multimapStart, &multimapEnd) == 0); std::int64_t pool = -1; Require(sceAmprAmmGiveDirectMemory(0, static_cast(sceKernelGetDirectMemorySize()), 4 * page, page, 1, &pool) == 0); const std::uint64_t prt = start + 0x300000; const std::uint64_t source = start + 0x380000; Recorder reserve; auto offset = reserve.Offset(); auto commands = reserve.Commands(); Require(sceAmprAmmCommandBufferMapAsPrt(&reserve.buffer, prt, 4 * page) == 0); RequireAppended(reserve, offset, commands, Apr::Opcode::AmmMapAsPrt, sceAmprAmmMeasureAmmCommandSizeMapAsPrt(prt, 4 * page)); Require(sceAmprAmmCommandBufferMap(&reserve.buffer, source, page, 0, cpuReadWrite) == 0); SubmitAmm(reserve); Require(Protection(prt) == 0x10 && Protection(prt + 3 * page) == 0x10); Require(At(prt) == 0 && At(prt + 4 * page - 8) == 0); At(source) = 0x88; Recorder back; offset = back.Offset(); commands = back.Commands(); Require(sceAmprAmmCommandBufferAllocatePaForPrt(&back.buffer, prt + page, 2 * page, 0, cpuReadWrite) == 0); RequireAppended(back, offset, commands, Apr::Opcode::AmmAllocatePaForPrt, sceAmprAmmMeasureAmmCommandSizeAllocatePaForPrt(prt + page, 2 * page, 0, cpuReadWrite)); Require(sceAmprAmmCommandBufferAllocatePaForPrt(&back.buffer, prt + 2 * page, page, 2, cpuGpuReadWrite) == 0); Require(sceAmprAmmCommandBufferRemapIntoPrt(&back.buffer, prt + 3 * page, source, page, cpuReadWrite, 0) == 0); SubmitAmm(back); Require(Protection(prt) == 0x10 && Protection(prt + page) == 0x03 && Protection(prt + 2 * page) == 0x33 && Protection(prt + 3 * page) == 0x03); At(prt + page) = 0x71; At(prt + 2 * page) = 0x72; Require(At(prt + page) == 0x71 && At(prt + 2 * page) == 0x72 && At(prt + 3 * page) == 0x88 && At(prt) == 0); Recorder release; offset = release.Offset(); commands = release.Commands(); Require(sceAmprAmmCommandBufferUnmapToPrt(&release.buffer, prt + page, 2 * page) == 0); RequireAppended(release, offset, commands, Apr::Opcode::AmmUnmapToPrt, sceAmprAmmMeasureAmmCommandSizeUnmap(prt + page, 2 * page)); SubmitAmm(release); Require(Protection(prt + page) == 0x10 && At(prt + page) == 0 && At(prt + 2 * page) == 0 && At(prt + 3 * page) == 0x88); Recorder rebuild; Require(sceAmprAmmCommandBufferAllocatePaForPrt(&rebuild.buffer, prt, 3 * page, 0, cpuReadWrite) == 0); SubmitAmm(rebuild); for (std::uint64_t index = 0; index < 3; ++index) { Require(Protection(prt + index * page) == 0x03); At(prt + index * page) = 0x90 + index; } Require(At(prt) == 0x90 && At(prt + page) == 0x91 && At(prt + 2 * page) == 0x92 && At(prt + 3 * page) == 0x88); const std::int64_t ammRejected = invalidArgument; Recorder empty; Require(sceAmprAmmCommandBufferMapAsPrt(&empty.buffer, prt + 8, page) == invalidArgument); Require(sceAmprAmmCommandBufferMapAsPrt(nullptr, prt, page) == invalidArgument); Require(sceAmprAmmCommandBufferAllocatePaForPrt(&empty.buffer, prt, page, 0, 0x04) == invalidArgument); Require(sceAmprAmmCommandBufferAllocatePaForPrt(nullptr, prt, page, 0, 0x404) == invalidArgument); Require(sceAmprAmmCommandBufferAllocatePaForPrt(&empty.buffer, prt, 0, 0, cpuReadWrite) == invalidArgument); Require(sceAmprAmmCommandBufferRemapIntoPrt(&empty.buffer, prt, source + 8, page, cpuReadWrite, 0) == invalidArgument); Require(sceAmprAmmCommandBufferRemapIntoPrt(&empty.buffer, prt, source, page, 0x04, 0) == invalidArgument); Require(sceAmprAmmCommandBufferUnmapToPrt(&empty.buffer, prt, page + 8) == invalidArgument); Require(empty.Offset() == 0 && empty.Commands() == 0); Apr::CommandBufferObject unbound{}; Require(sceAmprCommandBufferConstructor(&unbound) == 0); Require(sceAmprAmmCommandBufferMapAsPrt(&unbound, prt, page) == busy); Require(sceAmprAmmCommandBufferAllocatePaForPrt(&unbound, prt, page, 0, cpuReadWrite) == busy); Require(sceAmprAmmCommandBufferRemapIntoPrt(&unbound, prt, source, page, cpuReadWrite, 0) == permissionDenied); Require(sceAmprAmmCommandBufferUnmapToPrt(&unbound, prt, page) == permissionDenied); std::array loose{}; Apr::CommandBufferObject sized{Apr::BufferType::Generic, 0, 0, 0, static_cast(loose.size()), nullptr}; Require(sceAmprAmmCommandBufferMapAsPrt(&sized, prt, page) == permissionDenied); Require(sceAmprAmmMeasureAmmCommandSizeMapAsPrt(prt, 0) == ammRejected); Require(sceAmprAmmMeasureAmmCommandSizeAllocatePaForPrt(prt, page, 0, 0x08) == ammRejected); Require(sceAmprAmmMeasureAmmCommandSizeAllocatePaForPrt(prt + 8, page, 0, cpuReadWrite) == ammRejected); } void TestKernelWriteCommands() { std::uint64_t start = 0; std::uint64_t end = 0; std::uint64_t multimapStart = 0; std::uint64_t multimapEnd = 0; Require(sceAmprAmmGetVirtualAddressRanges(&start, &end, &multimapStart, &multimapEnd) == 0); std::int64_t pool = -1; Require(sceAmprAmmGiveDirectMemory(0, static_cast(sceKernelGetDirectMemorySize()), 4 * page, page, 1, &pool) == 0); const std::uint64_t target = start + 0x400000; const std::uint64_t source = start + 0x440000; const std::uint64_t prt = start + 0x480000; Recorder setup; Require(sceAmprAmmCommandBufferMap(&setup.buffer, target, 2 * page, 0, cpuReadWrite) == 0); Require(sceAmprAmmCommandBufferMap(&setup.buffer, source, page, 0, cpuReadWrite) == 0); Require(sceAmprAmmCommandBufferMapAsPrt(&setup.buffer, prt, 2 * page) == 0); SubmitAmm(setup); At(source) = 0x9C; std::uint64_t measured = 0; Require(sceKernelWriteModifyMtypeProtectCommand(nullptr, target, 2 * page, 5, 0x13, 0x10, &measured) == 0); Require(measured == sizeof(Apr::AmmProtectCommand)); std::array written{}; std::uint64_t reported = 0; Require(sceKernelWriteModifyMtypeProtectCommand(written.data(), target, 2 * page, 5, 0x13, 0x10, &reported) == 0); Require(reported == sizeof(Apr::AmmProtectCommand)); Apr::CommandHeader header; std::memcpy(&header, written.data(), sizeof(header)); Require(header.opcode == Apr::Opcode::AmmModifyMtypeProtect && header.bytes == sizeof(Apr::AmmProtectCommand)); Recorder sibling; Require(sceAmprAmmCommandBufferModifyMtypeProtect(&sibling.buffer, target, 2 * page, 5, 0x13, 0x10) == 0); Require(std::memcmp(written.data(), sibling.memory.data(), sizeof(Apr::AmmProtectCommand)) == 0); Require(sceKernelWriteModifyMtypeProtectCommand(written.data(), target, 2 * page, 5, 0x13, 0x10, nullptr) == 0); std::uint32_t id = 0; Require(sceAmprAmmSubmitCommandBuffer3(written.data(), static_cast(reported), 0, &id) == 0); Require(sceAmprAmmWaitCommandBufferCompletion(id) == 0); Require(Protection(target) == 0x13 && Protection(target + page) == 0x13); At(target) = 0x9D; Require(At(target) == 0x9D); std::uint64_t prtMeasured = 0; Require(sceKernelWriteRemapIntoPrtCommand(nullptr, prt + page, source, page, cpuReadWrite, 0, &prtMeasured) == 0); Require(prtMeasured == sizeof(Apr::AmmRemapCommand)); std::array prtWritten{}; Require(sceKernelWriteRemapIntoPrtCommand(reinterpret_cast(prtWritten.data()), prt + page, source, page, cpuReadWrite, 0, &prtMeasured) == 0); Apr::AmmRemapCommand remap; std::memcpy(&remap, prtWritten.data(), sizeof(remap)); Require(remap.header.opcode == Apr::Opcode::AmmRemapIntoPrt && remap.header.bytes == sizeof(remap)); Require(remap.address == prt + page && remap.source == source && remap.size == page && remap.protection == cpuReadWrite && remap.reserved == 1011); Require(sceKernelWriteRemapIntoPrtCommand(reinterpret_cast(prtWritten.data()), prt + page, source, page, cpuReadWrite, 42, &prtMeasured) == 0); std::memcpy(&remap, prtWritten.data(), sizeof(remap)); Require(remap.reserved == 42); Recorder prtSibling; Require(sceAmprAmmCommandBufferRemapIntoPrt(&prtSibling.buffer, prt + page, source, page, cpuReadWrite, 0) == 0); std::array prtSubmit{}; Require(sceKernelWriteRemapIntoPrtCommand(reinterpret_cast(prtSubmit.data()), prt + page, source, page, cpuReadWrite, 0, nullptr) == 0); Require(std::memcmp(prtSubmit.data(), prtSibling.memory.data(), sizeof(Apr::AmmRemapCommand)) == 0); Require(sceAmprAmmSubmitCommandBuffer3(prtSubmit.data(), static_cast(sizeof(Apr::AmmRemapCommand)), 0, &id) == 0); Require(sceAmprAmmWaitCommandBufferCompletion(id) == 0); Require(At(prt + page) == 0x9C); std::array untouched{}; untouched.fill(0xCC); std::uint64_t sentinel = 0xDDDDDDDDDDDDDDDDULL; Require(sceKernelWriteModifyMtypeProtectCommand(untouched.data(), target + 8, page, 0, 0x03, 0x03, &sentinel) == invalidArgument); Require(sceKernelWriteModifyMtypeProtectCommand(untouched.data(), target, 0, 0, 0x03, 0x03, &sentinel) == invalidArgument); Require(sceKernelWriteModifyMtypeProtectCommand(untouched.data(), 0, page, 0, 0x03, 0x03, &sentinel) == invalidArgument); Require(sceKernelWriteModifyMtypeProtectCommand(untouched.data(), target, page, 0, 0x04, 0x03, &sentinel) == invalidArgument); Require(sceKernelWriteModifyMtypeProtectCommand(untouched.data(), target, page, 0, 0x03, 0x04, &sentinel) == invalidArgument); Require(sceKernelWriteModifyMtypeProtectCommand(untouched.data(), ~std::uint64_t(0), page, 0, 0x03, 0x03, &sentinel) == invalidArgument); Require(sceKernelWriteModifyMtypeProtectCommand(untouched.data(), target, page, 0, 0x100000000ULL, 0x03, &sentinel) == invalidArgument); Require(sceKernelWriteRemapIntoPrtCommand(reinterpret_cast(untouched.data()), prt, source + 8, page, cpuReadWrite, 0, &sentinel) == invalidArgument); Require(sceKernelWriteRemapIntoPrtCommand(reinterpret_cast(untouched.data()), prt, source, 0, cpuReadWrite, 0, &sentinel) == invalidArgument); Require(sceKernelWriteRemapIntoPrtCommand(reinterpret_cast(untouched.data()), prt, source, page, 0x04, 0, &sentinel) == invalidArgument); Require(sentinel == 0xDDDDDDDDDDDDDDDDULL); for (const auto byte : untouched) Require(byte == 0xCC); Require(sceKernelWriteModifyMtypeProtectCommand(nullptr, target, page, 0, 0x03, 0x03, nullptr) == 0); Require(sceKernelWriteRemapIntoPrtCommand(nullptr, prt, source, page, cpuReadWrite, 0, nullptr) == 0); } } int main() { TestRecording("frame"); TestRecording(""); TestRecording("1234567"); TestRecording("12345678"); TestRecording(std::string(300, 'a')); TestRejectedArguments(); TestFullBuffer(); TestOffsetPastBuffer(); TestSubmission(); TestKernelEventQueue(); TestWaits(); TestWaitSatisfiedAfterSubmission(); TestAmmNotBehindAprWait(); TestCounters(); TestRejectedWaitsAndCounters(); TestNops(); TestVersionedCommands(); TestVersionedCounters(); TestClearBuffer(); TestConstructed(); TestZeroFilledBuffer(); TestResolveFailures(); TestMalformedCommandSizes(); TestResolveSymbolicLink(); TestGatherScatter(); TestAmmRangesAboveHintedMaps(); TestAmmUsageStats(); TestAmm(); TestAmmRemapAndProtect(); TestAmmPrt(); TestKernelWriteCommands(); return 0; }