mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01:00
memory: write GPU publishes to backed pages and skip reserved holes like sparse reads
This commit is contained in:
@@ -28,9 +28,9 @@ static bool CoalescePlaceholders(uint64_t vaddr, uint64_t size) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename Regions, typename Source, typename Gap>
|
template <typename Regions, typename Region, typename Gap>
|
||||||
static void CopySparseRange(const Regions& regions, uint64_t vaddr, void* data, uint64_t size,
|
static void ForEachSparseRange(const Regions& regions, uint64_t vaddr, uint64_t size,
|
||||||
Source&& source, Gap&& gap) {
|
Region&& region, Gap&& gap) {
|
||||||
const auto end = vaddr + size;
|
const auto end = vaddr + size;
|
||||||
auto mapping = regions.lower_bound(vaddr);
|
auto mapping = regions.lower_bound(vaddr);
|
||||||
if (mapping != regions.begin()) {
|
if (mapping != regions.begin()) {
|
||||||
@@ -40,23 +40,34 @@ static void CopySparseRange(const Regions& regions, uint64_t vaddr, void* data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* destination = static_cast<uint8_t*>(data);
|
|
||||||
auto cursor = vaddr;
|
auto cursor = vaddr;
|
||||||
for (; mapping != regions.end() && mapping->second.vaddr < end; ++mapping) {
|
for (; mapping != regions.end() && mapping->second.vaddr < end; ++mapping) {
|
||||||
const auto& entry = mapping->second;
|
const auto& entry = mapping->second;
|
||||||
const auto start = std::max(vaddr, entry.vaddr);
|
const auto start = std::max(vaddr, entry.vaddr);
|
||||||
const auto bytes = std::min(end, entry.vaddr + entry.size) - start;
|
const auto bytes = std::min(end, entry.vaddr + entry.size) - start;
|
||||||
if (cursor < start) {
|
if (cursor < start) {
|
||||||
gap(cursor, destination + (cursor - vaddr), start - cursor);
|
gap(cursor, start - cursor);
|
||||||
}
|
}
|
||||||
std::memcpy(destination + (start - vaddr), source(entry, start), bytes);
|
region(entry, start, bytes);
|
||||||
cursor = start + bytes;
|
cursor = start + bytes;
|
||||||
}
|
}
|
||||||
if (cursor < end) {
|
if (cursor < end) {
|
||||||
gap(cursor, destination + (cursor - vaddr), end - cursor);
|
gap(cursor, end - cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Regions, typename Source, typename Gap>
|
||||||
|
static void CopySparseRange(const Regions& regions, uint64_t vaddr, void* data, uint64_t size,
|
||||||
|
Source&& source, Gap&& gap) {
|
||||||
|
auto* destination = static_cast<uint8_t*>(data);
|
||||||
|
ForEachSparseRange(
|
||||||
|
regions, vaddr, size,
|
||||||
|
[&](const auto& entry, uint64_t start, uint64_t bytes) {
|
||||||
|
std::memcpy(destination + (start - vaddr), source(entry, start), bytes);
|
||||||
|
},
|
||||||
|
[&](uint64_t start, uint64_t bytes) { gap(start, destination + (start - vaddr), bytes); });
|
||||||
|
}
|
||||||
|
|
||||||
class GuestBackingStore {
|
class GuestBackingStore {
|
||||||
public:
|
public:
|
||||||
enum class FailureReason {
|
enum class FailureReason {
|
||||||
@@ -200,36 +211,16 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip unbacked gaps, mirroring sparse reads that zero-fill them.
|
||||||
std::lock_guard lock(m_mutex);
|
std::lock_guard lock(m_mutex);
|
||||||
const auto end = vaddr + size;
|
|
||||||
auto first = m_maps.upper_bound(vaddr);
|
|
||||||
if (first == m_maps.begin()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
--first;
|
|
||||||
auto current = vaddr;
|
|
||||||
for (auto mapping = first; current < end; ++mapping) {
|
|
||||||
if (mapping == m_maps.end() || current < mapping->second.vaddr ||
|
|
||||||
current >= mapping->second.vaddr + mapping->second.size) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto& entry = mapping->second;
|
|
||||||
const auto bytes = std::min(end, entry.vaddr + entry.size) - current;
|
|
||||||
if (!RangeOk(entry.backing_offset + current - entry.vaddr, bytes)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
current += bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto* source = static_cast<const uint8_t*>(data);
|
const auto* source = static_cast<const uint8_t*>(data);
|
||||||
current = vaddr;
|
ForEachSparseRange(
|
||||||
for (auto mapping = first; current < end; ++mapping) {
|
m_maps, vaddr, size,
|
||||||
const auto& entry = mapping->second;
|
[&](const auto& entry, uint64_t start, uint64_t bytes) {
|
||||||
const auto bytes = std::min(end, entry.vaddr + entry.size) - current;
|
std::memcpy(m_backing_base + (entry.backing_offset + start - entry.vaddr),
|
||||||
const auto offset = entry.backing_offset + current - entry.vaddr;
|
source + (start - vaddr), bytes);
|
||||||
std::memcpy(m_backing_base + offset, source + (current - vaddr), bytes);
|
},
|
||||||
current += bytes;
|
[](uint64_t, uint64_t) {});
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -775,8 +766,14 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes the backed bytes of a range that holds no private mappings.
|
||||||
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size) {
|
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size) {
|
||||||
return m_backing->TryWriteBacking(vaddr, data, size);
|
if (!Owns(vaddr, size)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::shared_lock lock(m_mutex);
|
||||||
|
return !OverlapsPrivateUnlocked(vaddr, size) &&
|
||||||
|
m_backing->TryWriteBacking(vaddr, data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size) {
|
bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size) {
|
||||||
@@ -790,17 +787,10 @@ public:
|
|||||||
}
|
}
|
||||||
std::shared_lock lock(m_mutex);
|
std::shared_lock lock(m_mutex);
|
||||||
if constexpr (!copy_private) {
|
if constexpr (!copy_private) {
|
||||||
auto mapping = m_mapped.upper_bound(vaddr);
|
if (OverlapsPrivateUnlocked(vaddr, size)) {
|
||||||
if (mapping != m_mapped.begin()) {
|
|
||||||
--mapping;
|
|
||||||
}
|
|
||||||
for (; mapping != m_mapped.end() && mapping->second.vaddr < vaddr + size; ++mapping) {
|
|
||||||
const auto& region = mapping->second;
|
|
||||||
if (region.type == MappingType::Private && region.vaddr + region.size > vaddr) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return m_backing->TryReadSparseBacking(
|
return m_backing->TryReadSparseBacking(
|
||||||
vaddr, data, size, [&](uint64_t start, uint8_t* destination, uint64_t bytes) {
|
vaddr, data, size, [&](uint64_t start, uint8_t* destination, uint64_t bytes) {
|
||||||
const auto zero = [](uint64_t, uint8_t* output, uint64_t length) {
|
const auto zero = [](uint64_t, uint8_t* output, uint64_t length) {
|
||||||
@@ -1111,6 +1101,20 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool OverlapsPrivateUnlocked(uint64_t vaddr, uint64_t size) const {
|
||||||
|
auto mapping = m_mapped.upper_bound(vaddr);
|
||||||
|
if (mapping != m_mapped.begin()) {
|
||||||
|
--mapping;
|
||||||
|
}
|
||||||
|
for (; mapping != m_mapped.end() && mapping->second.vaddr < vaddr + size; ++mapping) {
|
||||||
|
const auto& region = mapping->second;
|
||||||
|
if (region.type == MappingType::Private && region.vaddr + region.size > vaddr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool AddMappedUnlocked(uint64_t vaddr, uint64_t size, MappingType type) {
|
bool AddMappedUnlocked(uint64_t vaddr, uint64_t size, MappingType type) {
|
||||||
if (size == 0 || UINT64_MAX - vaddr < size) {
|
if (size == 0 || UINT64_MAX - vaddr < size) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1596,20 +1596,23 @@ void TestDirectMapQueryOffsetAndPartialMunmap() {
|
|||||||
ExpectUnmapped(test, base + SceKernelPageSize);
|
ExpectUnmapped(test, base + SceKernelPageSize);
|
||||||
|
|
||||||
constexpr uint64_t transaction_sentinel = 0x5452414e53414354ull; // "TRANSACT"
|
constexpr uint64_t transaction_sentinel = 0x5452414e53414354ull; // "TRANSACT"
|
||||||
constexpr uint64_t rejected_write = 0x4e4f504152544941ull; // "NOPARTIA"
|
constexpr uint64_t sparse_write = 0x4e4f504152544941ull; // "NOPARTIA"
|
||||||
|
constexpr uint64_t backed_prefix_mask = 0xffffffffull;
|
||||||
const auto crossing_address = base + SceKernelPageSize - sizeof(uint32_t);
|
const auto crossing_address = base + SceKernelPageSize - sizeof(uint32_t);
|
||||||
std::memcpy(reinterpret_cast<void*>(alias_base + SceKernelPageSize - sizeof(uint32_t)),
|
std::memcpy(reinterpret_cast<void*>(alias_base + SceKernelPageSize - sizeof(uint32_t)),
|
||||||
&transaction_sentinel, sizeof(transaction_sentinel));
|
&transaction_sentinel, sizeof(transaction_sentinel));
|
||||||
Check(test,
|
Check(test,
|
||||||
!Libs::LibKernel::Memory::TryWriteBacking(crossing_address, &rejected_write,
|
Libs::LibKernel::Memory::TryWriteBacking(crossing_address, &sparse_write,
|
||||||
sizeof(rejected_write)),
|
sizeof(sparse_write)),
|
||||||
"TryWriteBacking should reject a range crossing an unmapped span");
|
"TryWriteBacking should write a range crossing an unmapped span");
|
||||||
uint64_t backing_after_rejected_write = 0;
|
uint64_t backing_after_sparse_write = 0;
|
||||||
std::memcpy(&backing_after_rejected_write,
|
std::memcpy(&backing_after_sparse_write,
|
||||||
reinterpret_cast<const void*>(alias_base + SceKernelPageSize - sizeof(uint32_t)),
|
reinterpret_cast<const void*>(alias_base + SceKernelPageSize - sizeof(uint32_t)),
|
||||||
sizeof(backing_after_rejected_write));
|
sizeof(backing_after_sparse_write));
|
||||||
Check(test, backing_after_rejected_write == transaction_sentinel,
|
Check(test,
|
||||||
"failed backing writes must not modify a validated prefix");
|
backing_after_sparse_write == ((sparse_write & backed_prefix_mask) |
|
||||||
|
(transaction_sentinel & ~backed_prefix_mask)),
|
||||||
|
"sparse backing writes must fill the backed prefix and skip the unmapped span");
|
||||||
uint64_t rejected_read = transaction_sentinel;
|
uint64_t rejected_read = transaction_sentinel;
|
||||||
Check(test,
|
Check(test,
|
||||||
!Libs::LibKernel::Memory::TryReadBacking(crossing_address, &rejected_read,
|
!Libs::LibKernel::Memory::TryReadBacking(crossing_address, &rejected_read,
|
||||||
|
|||||||
Reference in New Issue
Block a user