Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -887,20 +887,28 @@ std::string hexText(std::uint64_t value) {

std::vector<std::uint32_t> ReadUserDataCallee(std::uint64_t address, std::uint32_t linkRegister) {
constexpr std::uint64_t page = 4096u;
constexpr std::size_t limitWords = 16384u;
constexpr std::size_t limitWords = 1024u * 1024u / 4u;
if (address % 4u != 0u) throw std::runtime_error("its callee address " + hexText(address) + " is not dword aligned");
std::vector<std::uint32_t> words;
const auto finish = [&] {
const auto length = ShaderRecompiler::UserDataCalleeLength(words, linkRegister);
if (length) words.resize(*length);
return length.has_value();
};
std::size_t nextCheck = 0;
while (words.size() < limitWords) {
const auto next = address + words.size() * 4u;
const auto bytes = static_cast<std::size_t>(page - next % page);
if (!GuestMemory::Accessible(reinterpret_cast<const void*>(static_cast<std::uintptr_t>(next)), bytes)) throw std::runtime_error("its callee at " + hexText(address) + " is not readable at " + hexText(next));
if (!GuestMemory::Accessible(reinterpret_cast<const void*>(static_cast<std::uintptr_t>(next)), bytes)) {
if (finish()) return words;
throw std::runtime_error("its callee at " + hexText(address) + " is not readable at " + hexText(next));
}
const auto old = words.size();
words.resize(old + bytes / 4u);
GuestMemory::Read(next, std::as_writable_bytes(std::span(words).subspan(old)));
if (const auto length = ShaderRecompiler::UserDataCalleeLength(words, linkRegister)) {
words.resize(*length);
return words;
}
if (words.size() < nextCheck && words.size() < limitWords) continue;
nextCheck = words.size() * 2u;
if (finish()) return words;
}
throw std::runtime_error("its callee at " + hexText(address) + " does not return within " + std::to_string(limitWords * 4u) + " bytes");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
#include "SceShaders.hpp"
#include "VulkanTestDevice.hpp"
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <span>
#include <stdexcept>
#include <string>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#endif

namespace {

Expand Down Expand Up @@ -92,6 +98,25 @@ void Register(bool wave32) {
AgcDriverRegisterShader_nid_postfix(&header.shader);
}

const void* LongCallee() {
constexpr std::size_t page = 4096u;
constexpr std::size_t words = 20000u;
constexpr std::size_t committed = (words * 4u + page - 1u) / page * page;
#ifdef _WIN32
auto* block = static_cast<std::uint32_t*>(VirtualAlloc(nullptr, committed + page, MEM_RESERVE, PAGE_NOACCESS));
Require(block != nullptr && VirtualAlloc(block, committed, MEM_COMMIT, PAGE_READWRITE) != nullptr, "cannot reserve the long callee");
#else
void* mapped = mmap(nullptr, committed + page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
Require(mapped != MAP_FAILED && mprotect(mapped, committed, PROT_READ | PROT_WRITE) == 0, "cannot reserve the long callee");
auto* block = static_cast<std::uint32_t*>(mapped);
#endif
block[0] = 0x4a020281u;
for (std::size_t word = 1; word + 1u < words; ++word) block[word] = 0xbf800000u;
block[words - 1u] = 0xbe80200eu;
for (std::size_t word = words; word < committed / 4u; ++word) block[word] = 0xbf810000u;
return block;
}

template <typename TAction>
void ExpectFailure(TAction action, const char* expected) {
try {
Expand All @@ -114,10 +139,12 @@ int main(int argc, char** argv) {
if (!device) return VulkanTestSkipped;
}
if (registered) Register(true);
const auto* longCallee = LongCallee();
for (const bool wave32 : registered ? std::vector<bool>{true} : std::vector<bool>{false, true}) {
Check(AddFive.data(), AddSixtyFour.data(), wave32, 3u * (5u + 64u), "two user-data callees called from a loop");
Check(AddOne.data(), AddSixtyFour.data(), wave32, 3u * (1u + 64u), "the first callee replaced");
Check(AddSixtyFour.data(), AddSixtyFour.data(), wave32, 3u * (64u + 64u), "one callee behind both pointers");
Check(longCallee, AddSixtyFour.data(), wave32, 3u * (1u + 64u), "a callee of 20,000 dwords whose last page is followed by unreadable memory");
}
const auto* unmapped = reinterpret_cast<const void*>(std::uintptr_t{0x1000u});
ExpectFailure([&] { Dispatch(AddFive.data(), unmapped, registered); }, "is not readable");
Expand Down
Loading