mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01:00
graphics: upload sparse PRT image backing
This commit is contained in:
+2
-1
@@ -621,7 +621,8 @@ ImageBufferSource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t siz
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto [staging, stage_offset] = m_staging_buffer.Map(size, 16);
|
auto [staging, stage_offset] = m_staging_buffer.Map(size, 16);
|
||||||
if (staging == nullptr || !Libs::LibKernel::Memory::TryReadBacking(vaddr, staging, size)) {
|
if (staging == nullptr || (!Libs::LibKernel::Memory::TryReadBacking(vaddr, staging, size) &&
|
||||||
|
!Libs::LibKernel::Memory::TryReadPrtBacking(vaddr, staging, size))) {
|
||||||
EXIT("BufferCache: failed to read mapped guest image backing\n");
|
EXIT("BufferCache: failed to read mapped guest image backing\n");
|
||||||
}
|
}
|
||||||
m_staging_buffer.Commit();
|
m_staging_buffer.Commit();
|
||||||
|
|||||||
+23
-2
@@ -921,18 +921,39 @@ constexpr uint64_t PRT_APERTURE_END = 0xfc00000000ull;
|
|||||||
static std::array<PrtAperture, PRT_APERTURE_MAX_INDEX + 1> g_prt_apertures {};
|
static std::array<PrtAperture, PRT_APERTURE_MAX_INDEX + 1> g_prt_apertures {};
|
||||||
static Common::Mutex g_prt_aperture_mutex;
|
static Common::Mutex g_prt_aperture_mutex;
|
||||||
|
|
||||||
static bool IsInPrtAperture(uint64_t address) {
|
static bool IsInPrtAperture(uint64_t address, uint64_t size = 1) {
|
||||||
|
if (size == 0 || UINT64_MAX - address < size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Common::LockGuard lock(g_prt_aperture_mutex);
|
Common::LockGuard lock(g_prt_aperture_mutex);
|
||||||
|
|
||||||
for (const auto& aperture: g_prt_apertures) {
|
for (const auto& aperture: g_prt_apertures) {
|
||||||
if (address >= aperture.address && address < aperture.address + aperture.size) {
|
if (address >= aperture.address) {
|
||||||
|
const auto offset = address - aperture.address;
|
||||||
|
if (offset < aperture.size && size <= aperture.size - offset) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TryReadPrtBacking(uint64_t vaddr, void* data, uint64_t size) {
|
||||||
|
std::vector<VirtualRanges::Range> ranges;
|
||||||
|
if (g_guest_address_space == nullptr || g_virtual_ranges == nullptr ||
|
||||||
|
!IsInPrtAperture(vaddr, size) || !g_virtual_ranges->QuerySpan(vaddr, size, &ranges)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (std::any_of(ranges.begin(), ranges.end(), [](const auto& range) {
|
||||||
|
return !IsReservedRangeType(range.type) &&
|
||||||
|
!g_guest_address_space->BackingContains(range.start, range.size);
|
||||||
|
})) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return g_guest_address_space->TryReadSparseBacking(vaddr, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
static bool SelfTestSub64SharedPlaceholderAlias() {
|
static bool SelfTestSub64SharedPlaceholderAlias() {
|
||||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||||
constexpr uint64_t PageSize = 0x4000;
|
constexpr uint64_t PageSize = 0x4000;
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ void RegisterCallbacks(callback_func_t alloc_func, callback_fu
|
|||||||
void SetFlexibleMemorySize(uint64_t size);
|
void SetFlexibleMemorySize(uint64_t size);
|
||||||
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size);
|
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size);
|
||||||
bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size);
|
bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size);
|
||||||
|
bool TryReadPrtBacking(uint64_t vaddr, void* data, uint64_t size);
|
||||||
[[nodiscard]] uint64_t ClampRangeSize(uint64_t vaddr, uint64_t size);
|
[[nodiscard]] uint64_t ClampRangeSize(uint64_t vaddr, uint64_t size);
|
||||||
void WriteBacking(uint64_t vaddr, const void* data, uint64_t size) noexcept;
|
void WriteBacking(uint64_t vaddr, const void* data, uint64_t size) noexcept;
|
||||||
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
void InvalidateMemory(uint64_t vaddr, uint64_t size);
|
||||||
|
|||||||
@@ -188,6 +188,43 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TryReadSparseBacking(uint64_t vaddr, void* data, uint64_t size) {
|
||||||
|
if (!IsAvailable() || data == nullptr || size == 0 || UINT64_MAX - vaddr < size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
const auto end = vaddr + size;
|
||||||
|
auto first = m_maps.lower_bound(vaddr);
|
||||||
|
if (first != m_maps.begin()) {
|
||||||
|
auto previous = std::prev(first);
|
||||||
|
if (previous->second.vaddr + previous->second.size > vaddr) {
|
||||||
|
first = previous;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto mapping = first; mapping != m_maps.end() && mapping->second.vaddr < end;
|
||||||
|
++mapping) {
|
||||||
|
const auto& entry = mapping->second;
|
||||||
|
const auto start = std::max(vaddr, entry.vaddr);
|
||||||
|
const auto bytes = std::min(end, entry.vaddr + entry.size) - start;
|
||||||
|
if (!RangeOk(entry.backing_offset + start - entry.vaddr, bytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* destination = static_cast<uint8_t*>(data);
|
||||||
|
std::memset(destination, 0, size);
|
||||||
|
for (auto mapping = first; mapping != m_maps.end() && mapping->second.vaddr < end;
|
||||||
|
++mapping) {
|
||||||
|
const auto& entry = mapping->second;
|
||||||
|
const auto start = std::max(vaddr, entry.vaddr);
|
||||||
|
const auto bytes = std::min(end, entry.vaddr + entry.size) - start;
|
||||||
|
const auto offset = entry.backing_offset + start - entry.vaddr;
|
||||||
|
std::memcpy(destination + start - vaddr, m_backing_base + offset, bytes);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool MapExistingPlaceholderFixed(uint64_t vaddr, uint64_t size, uint64_t backing_offset,
|
bool MapExistingPlaceholderFixed(uint64_t vaddr, uint64_t size, uint64_t backing_offset,
|
||||||
VirtualMemory::Mode mode,
|
VirtualMemory::Mode mode,
|
||||||
FailureReason* failure_reason = nullptr) {
|
FailureReason* failure_reason = nullptr) {
|
||||||
@@ -694,6 +731,10 @@ public:
|
|||||||
return m_backing->TryReadBacking(vaddr, data, size);
|
return m_backing->TryReadBacking(vaddr, data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TryReadSparseBacking(uint64_t vaddr, void* data, uint64_t size) {
|
||||||
|
return m_backing->TryReadSparseBacking(vaddr, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
bool BackingContains(uint64_t vaddr, uint64_t size) { return m_backing->Contains(vaddr, size); }
|
bool BackingContains(uint64_t vaddr, uint64_t size) { return m_backing->Contains(vaddr, size); }
|
||||||
|
|
||||||
bool Protect(uint64_t vaddr, uint64_t size, VirtualMemory::Mode mode) {
|
bool Protect(uint64_t vaddr, uint64_t size, VirtualMemory::Mode mode) {
|
||||||
|
|||||||
@@ -354,6 +354,84 @@ void TestGuestAddressSpaceOwnsReservationsBeforeBacking() {
|
|||||||
std::printf("[host] %-48s ok\n", test);
|
std::printf("[host] %-48s ok\n", test);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestPrtBackingReadPreservesSparseResidency() {
|
||||||
|
const char* test = "PrtBackingReadPreservesSparseResidency";
|
||||||
|
constexpr uint64_t commit_size = SceKernelMemoryPoolCommitLen;
|
||||||
|
constexpr uint64_t aperture_len = commit_size * 3;
|
||||||
|
int64_t pool_offset = -1;
|
||||||
|
CheckOk(test,
|
||||||
|
Libs::LibKernel::Memory::KernelMemoryPoolExpand(
|
||||||
|
0, Libs::LibKernel::Memory::KernelGetDirectMemorySize(), commit_size * 2,
|
||||||
|
SceKernelMemoryPoolAlignment, &pool_offset),
|
||||||
|
"KernelMemoryPoolExpand");
|
||||||
|
|
||||||
|
void* arena = nullptr;
|
||||||
|
CheckOk(test,
|
||||||
|
Libs::LibKernel::Memory::KernelMemoryPoolReserve(
|
||||||
|
reinterpret_cast<void*>(0x1000000000ull), SceKernelMemoryPoolReserveLen, 0, 0,
|
||||||
|
&arena),
|
||||||
|
"KernelMemoryPoolReserve");
|
||||||
|
const auto base = reinterpret_cast<uint64_t>(arena);
|
||||||
|
CheckOk(test,
|
||||||
|
Libs::LibKernel::Memory::KernelMemoryPoolCommit(
|
||||||
|
arena, commit_size, SceKernelMtypeC, SceKernelProtCpuRw, 0),
|
||||||
|
"KernelMemoryPoolCommit(first)");
|
||||||
|
CheckOk(test,
|
||||||
|
Libs::LibKernel::Memory::KernelMemoryPoolCommit(
|
||||||
|
reinterpret_cast<void*>(base + commit_size * 2), commit_size, SceKernelMtypeC,
|
||||||
|
SceKernelProtCpuRw, 0),
|
||||||
|
"KernelMemoryPoolCommit(third)");
|
||||||
|
std::memset(reinterpret_cast<void*>(base), 0x3c, commit_size);
|
||||||
|
std::memset(reinterpret_cast<void*>(base + commit_size * 2), 0xa7, commit_size);
|
||||||
|
|
||||||
|
CheckOk(test, Libs::LibKernel::Memory::KernelSetPrtAperture(2, arena, aperture_len),
|
||||||
|
"KernelSetPrtAperture");
|
||||||
|
std::vector<uint8_t> bytes(aperture_len, 0x5a);
|
||||||
|
Check(test, !Libs::LibKernel::Memory::TryReadBacking(base, bytes.data(), bytes.size()),
|
||||||
|
"dense backing read accepted a nonresident span");
|
||||||
|
Check(test, std::all_of(bytes.begin(), bytes.end(), [](uint8_t value) { return value == 0x5a; }),
|
||||||
|
"failed dense backing read modified its destination");
|
||||||
|
Check(test, Libs::LibKernel::Memory::TryReadPrtBacking(base, bytes.data(), bytes.size()),
|
||||||
|
"PRT backing read rejected a valid sparse aperture range");
|
||||||
|
Check(test,
|
||||||
|
std::all_of(bytes.begin(), bytes.begin() + commit_size,
|
||||||
|
[](uint8_t value) { return value == 0x3c; }) &&
|
||||||
|
std::all_of(bytes.begin() + commit_size, bytes.begin() + commit_size * 2,
|
||||||
|
[](uint8_t value) { return value == 0; }) &&
|
||||||
|
std::all_of(bytes.begin() + commit_size * 2, bytes.end(),
|
||||||
|
[](uint8_t value) { return value == 0xa7; }),
|
||||||
|
"PRT backing read did not copy resident pages and zero nonresident pages");
|
||||||
|
Check(test,
|
||||||
|
!Libs::LibKernel::Memory::TryReadPrtBacking(base + commit_size * 2, bytes.data(),
|
||||||
|
commit_size * 2),
|
||||||
|
"PRT backing read crossed the registered aperture");
|
||||||
|
|
||||||
|
constexpr uint64_t unowned_prt = 0x5000000000ull;
|
||||||
|
CheckOk(test,
|
||||||
|
Libs::LibKernel::Memory::KernelSetPrtAperture(
|
||||||
|
2, reinterpret_cast<void*>(unowned_prt), commit_size),
|
||||||
|
"KernelSetPrtAperture(unowned)");
|
||||||
|
Check(test,
|
||||||
|
!Libs::LibKernel::Memory::TryReadPrtBacking(unowned_prt, bytes.data(), commit_size),
|
||||||
|
"PRT backing read accepted an unowned virtual range");
|
||||||
|
CheckOk(test, Libs::LibKernel::Memory::KernelSetPrtAperture(2, nullptr, 0),
|
||||||
|
"KernelSetPrtAperture(clear)");
|
||||||
|
|
||||||
|
CheckOk(test, Libs::LibKernel::Memory::KernelMemoryPoolDecommit(arena, commit_size, 0),
|
||||||
|
"KernelMemoryPoolDecommit(first)");
|
||||||
|
CheckOk(test,
|
||||||
|
Libs::LibKernel::Memory::KernelMemoryPoolDecommit(
|
||||||
|
reinterpret_cast<void*>(base + commit_size * 2), commit_size, 0),
|
||||||
|
"KernelMemoryPoolDecommit(third)");
|
||||||
|
CheckOk(test, Libs::LibKernel::Memory::KernelMunmap(base, SceKernelMemoryPoolReserveLen),
|
||||||
|
"KernelMunmap(pool reserve)");
|
||||||
|
CheckOk(test,
|
||||||
|
Libs::LibKernel::Memory::KernelReleaseDirectMemory(pool_offset, commit_size * 2),
|
||||||
|
"KernelReleaseDirectMemory(pool expansion)");
|
||||||
|
|
||||||
|
std::printf("[host] %-48s ok\n", test);
|
||||||
|
}
|
||||||
|
|
||||||
void TestGuestAddressSpaceHasNoFixedFallback() {
|
void TestGuestAddressSpaceHasNoFixedFallback() {
|
||||||
const char* test = "GuestAddressSpaceHasNoFixedFallback";
|
const char* test = "GuestAddressSpaceHasNoFixedFallback";
|
||||||
const auto unowned_address = reinterpret_cast<void*>(0x10000);
|
const auto unowned_address = reinterpret_cast<void*>(0x10000);
|
||||||
@@ -2440,6 +2518,7 @@ int main(int argc, char** argv) {
|
|||||||
RunTest(TestWindowsGuestRedZoneStaticPatcher);
|
RunTest(TestWindowsGuestRedZoneStaticPatcher);
|
||||||
RunTest(TestProsperoArgumentAndInfoSizeContracts);
|
RunTest(TestProsperoArgumentAndInfoSizeContracts);
|
||||||
RunTest(TestGuestAddressSpaceOwnsReservationsBeforeBacking);
|
RunTest(TestGuestAddressSpaceOwnsReservationsBeforeBacking);
|
||||||
|
RunTest(TestPrtBackingReadPreservesSparseResidency);
|
||||||
RunTest(TestGuestAddressSpaceHasNoFixedFallback);
|
RunTest(TestGuestAddressSpaceHasNoFixedFallback);
|
||||||
RunTest(TestGuestFreeRangeSearchDoesNotUnderflow);
|
RunTest(TestGuestFreeRangeSearchDoesNotUnderflow);
|
||||||
RunTest(TestFlexibleMemoryCapacityIsBootFixed);
|
RunTest(TestFlexibleMemoryCapacityIsBootFixed);
|
||||||
|
|||||||
Reference in New Issue
Block a user