Merge pull request #4395 from oneandonlydean/up4/carry-1810

fix(agc): cache Linux describePages results
This commit is contained in:
Dean 2026-10-12 23:54:52 +13:00 committed by GitHub
commit 3d8ffeb271
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 56 additions and 10 deletions

View File

@ -423,6 +423,17 @@ struct PageStates {
arena.forget(address, bytes);
image.forget(address, bytes);
}
void record(std::uintptr_t base, std::uintptr_t regionEnd, bool readable, bool writable, std::uint64_t generation) {
for (PageSpan* span : {&arena, &image}) {
if (!readable || span->size == 0 || regionEnd <= span->base || base >= span->base + span->size) continue;
const std::uint8_t value = PageReadable | (writable ? PageWritable : 0u);
const auto first = std::max(base, span->base);
const auto last = std::min(regionEnd, span->base + span->size);
for (auto at = first; at < last; at += PageBytes) span->store(at, value);
if (GuestAllocations::GuestAllocationsGeneration_nid_postfix() != generation) span->forget(first, static_cast<std::size_t>(last - first));
}
}
};
PageStates& Pages() {
@ -533,18 +544,16 @@ bool describePages(std::uintptr_t address, std::size_t bytes, Emit&& emit) {
const bool committed = memory.State == MEM_COMMIT && (memory.Protect & (PAGE_GUARD | PAGE_NOACCESS)) == 0;
const bool readable = committed && readableProtection(protection);
const bool writable = readable && writableProtection(protection);
for (PageSpan* span : {&pages.arena, &pages.image}) {
if (!readable || span->size == 0 || regionEnd <= span->base || base >= span->base + span->size) continue;
const std::uint8_t value = PageReadable | (writable ? PageWritable : 0u);
const auto first = std::max(base, span->base);
const auto last = std::min(regionEnd, span->base + span->size);
for (auto at = first; at < last; at += PageBytes) span->store(at, value);
if (GuestAllocations::GuestAllocationsGeneration_nid_postfix() != generation) span->forget(first, static_cast<std::size_t>(last - first));
}
pages.record(base, regionEnd, readable, writable, generation);
const auto next = std::min(end, regionEnd);
if (!emit(PageRun{cursor, next, readable, writable})) return true;
cursor = next;
#else
const auto generation = GuestAllocations::GuestAllocationsGeneration_nid_postfix();
const auto recordRun = [&](std::uintptr_t next, bool readable, bool writable) {
constexpr auto mask = static_cast<std::uintptr_t>(PageBytes - 1);
pages.record(cursor & ~mask, (next + mask) & ~mask, readable, writable, generation);
};
if (const int fd = HostMapping::QueryFd(); fd >= 0) {
procmap_query query{};
query.size = sizeof(query);
@ -559,8 +568,10 @@ bool describePages(std::uintptr_t address, std::size_t bytes, Emit&& emit) {
continue;
}
const bool readable = (query.vma_flags & PROCMAP_QUERY_VMA_READABLE) != 0;
const bool writable = readable && (query.vma_flags & PROCMAP_QUERY_VMA_WRITABLE) != 0;
const auto next = std::min<std::uintptr_t>(end, query.vma_end);
if (!emit(PageRun{cursor, next, readable, readable && (query.vma_flags & PROCMAP_QUERY_VMA_WRITABLE) != 0})) return true;
recordRun(next, readable, writable);
if (!emit(PageRun{cursor, next, readable, writable})) return true;
cursor = next;
continue;
}
@ -589,8 +600,11 @@ bool describePages(std::uintptr_t address, std::size_t bytes, Emit&& emit) {
found = true;
break;
}
const bool readable = permissions[0] == 'r';
const bool writable = readable && permissions[1] == 'w';
const auto next = std::min(end, last);
if (!emit(PageRun{cursor, next, permissions[0] == 'r', permissions[0] == 'r' && permissions[1] == 'w'})) return true;
recordRun(next, readable, writable);
if (!emit(PageRun{cursor, next, readable, writable})) return true;
cursor = next;
found = true;
break;

View File

@ -1,5 +1,6 @@
#include "BdaTests.hpp"
#include "prx/libc/include/GuestAllocations.hpp"
#include "prx/libc/include/GuestArena.hpp"
#include "prx/libc/include/GuestHeap.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp"
@ -187,5 +188,36 @@ void RunUnmappedGapTests() {
Require(!GuestMemory::Accessible(reinterpret_cast<const void*>(std::uintptr_t{0x18}), 4), "a near-null address counts as accessible");
munmap(block, page);
munmap(block + 2 * page, page);
if (!GuestArena::GuestArenaAvailable_nid_postfix()) return;
auto* guest = static_cast<std::byte*>(GuestArena::GuestArenaAllocate_nid_postfix(2 * page, page));
Require(mmap(guest, 2 * page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == guest, "cannot map the arena page test pages");
{
GuestAllocations::Mutation mutation;
mutation.Add(guest, 2 * page, true, true, true);
}
Require(GuestMemory::Accessible(guest, page, true), "a mapped arena page is not writable");
Require(mprotect(guest, page, PROT_NONE) == 0, "cannot revoke the arena test page behind the registry");
Require(GuestMemory::Accessible(guest, page, true), "a checked arena page was queried again instead of read from the page table");
Require(mprotect(guest, page, PROT_READ | PROT_WRITE) == 0, "cannot restore the arena test page behind the registry");
Require(mprotect(guest + page, page, PROT_READ) == 0, "cannot protect the arena test's second page");
Require(!GuestMemory::Accessible(guest + page, page, true), "a check stored arena pages outside its range");
{
GuestAllocations::Mutation mutation;
mutation.Protect(guest + page, page, true, true, true, [&] { Require(mprotect(guest + page, page, PROT_READ | PROT_WRITE) == 0, "cannot restore the arena test's second page"); });
}
Require(GuestMemory::Accessible(guest, 2 * page, true), "mapped arena pages are not writable");
{
GuestAllocations::Mutation mutation;
mutation.Protect(guest, page, true, false, true, [&] { Require(mprotect(guest, page, PROT_READ) == 0, "cannot protect the arena test page"); });
}
Require(!GuestMemory::Accessible(guest, page, true) && GuestMemory::Accessible(guest, page) && GuestMemory::Accessible(guest + page, page, true), "an arena page protected through the registry kept its old access");
{
GuestAllocations::Mutation mutation;
const auto release = [](const void* pointer, std::size_t bytes, const void*, bool) { Require(munmap(const_cast<void*>(pointer), bytes) == 0, "cannot unmap an arena test page"); };
mutation.Unmap(guest, page, release);
mutation.Unmap(guest + page, page, release);
}
Require(!GuestMemory::Accessible(guest, page) && !GuestMemory::Accessible(guest + page, page), "unmapped arena pages are still accessible");
GuestArena::GuestArenaRelease_nid_postfix(guest, 2 * page);
#endif
}