mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 107d5f750a | |||
| f40755173f | |||
| 4e14eed770 | |||
| aa3b2cd57f |
@@ -198,12 +198,6 @@ KernelSemaPrivate::Result KernelSemaPrivate::Wait(int need_count, uint32_t* ptr_
|
||||
infinitely = false;
|
||||
}
|
||||
|
||||
uint32_t elapsed = 0;
|
||||
Common::Timer t;
|
||||
t.Start();
|
||||
|
||||
int id = Common::Thread::GetThreadIdUnique();
|
||||
|
||||
if (m_count >= need_count) {
|
||||
m_count -= need_count;
|
||||
if (ptr_micros != nullptr) {
|
||||
@@ -216,6 +210,12 @@ KernelSemaPrivate::Result KernelSemaPrivate::Wait(int need_count, uint32_t* ptr_
|
||||
return Result::TimedOut;
|
||||
}
|
||||
|
||||
uint32_t elapsed = 0;
|
||||
Common::Timer t;
|
||||
t.Start();
|
||||
|
||||
int id = Common::Thread::GetThreadIdUnique();
|
||||
|
||||
WaitingThread waiter {};
|
||||
waiter.id = id;
|
||||
waiter.priority = (m_fifo_order ? 0 : Libs::LibKernel::PthreadGetCurrentPriorityForKernel());
|
||||
|
||||
@@ -3217,6 +3217,14 @@ uint32_t KYTY_SYSV_ABI AgcAcbJumpGetSize() {
|
||||
return 0x10u;
|
||||
}
|
||||
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbRewind(CommandBuffer* buf, uint8_t initial_state, uint8_t offload) {
|
||||
auto* cmd = AgcDcbRewind(buf, initial_state);
|
||||
if (cmd != nullptr) {
|
||||
cmd[1] |= (static_cast<uint32_t>(offload & 0x1u) << 24u);
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8_t compare_function,
|
||||
uint8_t cache_policy, const volatile void* address,
|
||||
uint64_t reference, uint64_t mask, uint32_t poll_cycles) {
|
||||
|
||||
@@ -206,6 +206,7 @@ uint32_t KYTY_SYSV_ABI AgcAcbCondExecGetSize();
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbJump(CommandBuffer* buf, uint8_t cache_policy,
|
||||
const uint32_t* target, uint32_t size_in_dwords);
|
||||
uint32_t KYTY_SYSV_ABI AgcAcbJumpGetSize();
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbRewind(CommandBuffer* buf, uint8_t initial_state, uint8_t offload);
|
||||
uint32_t* KYTY_SYSV_ABI AgcAcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8_t compare_function,
|
||||
uint8_t cache_policy, const volatile void* address,
|
||||
uint64_t reference, uint64_t mask, uint32_t poll_cycles);
|
||||
|
||||
@@ -74,6 +74,7 @@ LIB_DEFINE(InitAgcDriver_1) {
|
||||
LIB_FUNC("ozKzBP4aki4", Gen5::AgcAcbCondExecGetSize);
|
||||
LIB_FUNC("e1DFTg+Sd8U", Gen5::AgcAcbJump);
|
||||
LIB_FUNC("b-oySn+G2tE", Gen5::AgcAcbJumpGetSize);
|
||||
LIB_FUNC("DwICrVxerkY", Gen5::AgcAcbRewind);
|
||||
LIB_FUNC("htn36gPnBk4", Gen5::AgcAcbWaitRegMem);
|
||||
LIB_FUNC("idlaArvdXEs", Gen5::AgcAcbWaitOnAddressGetSize);
|
||||
LIB_FUNC("-RnpfpxIhec", Gen5::AgcAcbDmaData);
|
||||
@@ -156,6 +157,7 @@ LIB_DEFINE(InitAgcDriver_1) {
|
||||
LIB_FUNC("k-JpyR2dYAM", Gen5::AgcCondExecPatchSetEnd);
|
||||
LIB_FUNC("3ZWa3AoyWZQ", Gen5::AgcCondExecPatchSetCommandAddress);
|
||||
LIB_FUNC("ziVA3whp3p4", Gen5::AgcRewindPatchSetRewindState);
|
||||
LIB_FUNC("eWaWyFegzgQ", Gen5::AgcRewindPatchSetRewindState);
|
||||
LIB_FUNC("YUeqkyT7mEQ", Gen5::AgcDcbSetFlip);
|
||||
}
|
||||
|
||||
|
||||
+14
-13
@@ -19,6 +19,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
@@ -48,7 +49,7 @@ struct ResolvedPathInfo {
|
||||
uint32_t file_id = 0xffffffffu;
|
||||
uint64_t file_size = 0;
|
||||
bool is_dir = false;
|
||||
std::string host_path;
|
||||
std::filesystem::path host_path;
|
||||
};
|
||||
|
||||
static std::mutex g_mutex;
|
||||
@@ -56,7 +57,7 @@ static uint32_t g_next_submission_id =
|
||||
static uint32_t g_next_file_id = 1;
|
||||
static std::unordered_map<uint32_t, bool> g_submissions;
|
||||
static std::condition_variable g_submission_cv;
|
||||
static std::unordered_map<uint32_t, std::string> g_files;
|
||||
static std::unordered_map<uint32_t, std::filesystem::path> g_files;
|
||||
static std::unordered_map<uint32_t, uint64_t> g_file_sizes;
|
||||
static std::unordered_map<std::string, ResolvedPathInfo> g_resolved_paths;
|
||||
|
||||
@@ -113,7 +114,7 @@ static int ReadGuestCString(uint64_t addr, char* out, size_t out_size) {
|
||||
return LibKernel::KERNEL_ERROR_ENAMETOOLONG;
|
||||
}
|
||||
|
||||
static bool TryGetHostPath(uint32_t file_id, std::string* out) {
|
||||
static bool TryGetHostPath(uint32_t file_id, std::filesystem::path* out) {
|
||||
std::scoped_lock lock(g_mutex);
|
||||
const auto it = g_files.find(file_id);
|
||||
if (it == g_files.end()) {
|
||||
@@ -133,7 +134,7 @@ static bool TryGetHostFileSize(uint32_t file_id, uint64_t* out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int GetHostPathStat(const std::string& host_path, LibKernel::FileSystem::FileStat* st) {
|
||||
static int GetHostPathStat(const std::filesystem::path& host_path, LibKernel::FileSystem::FileStat* st) {
|
||||
if (st == nullptr) {
|
||||
return LibKernel::KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
@@ -194,7 +195,7 @@ static int ResolveOnePath(const char* guest_path, uint32_t* id, uint64_t* size)
|
||||
|
||||
if (!found) {
|
||||
const auto real_path = LibKernel::FileSystem::GetRealFilename(path);
|
||||
info.host_path = Common::PathToString(real_path);
|
||||
info.host_path = real_path;
|
||||
|
||||
if (Common::File::IsDirectoryExisting(real_path)) {
|
||||
info.is_dir = true;
|
||||
@@ -225,7 +226,7 @@ static int ResolveOnePath(const char* guest_path, uint32_t* id, uint64_t* size)
|
||||
}
|
||||
}
|
||||
if (log_missing) {
|
||||
LOGF("\tAPR resolve missing path: %s -> %s\n", guest_path, info.host_path.c_str());
|
||||
LOGF("\tAPR resolve missing path: %s -> %s\n", guest_path, Common::PathToString(info.host_path).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +409,7 @@ static int KYTY_SYSV_ABI GetFileStat(uint32_t file_id, LibKernel::FileSystem::Fi
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EINVAL);
|
||||
}
|
||||
|
||||
std::string host_path;
|
||||
std::filesystem::path host_path;
|
||||
if (!AprShared::TryGetHostPath(file_id, &host_path)) {
|
||||
LOGF("\tAPR stat failed for unknown file id: 0x%08" PRIx32 "\n", file_id);
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_ENOENT);
|
||||
@@ -430,7 +431,7 @@ static int KYTY_SYSV_ABI GetFileSize(uint32_t file_id, uint64_t* size) {
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EFAULT);
|
||||
}
|
||||
|
||||
std::string host_path;
|
||||
std::filesystem::path host_path;
|
||||
if (!AprShared::TryGetHostPath(file_id, &host_path)) {
|
||||
LOGF("\tAPR size failed for unknown file id: 0x%08" PRIx32 "\n", file_id);
|
||||
return KernelSyscallResult(LibKernel::KERNEL_ERROR_ENOENT);
|
||||
@@ -1161,13 +1162,13 @@ static bool AppendCommandRecord(uint64_t command_buffer, CommandBufferState::Com
|
||||
return CommitCommandBufferRecord(command_buffer, &state, record_size);
|
||||
}
|
||||
|
||||
static int ReadHostFileToGuest(const std::string& host_path, uint64_t file_offset,
|
||||
static int ReadHostFileToGuest(const std::filesystem::path& host_path, uint64_t file_offset,
|
||||
uint64_t destination, uint64_t size, uint64_t* bytes_read);
|
||||
|
||||
static int ExecuteCommand(const CommandBufferState::Command& entry) {
|
||||
if (const auto* payload = std::get_if<CommandBufferState::ReadFileCommand>(&entry.data)) {
|
||||
const auto& command = *payload;
|
||||
std::string host_path;
|
||||
std::filesystem::path host_path;
|
||||
if (!AprShared::TryGetHostPath(command.file_id, &host_path)) {
|
||||
LOGF("\tAPR submit failed for unknown file id: 0x%08" PRIx32 "\n", command.file_id);
|
||||
return LibKernel::KERNEL_ERROR_ENOENT;
|
||||
@@ -1178,7 +1179,7 @@ static int ExecuteCommand(const CommandBufferState::Command& entry) {
|
||||
command.size, &bytes_read);
|
||||
if (result != OK) {
|
||||
LOGF("\tAPR submit read failed: id=0x%08" PRIx32 ", result=0x%08" PRIx32 ", path=%s\n",
|
||||
command.file_id, static_cast<uint32_t>(result), host_path.c_str());
|
||||
command.file_id, static_cast<uint32_t>(result), Common::PathToString(host_path).c_str());
|
||||
return result;
|
||||
}
|
||||
} else if (const auto* payload =
|
||||
@@ -1380,7 +1381,7 @@ static bool AdvanceCommandBuffer(uint64_t command_buffer, uint64_t record_size)
|
||||
return CommitCommandBufferRecord(command_buffer, &state, record_size);
|
||||
}
|
||||
|
||||
static int ReadHostFileToGuest(const std::string& host_path, uint64_t file_offset,
|
||||
static int ReadHostFileToGuest(const std::filesystem::path& host_path, uint64_t file_offset,
|
||||
uint64_t destination, uint64_t size, uint64_t* bytes_read) {
|
||||
if (bytes_read == nullptr) {
|
||||
return LibKernel::KERNEL_ERROR_EINVAL;
|
||||
@@ -1396,7 +1397,7 @@ static int ReadHostFileToGuest(const std::string& host_path, uint64_t file_offse
|
||||
|
||||
Common::File file;
|
||||
if (!file.Open(host_path, Common::File::Mode::Read)) {
|
||||
LOGF("\tAPR read missing host file: %s\n", host_path.c_str());
|
||||
LOGF("\tAPR read missing host file: %s\n", Common::PathToString(host_path).c_str());
|
||||
return LibKernel::KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
const auto file_size = file.Size();
|
||||
|
||||
+73
-3
@@ -1497,13 +1497,43 @@ namespace LibNpCommerce {
|
||||
|
||||
LIB_VERSION("NpCommerce", 1, "NpCommerce", 1, 1);
|
||||
|
||||
constexpr int COMMERCE_STATUS_NONE = 0;
|
||||
constexpr int COMMERCE_STATUS_INITIALIZED = 1;
|
||||
constexpr int COMMERCE_STATUS_NONE = 0;
|
||||
constexpr int COMMERCE_STATUS_INITIALIZED = 1;
|
||||
constexpr int COMMERCE_STATUS_FINISHED = 3;
|
||||
constexpr int COMMERCE_RESULT_USER_CANCELED = 1;
|
||||
|
||||
constexpr int COMMERCE_ERROR_NOT_INITIALIZED = static_cast<int>(0x80B80003u);
|
||||
constexpr int COMMERCE_ERROR_ALREADY_INITIALIZED = static_cast<int>(0x80B80004u);
|
||||
constexpr int COMMERCE_ERROR_NOT_FINISHED = static_cast<int>(0x80B80005u);
|
||||
constexpr int COMMERCE_ERROR_PARAM_INVALID = static_cast<int>(0x80B8000Au);
|
||||
constexpr int COMMERCE_ERROR_ARG_NULL = static_cast<int>(0x80B8000Du);
|
||||
|
||||
static int g_commerce_status = COMMERCE_STATUS_NONE;
|
||||
struct CommerceDialogParam {
|
||||
uint8_t base_param[48];
|
||||
int32_t size;
|
||||
int32_t user_id;
|
||||
int32_t mode;
|
||||
uint32_t service_label;
|
||||
const char* const* targets;
|
||||
uint32_t num_targets;
|
||||
uint32_t padding;
|
||||
uint64_t features;
|
||||
void* user_data;
|
||||
uint8_t reserved[32];
|
||||
};
|
||||
|
||||
struct CommerceDialogResult {
|
||||
int32_t result;
|
||||
bool authorized;
|
||||
void* user_data;
|
||||
uint8_t reserved[32];
|
||||
};
|
||||
|
||||
static_assert(sizeof(CommerceDialogParam) == 128 && offsetof(CommerceDialogParam, user_data) == 88);
|
||||
static_assert(sizeof(CommerceDialogResult) == 48 && offsetof(CommerceDialogResult, user_data) == 8);
|
||||
|
||||
static int g_commerce_status = COMMERCE_STATUS_NONE;
|
||||
static CommerceDialogResult g_commerce_result {};
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogInitialize() {
|
||||
PRINT_NAME();
|
||||
@@ -1520,9 +1550,47 @@ static int KYTY_SYSV_ABI NpCommerceDialogTerminate() {
|
||||
return COMMERCE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
g_commerce_status = COMMERCE_STATUS_NONE;
|
||||
g_commerce_result = {};
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogOpen(const CommerceDialogParam* param) {
|
||||
PRINT_NAME();
|
||||
if (g_commerce_status == COMMERCE_STATUS_NONE) {
|
||||
return COMMERCE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (param == nullptr) {
|
||||
return COMMERCE_ERROR_ARG_NULL;
|
||||
}
|
||||
g_commerce_result = {};
|
||||
if (param->size == sizeof(CommerceDialogParam)) {
|
||||
g_commerce_result.user_data = param->user_data;
|
||||
}
|
||||
g_commerce_status = COMMERCE_STATUS_FINISHED;
|
||||
if (param->size != sizeof(CommerceDialogParam) || param->mode < 0 || param->mode > 5 ||
|
||||
std::ranges::any_of(param->reserved, [](uint8_t value) { return value != 0; })) {
|
||||
g_commerce_result.result = COMMERCE_ERROR_PARAM_INVALID;
|
||||
return COMMERCE_ERROR_PARAM_INVALID;
|
||||
}
|
||||
g_commerce_result.result = COMMERCE_RESULT_USER_CANCELED;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogGetResult(CommerceDialogResult* result) {
|
||||
PRINT_NAME();
|
||||
if (g_commerce_status == COMMERCE_STATUS_NONE) {
|
||||
return COMMERCE_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (result == nullptr) {
|
||||
return COMMERCE_ERROR_ARG_NULL;
|
||||
}
|
||||
if (g_commerce_status != COMMERCE_STATUS_FINISHED) {
|
||||
return COMMERCE_ERROR_NOT_FINISHED;
|
||||
}
|
||||
*result = g_commerce_result;
|
||||
return result->result;
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI NpCommerceDialogUpdateStatus() {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -1532,6 +1600,8 @@ static int KYTY_SYSV_ABI NpCommerceDialogUpdateStatus() {
|
||||
LIB_DEFINE(InitNet_1_NpCommerce) {
|
||||
LIB_FUNC("0aR2aWmQal4", NpCommerceDialogInitialize);
|
||||
LIB_FUNC("m-I92Ab50W8", NpCommerceDialogTerminate);
|
||||
LIB_FUNC("DfSCDRA3EjY", NpCommerceDialogOpen);
|
||||
LIB_FUNC("r42bWcQbtZY", NpCommerceDialogGetResult);
|
||||
LIB_FUNC("LR5cwFMMCVE", NpCommerceDialogUpdateStatus);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,10 @@ namespace Libs::LibNpWebApi2 {
|
||||
void InitNet_1_NpWebApi2(Loader::SymbolDatabase *symbols);
|
||||
}
|
||||
|
||||
namespace Libs::LibNpCommerce {
|
||||
void InitNet_1_NpCommerce(Loader::SymbolDatabase *symbols);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
namespace FileSystem = Libs::LibKernel::FileSystem;
|
||||
@@ -385,6 +389,91 @@ void TestAioBatches() {
|
||||
Check(FileSystem::KernelClose(fd) == OK, "close AIO read fixture");
|
||||
}
|
||||
|
||||
void TestNpCommerceDialog() {
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::LibNpCommerce::InitNet_1_NpCommerce(&symbols);
|
||||
const auto find = [&](const char *nid) {
|
||||
const auto *symbol = symbols.FindByNid(nid, Loader::SymbolType::Func);
|
||||
Check(symbol != nullptr, "NpCommerce dialog exports resolve");
|
||||
return symbol->vaddr;
|
||||
};
|
||||
using Call = int (KYTY_SYSV_ABI *)();
|
||||
using Open = int (KYTY_SYSV_ABI *)(const void *);
|
||||
using GetResult = int (KYTY_SYSV_ABI *)(void *);
|
||||
const auto initialize = reinterpret_cast<Call>(find("0aR2aWmQal4"));
|
||||
const auto terminate = reinterpret_cast<Call>(find("m-I92Ab50W8"));
|
||||
const auto update = reinterpret_cast<Call>(find("LR5cwFMMCVE"));
|
||||
const auto open = reinterpret_cast<Open>(find("DfSCDRA3EjY"));
|
||||
const auto get_result = reinterpret_cast<GetResult>(find("r42bWcQbtZY"));
|
||||
struct BaseParam {
|
||||
uint64_t size;
|
||||
uint8_t reserved[36];
|
||||
uint32_t magic;
|
||||
};
|
||||
struct Param {
|
||||
BaseParam base;
|
||||
int32_t size, user_id, mode;
|
||||
uint32_t service_label;
|
||||
const char *const *targets;
|
||||
uint32_t num_targets, padding;
|
||||
uint64_t features;
|
||||
void *user_data;
|
||||
uint8_t reserved[32];
|
||||
} param {};
|
||||
struct Result {
|
||||
int32_t result;
|
||||
bool authorized;
|
||||
void *user_data;
|
||||
uint8_t reserved[32];
|
||||
} result {};
|
||||
static_assert(sizeof(Param) == 128 && offsetof(Param, user_data) == 88);
|
||||
static_assert(sizeof(Result) == 48 && offsetof(Result, user_data) == 8);
|
||||
param.base.size = sizeof(BaseParam);
|
||||
param.base.magic = static_cast<uint32_t>(0xC0D1A109u + reinterpret_cast<uintptr_t>(¶m.base));
|
||||
param.size = sizeof(param);
|
||||
param.user_id = 1;
|
||||
param.user_data = &symbols;
|
||||
Check(open(¶m) == static_cast<int>(0x80B80003u) &&
|
||||
get_result(&result) == static_cast<int>(0x80B80003u),
|
||||
"NpCommerce dialog calls require initialization");
|
||||
Check(initialize() == OK && update() == 1 &&
|
||||
get_result(&result) == static_cast<int>(0x80B80005u),
|
||||
"NpCommerce dialog result requires completion");
|
||||
Check(open(nullptr) == static_cast<int>(0x80B8000Du) && update() == 1 &&
|
||||
get_result(nullptr) == static_cast<int>(0x80B8000Du),
|
||||
"NpCommerce dialog rejects null arguments");
|
||||
Check(open(¶m) == OK && update() == 3, "NpCommerce dialog finishes without a store");
|
||||
param.user_data = nullptr;
|
||||
result.authorized = true;
|
||||
Check(get_result(&result) == 1 && result.result == 1 && !result.authorized &&
|
||||
result.user_data == &symbols,
|
||||
"NpCommerce cancellation returns the original user data");
|
||||
param.mode = 5;
|
||||
Check(open(¶m) == OK && get_result(&result) == 1 && !result.authorized &&
|
||||
result.user_data == nullptr,
|
||||
"NpCommerce dialog can reopen with fresh result data");
|
||||
const auto invalid_param = [&] {
|
||||
Check(open(¶m) == static_cast<int>(0x80B8000Au) && update() == 3 &&
|
||||
get_result(&result) == static_cast<int>(0x80B8000Au) &&
|
||||
result.result == static_cast<int>(0x80B8000Au) && !result.authorized,
|
||||
"NpCommerce dialog reports invalid parameters in the completion result");
|
||||
};
|
||||
param.mode = 99;
|
||||
invalid_param();
|
||||
param.mode = -1;
|
||||
invalid_param();
|
||||
param.mode = 0;
|
||||
param.size = 64;
|
||||
invalid_param();
|
||||
param.size = sizeof(param);
|
||||
param.reserved[0] = 1;
|
||||
invalid_param();
|
||||
param.reserved[0] = 0;
|
||||
Check(open(¶m) == OK && get_result(&result) == 1 && terminate() == OK && update() == 0 &&
|
||||
get_result(&result) == static_cast<int>(0x80B80003u),
|
||||
"NpCommerce dialog recovers from invalid parameters and terminates");
|
||||
}
|
||||
|
||||
void TestNpWebApi2Memory() {
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::LibNpWebApi2::InitNet_1_NpWebApi2(&symbols);
|
||||
@@ -1644,6 +1733,7 @@ int main(int, char**) {
|
||||
CheckSocketWakeup();
|
||||
CheckEtherAddressFormatting();
|
||||
TestNpWebApi2Memory();
|
||||
TestNpCommerceDialog();
|
||||
graphics.reset();
|
||||
subsystems.Destroy();
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "libs/agc.h"
|
||||
#include "libs/dialog.h"
|
||||
#include "libs/errno.h"
|
||||
#include "loader/symbolDatabase.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
#if __has_include("graphics/host_gpu/renderer/renderTargetBarriers.h")
|
||||
@@ -104,6 +105,10 @@
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace Libs {
|
||||
void InitAgcDriver_1(Loader::SymbolDatabase *symbols);
|
||||
}
|
||||
|
||||
namespace Libs::Graphics {
|
||||
|
||||
template <typename Cache>
|
||||
@@ -41645,35 +41650,97 @@ void CheckPm4WaitResume(RenderContext &renderer) {
|
||||
}
|
||||
|
||||
void CheckPm4RewindResume(RenderContext &renderer) {
|
||||
constexpr const char *name = "Pm4RewindResume";
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::InitAgcDriver_1(&symbols);
|
||||
const auto *emit_symbol = symbols.FindByNid("DwICrVxerkY", Loader::SymbolType::Func);
|
||||
const auto *patch_symbol = symbols.FindByNid("eWaWyFegzgQ", Loader::SymbolType::Func);
|
||||
Require(name, "async exports",
|
||||
emit_symbol != nullptr && patch_symbol != nullptr &&
|
||||
emit_symbol->vaddr == reinterpret_cast<uint64_t>(&Gen5::AgcAcbRewind) &&
|
||||
patch_symbol->vaddr ==
|
||||
reinterpret_cast<uint64_t>(&Gen5::AgcRewindPatchSetRewindState),
|
||||
"async rewind imports did not resolve to the emitter and state patch");
|
||||
const auto async_patch = reinterpret_cast<decltype(&Gen5::AgcRewindPatchSetRewindState)>(
|
||||
patch_symbol->vaddr);
|
||||
|
||||
constexpr uint32_t sentinel = 0x12345678u;
|
||||
std::array<uint32_t, 4> packet{sentinel, sentinel, sentinel, sentinel};
|
||||
CommandBufferLayout acb{packet.data(), packet.data() + packet.size(),
|
||||
packet.data() + 1, packet.data() + 3, nullptr, nullptr, 0};
|
||||
auto *buffer = reinterpret_cast<Gen5::CommandBuffer *>(&acb);
|
||||
for (const uint8_t state : {0, 1}) {
|
||||
for (const uint8_t offload : {0, 1}) {
|
||||
acb.cursor_up = packet.data() + 1;
|
||||
Require(name, "async packet",
|
||||
Gen5::AgcAcbRewind(buffer, state, offload) == packet.data() + 1 &&
|
||||
acb.cursor_up == packet.data() + 3 &&
|
||||
packet[0] == sentinel && packet[3] == sentinel &&
|
||||
packet[1] == 0xc0005900u &&
|
||||
packet[2] == ((uint32_t{state} << 31u) | (uint32_t{offload} << 24u)),
|
||||
"async rewind packet or two-DWORD allocation differs from the native command");
|
||||
}
|
||||
}
|
||||
const auto before = packet;
|
||||
acb.cursor_up = packet.data() + 1;
|
||||
acb.reserved_dw = 1;
|
||||
Require(name, "bounded allocation",
|
||||
Gen5::AgcAcbRewind(buffer, 1, 1) == nullptr &&
|
||||
Gen5::AgcAcbRewind(nullptr, 1, 1) == nullptr &&
|
||||
acb.cursor_up == packet.data() + 1 && packet == before,
|
||||
"failed rewind allocation changed the cursor or command storage");
|
||||
|
||||
// Native patchers inspect only the opcode, preserving the header and low 31 bits.
|
||||
std::array<uint32_t, 2> command{0xa5ff591fu, 0x0155aa55u};
|
||||
Require(name, "patch preserves fields",
|
||||
async_patch(command.data(), 1) == 0 && command[0] == 0xa5ff591fu &&
|
||||
command[1] == 0x8155aa55u && async_patch(command.data(), 0) == 0 &&
|
||||
command[0] == 0xa5ff591fu && command[1] == 0x0155aa55u,
|
||||
"rewind patch changed the offload bit, header, or unrelated payload bits");
|
||||
command[0] = 0xc0001000u;
|
||||
const auto invalid = command;
|
||||
Require(name, "wrong opcode",
|
||||
async_patch(command.data(), 1) == static_cast<int>(0x8a6c000cu) &&
|
||||
command == invalid,
|
||||
"rewind patch accepted or modified a packet with the wrong opcode");
|
||||
|
||||
GraphicsInitJmpTables();
|
||||
CommandProcessor processor(renderer, 0);
|
||||
|
||||
uint32_t suffix = 0;
|
||||
const auto address = reinterpret_cast<uint64_t>(&suffix);
|
||||
std::array<uint32_t, 7> commands{};
|
||||
commands[0] = KYTY_PM4(2, Pm4::IT_REWIND, 0);
|
||||
commands[1] = 0;
|
||||
commands[2] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0);
|
||||
commands[3] = 0;
|
||||
commands[4] = static_cast<uint32_t>(address);
|
||||
commands[5] = static_cast<uint32_t>(address >> 32u);
|
||||
commands[6] = 33;
|
||||
|
||||
Pm4Execution execution;
|
||||
Require("Pm4RewindResume", "pending",
|
||||
processor.Process(execution, commands) == Pm4ProcessResult::Blocked &&
|
||||
suffix == 0,
|
||||
"pending rewind did not preserve its command position");
|
||||
Require("Pm4RewindResume", "patch valid",
|
||||
Gen5::AgcRewindPatchSetRewindState(commands.data(), 1) == 0 &&
|
||||
commands[1] == 0x80000000u,
|
||||
"rewind state patch did not set the valid bit");
|
||||
Require("Pm4RewindResume", "resume",
|
||||
processor.Process(execution, commands) ==
|
||||
Pm4ProcessResult::Complete &&
|
||||
suffix == 33,
|
||||
"valid rewind did not resume at the following packet");
|
||||
std::printf("[host] %-32s ok\n", "Pm4RewindResume");
|
||||
for (const bool async : {false, true}) {
|
||||
for (const uint8_t offload : {0, 1}) {
|
||||
if (!async && offload != 0) {
|
||||
continue;
|
||||
}
|
||||
uint32_t suffix = 0;
|
||||
const auto address = reinterpret_cast<uint64_t>(&suffix);
|
||||
std::array<uint32_t, 7> commands{
|
||||
0, 0, KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0), 0,
|
||||
static_cast<uint32_t>(address), static_cast<uint32_t>(address >> 32u), 33};
|
||||
CommandBufferLayout cb{commands.data(), commands.data() + 2,
|
||||
commands.data(), commands.data() + 2, nullptr, nullptr, 0};
|
||||
auto *command_buffer = reinterpret_cast<Gen5::CommandBuffer *>(&cb);
|
||||
const auto *emitted = async ? Gen5::AgcAcbRewind(command_buffer, 0, offload)
|
||||
: Gen5::AgcDcbRewind(command_buffer, 0);
|
||||
const auto patch = async ? async_patch : &Gen5::AgcRewindPatchSetRewindState;
|
||||
Pm4Execution execution;
|
||||
Require(name, "pending",
|
||||
emitted == commands.data() &&
|
||||
processor.Process(execution, commands) == Pm4ProcessResult::Blocked &&
|
||||
suffix == 0,
|
||||
"pending rewind did not preserve its command position");
|
||||
Require(name, "still pending",
|
||||
processor.Process(execution, commands) == Pm4ProcessResult::Blocked &&
|
||||
!execution.MadeProgress() && suffix == 0,
|
||||
"pending rewind advanced before its state was patched");
|
||||
Require(name, "patch and resume",
|
||||
patch(commands.data(), 1) == 0 &&
|
||||
commands[1] == (0x80000000u | (uint32_t{offload} << 24u)) &&
|
||||
processor.Process(execution, commands) == Pm4ProcessResult::Complete &&
|
||||
suffix == 33,
|
||||
"patched rewind lost the offload bit or failed to resume");
|
||||
}
|
||||
}
|
||||
std::printf("[host] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
void CheckPm4CeCompletion(RenderContext &renderer) {
|
||||
|
||||
Reference in New Issue
Block a user