fix(agc): address-based builds map only memory mapped for the GPU

The registry records whether a range was mapped or protected with GPU
access (0x10/0x20), and an address-based build leaves out ranges without
it, as the console's GPU page tables do. The main image is registered
without GPU access; the title's own mprotect with GPU bits adds it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
raghav314 2026-10-08 08:40:14 +05:30
parent 3d1a7d90c7
commit c11a9cb59f
55 changed files with 189 additions and 113 deletions

View File

@ -1810,7 +1810,7 @@ void GuestBufferMemory::AcquireRegistered() {
std::vector<AddressCopy> copies;
std::vector<std::uint64_t> imported;
for (const auto& range : lease) {
if (!range->readable) continue;
if (!range->readable || !range->gpu) continue;
validate(range->address, range->bytes);
Region region{range->address, range->address + range->bytes, range->writable, {}, nullptr};
const HostImport* entry = nullptr;

View File

@ -53,7 +53,7 @@ void importCrossingTests(const Context& context, const BdaTestAccess& access) {
const auto registry = [&](bool add) {
auto* mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix();
for (auto* range : {guest, guest + half}) {
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, range, half, true, false);
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, range, half, true, false, true);
else GuestAllocations::GuestAllocationsRemove_nid_postfix(mutation, range);
}
GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation);
@ -108,7 +108,7 @@ void importedHeapMirrorTests(const Context& context, const BdaTestAccess& access
importing.hostImportAlignment = bytes;
const auto registry = [&](bool add) {
auto* mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix();
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, block, bytes, true, false);
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, block, bytes, true, false, true);
else GuestAllocations::GuestAllocationsRemove_nid_postfix(mutation, block);
GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation);
};
@ -163,7 +163,7 @@ void importedFreshTests(const Context& context) {
importing.hostImportAlignment = blockBytes;
const auto registry = [&](bool add) {
auto* mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix();
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, reinterpret_cast<void*>(base), blockBytes, true, true);
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, reinterpret_cast<void*>(base), blockBytes, true, true, true);
else GuestAllocations::GuestAllocationsRemove_nid_postfix(mutation, reinterpret_cast<void*>(base));
GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation);
};
@ -279,7 +279,7 @@ void heapMirrorTests(const Context& context, const BdaTestAccess& access) {
const auto address = reinterpret_cast<std::uintptr_t>(block);
const auto registry = [&](bool add, bool writable) {
auto* mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix();
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, block, bytes, true, writable);
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, block, bytes, true, writable, true);
else GuestAllocations::GuestAllocationsRemove_nid_postfix(mutation, block);
GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation);
};
@ -370,7 +370,7 @@ void heapMirrorTests(const Context& context, const BdaTestAccess& access) {
const auto registerPair = [&](bool add) {
auto* mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix();
for (auto* range : {first, second}) {
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, range, half, true, false);
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, range, half, true, false, true);
else GuestAllocations::GuestAllocationsRemove_nid_postfix(mutation, range);
}
GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation);
@ -423,7 +423,7 @@ void heapMirrorTests(const Context& context, const BdaTestAccess& access) {
GuestAllocations::GuestAllocationsInvalidate_nid_postfix(page, 4096);
const auto registerRange = [&](bool add) {
auto* mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix();
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, raw, size, true, true);
if (add) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, raw, size, true, true, true);
else GuestAllocations::GuestAllocationsRemove_nid_postfix(mutation, raw);
GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation);
};
@ -483,7 +483,7 @@ void heapMirrorTests(const Context& context, const BdaTestAccess& access) {
const auto change = [&](std::initializer_list<std::size_t> added, std::initializer_list<std::size_t> removed) {
auto* mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix();
for (const auto index : removed) GuestAllocations::GuestAllocationsRemove_nid_postfix(mutation, heaps[index]);
for (const auto index : added) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, heaps[index], sizes[index], true, false);
for (const auto index : added) GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, heaps[index], sizes[index], true, false, true);
GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation);
};
const auto mirrored = [&](std::size_t index) {
@ -541,6 +541,34 @@ void heapMirrorTests(const Context& context, const BdaTestAccess& access) {
}
void gpuMappingTests(const Context& context) {
constexpr std::size_t bytes = 65536;
auto* cpu = static_cast<std::byte*>(::operator new(bytes, std::align_val_t{bytes}));
auto* gpu = static_cast<std::byte*>(::operator new(bytes, std::align_val_t{bytes}));
{
GuestAllocations::Mutation mutation;
mutation.Add(cpu, bytes, true, true, false);
mutation.Add(gpu, bytes, true, true, true);
}
{
GuestBufferMemory leased(context);
leased.AcquireRegistered();
leased.Upload(true);
const auto ranges = leased.AddressRanges();
const auto has = [&](const std::byte* block) { return std::any_of(ranges.begin(), ranges.end(), [&](const auto& range) { return range.begin == reinterpret_cast<std::uintptr_t>(block); }); };
Require(has(gpu), "a GPU-mapped range is missing from the BDA table");
Require(!has(cpu), "a range mapped without GPU access is in the BDA table");
leased.WriteBack();
}
{
GuestAllocations::Mutation mutation;
mutation.Remove(cpu);
mutation.Remove(gpu);
}
::operator delete(cpu, std::align_val_t{bytes});
::operator delete(gpu, std::align_val_t{bytes});
}
void RunBdaResourceTests(const Context& context, const BdaTestAccess& access) {
alignas(64) std::array<std::uint32_t, 16> guest{};
guest[0] = 123;
@ -694,6 +722,7 @@ void RunBdaResourceTests(const Context& context, const BdaTestAccess& access) {
importedHeapMirrorTests(context, access);
importWatchTests();
importedFreshTests(context);
gpuMappingTests(context);
Require(AddressCopyOverflow({{0x1000, 0x3000, 0x2000, "uncommitted pages"}}, 0x2000).empty(), "copies within the limit were refused");
const auto copies = AddressCopyOverflow({{0x1000, 0x2000, 0x1000, "not mirrored"}, {0x10000, 0x30000, 0x18000, "uncommitted pages"}}, 0x2000);
Require(!copies.empty() && copies.find("0x10000+0x20000 (0.1 MiB committed, uncommitted pages)") < copies.find("0x1000+0x1000"), "the copy limit does not name the largest copy first");

View File

@ -177,8 +177,8 @@ int main() {
}
{
GuestAllocations::Mutation mutation;
mutation.Add(SurfaceStorage.data(), SurfaceStorage.size(), true, true);
mutation.Add(Cmask.data(), Cmask.size(), true, true);
mutation.Add(SurfaceStorage.data(), SurfaceStorage.size(), true, true, true);
mutation.Add(Cmask.data(), Cmask.size(), true, true, true);
}
run(*device, ColorTileMode::Linear, "linear");
run(*device, ColorTileMode::RenderTarget, "SW_64KB_R_X");

View File

@ -34,8 +34,8 @@ void RunGuestLeaseWaitTests() {
const auto address = reinterpret_cast<std::uintptr_t>(memory.data());
{
GuestAllocations::Mutation mutation;
mutation.Add(memory.data(), 64, true, true);
mutation.Add(memory.data() + 64, 64, true, true);
mutation.Add(memory.data(), 64, true, true, true);
mutation.Add(memory.data() + 64, 64, true, true, true);
}
auto lease = GuestAllocations::GuestAllocationsAcquire_nid_postfix();
std::erase_if(lease, [&](const auto& range) { return range->address != address; });
@ -83,7 +83,7 @@ void RunGuestAllocationTests() {
reject([&] { GuestHeap::GuestHeapReallocate_nid_postfix(pointer, 64); });
GuestAllocations::Mutation mutation;
bool applied = false;
reject([&] { mutation.Protect(pointer, 32, true, false, [&] { applied = true; }); });
reject([&] { mutation.Protect(pointer, 32, true, false, true, [&] { applied = true; }); });
Require(!applied, "pinned guest protection changed");
}
pointer = GuestHeap::GuestHeapReallocate_nid_postfix(pointer, 64);
@ -102,9 +102,9 @@ void RunGuestAllocationTests() {
std::array<std::byte, 128> mapping{};
{
GuestAllocations::Mutation mutation;
mutation.Add(mapping.data(), mapping.size(), true, true);
mutation.Add(mapping.data(), mapping.size(), true, true, true);
reject([&] { mutation.RequireAvailable(mapping.data() + 32, 16); });
mutation.Protect(mapping.data() + 32, 32, true, false, [] {});
mutation.Protect(mapping.data() + 32, 32, true, false, true, [] {});
}
{
const auto lease = GuestAllocations::GuestAllocationsAcquire_nid_postfix();
@ -120,7 +120,7 @@ void RunGuestAllocationTests() {
applied = true;
});
Require(applied, "partial unmap callback was not called");
reject([&] { mutation.Protect(mapping.data(), mapping.size(), true, true, [] {}); });
reject([&] { mutation.Protect(mapping.data(), mapping.size(), true, true, true, [] {}); });
mutation.Unmap(mapping.data(), 32, [&](const void*, std::size_t, const void* allocation, bool last) {
Require(allocation == mapping.data() && !last, "first fragment released remaining mapping");
});
@ -144,10 +144,11 @@ void RunGuestAllocationTests() {
imageRangeCount = lease.size();
const auto found = std::find_if(lease.begin(), lease.end(), [&](const auto& range) { return imageAddress >= range->address && imageAddress - range->address < range->bytes; });
Require(found != lease.end() && (*found)->writable && !(*found)->releasable, "main image registration is missing or releasable");
Require(std::none_of(lease.begin(), lease.end(), [](const auto& range) { return !range->releasable && range->gpu; }), "main image registered as GPU-mapped");
allocationAddress = (*found)->allocationAddress;
GuestAllocations::Mutation mutation;
bool applied = false;
reject([&] { mutation.Protect(&imageProbe, 1, true, false, [&] { applied = true; }); });
reject([&] { mutation.Protect(&imageProbe, 1, true, false, true, [&] { applied = true; }); });
Require(!applied, "pinned image protection changed");
}
{
@ -157,12 +158,12 @@ void RunGuestAllocationTests() {
bool applied = false;
reject([&] { mutation.Unmap(&imageProbe, 1, [&](const void*, std::size_t, const void*, bool) { applied = true; }); });
Require(!applied, "image memory was unmapped");
reject([&] { mutation.Protect(&imageProbe, 1, true, false, [] { throw std::runtime_error("host protection failure"); }); });
reject([&] { mutation.Protect(&imageProbe, 1, true, false, true, [] { throw std::runtime_error("host protection failure"); }); });
}
Require(GuestAllocations::GuestAllocationsAcquire_nid_postfix().size() == imageRangeCount, "failed image protection changed registry ranges");
{
GuestAllocations::Mutation mutation;
mutation.Protect(&imageProbe, 1, true, true, [] {});
mutation.Protect(&imageProbe, 1, true, true, true, [] {});
bool applied = false;
reject([&] { mutation.Unmap(&imageProbe, 1, [&](const void*, std::size_t, const void*, bool) { applied = true; }); });
Require(!applied, "split image memory became releasable");

View File

@ -288,7 +288,7 @@ int main() {
auto* record = static_cast<std::uint32_t*>(::operator new(recordBlockBytes, std::align_val_t{65536}));
{
GuestAllocations::Mutation mutation;
mutation.Add(record, recordBlockBytes, true, true);
mutation.Add(record, recordBlockBytes, true, true, true);
}
const auto drawIndirect = [&](std::uint32_t count, std::uint32_t instances, std::uint32_t first) {
const std::array<std::uint32_t, 5> words{count, instances, first, 0u, 0u};

View File

@ -742,7 +742,7 @@ void storeRunTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
const auto* import = HostImportFor(context, address, bytes);
if (import == nullptr) {
@ -816,7 +816,7 @@ void remappedImportTests(const Device& device) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
if (HostImportFor(context, address, bytes) == nullptr) {
std::cout << "host import of the remap test block refused: remapped imports not tested\n";
@ -827,7 +827,7 @@ void remappedImportTests(const Device& device) {
{
GuestAllocations::Mutation mutation;
mutation.Remove(block);
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
Require(HostImportFor(context, address, bytes) != nullptr, "the remapped range was not imported again");
const auto second = HostImportSerial(context, address, bytes, true);
@ -861,7 +861,7 @@ void movedMetadataTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -954,7 +954,7 @@ void viewPastLastMipTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -1038,7 +1038,7 @@ void resourceReadTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
if (HostImportFor(context, address, bytes) == nullptr) {
std::cout << "host import of the test block refused: resource read notes not tested\n";
@ -1146,7 +1146,7 @@ void misalignedRegionTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -1234,7 +1234,7 @@ void misalignedSnapshotTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -1309,7 +1309,7 @@ void drawSnapshotReuseTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -1461,7 +1461,7 @@ void drawInputReuseTests(const Device& device, Recorder& recorder) {
alignas(64) static std::byte other[64];
{
GuestAllocations::Mutation mutation;
mutation.Add(other, sizeof(other), true, true);
mutation.Add(other, sizeof(other), true, true, true);
}
{
GuestAllocations::Mutation mutation;
@ -1506,7 +1506,7 @@ void drawInputInPlaceTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -1578,7 +1578,7 @@ void unitShadowTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -1732,7 +1732,7 @@ void unitShadowTests(const Device& device, Recorder& recorder) {
{
GuestAllocations::Mutation mutation;
mutation.Remove(block);
mutation.Add(block, static_cast<std::size_t>(5 * unit), true, true);
mutation.Add(block, static_cast<std::size_t>(5 * unit), true, true, true);
}
Require(HostImportFor(context, address, bytes) == nullptr, "a shrunk registration still imports the old range");
Require(!AnyShadowedOverlaps(address, bytes), "the retired import's shadow survived");
@ -1775,7 +1775,7 @@ void storageRefreshTests(const Device& device, Recorder& recorder, bool watched)
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -1962,7 +1962,7 @@ void hostImportUnmapTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
const HostImport* imported = nullptr;
{
@ -2036,7 +2036,7 @@ void importWatchTests(const Device& device) {
};
const auto registerRange = [](void* block, std::size_t bytes) {
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
};
const auto unregisterRange = [](void* block) {
GuestAllocations::Mutation mutation;
@ -2140,7 +2140,7 @@ void staleGenerationTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -2282,7 +2282,7 @@ void writeBackPaddingTests(const Device& device, Recorder& recorder, bool watche
const auto address = blockAddress + test.offset;
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -2385,7 +2385,7 @@ void unchangedCpuStampTests(const Device& device, Recorder& recorder) {
const auto address = blockAddress + offset;
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -2509,7 +2509,7 @@ void importWindowTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -2897,7 +2897,7 @@ void pendingKeyStoreTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -2964,7 +2964,7 @@ void sampleDumpTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -3036,7 +3036,7 @@ void metadataPassTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -3563,7 +3563,7 @@ void keysFillTests(const Device& device, Recorder& recorder) {
const auto keysAddress = address + surfaceBytes;
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;
@ -3673,7 +3673,7 @@ void denormalClearTests(const Device& device, Recorder& recorder) {
const auto address = reinterpret_cast<std::uint64_t>(block);
{
GuestAllocations::Mutation mutation;
mutation.Add(block, bytes, true, true);
mutation.Add(block, bytes, true, true, true);
}
struct Unregister {
const Context& context;

View File

@ -52,7 +52,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "BDA span reads: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -152,7 +152,7 @@ public:
#endif
Require(block != nullptr, "bvh64 intersect ray: cannot allocate the guest block");
std::memset(block, 0, BlockBytes);
GuestAllocations::Mutation().Add(block, BlockBytes, true, false);
GuestAllocations::Mutation().Add(block, BlockBytes, true, false, true);
}
~GuestBlock() {

View File

@ -35,7 +35,7 @@ public:
#endif
Require(block != nullptr, "constant store: cannot allocate the guest block");
Clear();
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -104,7 +104,7 @@ public:
#endif
Require(block != nullptr, "DCC keys first write: cannot allocate the guest block");
Require(!watched || AgcDriver::GuestMemory::Watched(AddressOf(block), BlockBytes), "DCC keys first write: the watched guest block is not write-watched");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -221,7 +221,7 @@ public:
std::copy(instance.begin(), instance.end(), BufferTable.begin() + 4);
records = static_cast<std::uint32_t*>(::operator new(RecordBytes, std::align_val_t{65536}));
GuestAllocations::Mutation mutation;
mutation.Add(records, RecordBytes, true, true);
mutation.Add(records, RecordBytes, true, true, true);
}
std::vector<std::uint32_t> UserData(std::uint32_t baseVertex, std::uint32_t startInstance) const {

View File

@ -135,7 +135,7 @@ struct Block {
Require(data != nullptr, "cannot allocate the color target");
Require(!watched || AgcDriver::GuestMemory::Watched(Address(), bytes), "the color target is not write-watched");
GuestAllocations::Mutation mutation;
mutation.Add(data, bytes, true, true);
mutation.Add(data, bytes, true, true, true);
}
~Block() {
AgcDriver::Graphics::StorageTexture::FlushPending(Address(), bytes, nullptr, "test release");

View File

@ -67,7 +67,7 @@ public:
#endif
Require(block != nullptr, "fill cover test: cannot allocate the guest block");
std::memset(block, 0, BlockBytes);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -86,7 +86,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "flat cache bits: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -482,7 +482,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "flat d16 loads: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, false);
GuestAllocations::Mutation().Add(block, BlockBytes, true, false, true);
}
~GuestBlock() {

View File

@ -94,7 +94,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "flat loads: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, false);
GuestAllocations::Mutation().Add(block, BlockBytes, true, false, true);
}
~GuestBlock() {

View File

@ -93,7 +93,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "flat loads x2 x3: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, false);
GuestAllocations::Mutation().Add(block, BlockBytes, true, false, true);
}
~GuestBlock() {

View File

@ -93,7 +93,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "flat loads x4: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, false);
GuestAllocations::Mutation().Add(block, BlockBytes, true, false, true);
}
~GuestBlock() {

View File

@ -47,7 +47,7 @@ public:
#endif
Require(block != nullptr, "flat store: cannot allocate the guest block");
Clear();
GuestAllocations::Mutation().Add(block, BlockBytes, true, writable);
GuestAllocations::Mutation().Add(block, BlockBytes, true, writable, true);
}
~GuestBlock() {

View File

@ -89,7 +89,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "global atomic csub: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -292,7 +292,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "global atomics: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, writable);
GuestAllocations::Mutation().Add(block, BlockBytes, true, writable, true);
}
~GuestBlock() {

View File

@ -256,7 +256,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "global atomics: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, writable);
GuestAllocations::Mutation().Add(block, BlockBytes, true, writable, true);
}
~GuestBlock() {

View File

@ -585,7 +585,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "global float atomics: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, writable);
GuestAllocations::Mutation().Add(block, BlockBytes, true, writable, true);
}
~GuestBlock() {

View File

@ -67,7 +67,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "global atomics: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, writable);
GuestAllocations::Mutation().Add(block, BlockBytes, true, writable, true);
}
~GuestBlock() {

View File

@ -94,7 +94,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "global loads: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, false);
GuestAllocations::Mutation().Add(block, BlockBytes, true, false, true);
}
~GuestBlock() {

View File

@ -93,7 +93,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "global loads x2 x3: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, false);
GuestAllocations::Mutation().Add(block, BlockBytes, true, false, true);
}
~GuestBlock() {

View File

@ -93,7 +93,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "global loads x4: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, false);
GuestAllocations::Mutation().Add(block, BlockBytes, true, false, true);
}
~GuestBlock() {

View File

@ -61,7 +61,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "global store addtid: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -56,7 +56,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "global vcc base: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -35,7 +35,7 @@ public:
#endif
Require(block != nullptr, "host import teardown: cannot allocate the guest block");
std::memset(block, 0, BlockBytes);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -166,7 +166,7 @@ int main() {
try {
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
GuestAllocations::Mutation().Add(Texels.data(), Bytes, true, true);
GuestAllocations::Mutation().Add(Texels.data(), Bytes, true, true, true);
constexpr Format r8{R8UInt, 1u, 1u};
constexpr Format r16{R16UInt, 2u, 1u};
constexpr Format rg8{Rg8UInt, 1u, 2u};

View File

@ -103,7 +103,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "image converted unorm: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -170,7 +170,7 @@ int main() {
try {
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
GuestAllocations::Mutation().Add(Texels.data(), Bytes, true, true);
GuestAllocations::Mutation().Add(Texels.data(), Bytes, true, true, true);
constexpr Format r8{R8UInt, 1u, 1u};
constexpr Format r16{R16UInt, 2u, 1u};
constexpr Format rg8{Rg8UInt, 1u, 2u};

View File

@ -82,7 +82,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "image sRGB load: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -50,7 +50,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "image store line through plane: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -58,7 +58,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "image store packed: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -76,7 +76,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "image store sint: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -36,7 +36,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "masked global store: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() { GuestAllocations::Mutation().Remove(block); }
GuestBlock(const GuestBlock&) = delete;

View File

@ -128,7 +128,7 @@ struct Block {
Require(data != nullptr, "cannot allocate the color target");
Require(!watched || AgcDriver::GuestMemory::Watched(Address(), bytes), "the color target is not write-watched");
GuestAllocations::Mutation mutation;
mutation.Add(data, bytes, true, true);
mutation.Add(data, bytes, true, true, true);
}
~Block() {
AgcDriver::Graphics::StorageTexture::FlushPending(Address(), bytes, nullptr, "test release");

View File

@ -94,7 +94,7 @@ int main() {
std::fill(texels, texels + BlockBytes / 4u, Untouched);
{
GuestAllocations::Mutation mutation;
mutation.Add(texels, BlockBytes, true, true);
mutation.Add(texels, BlockBytes, true, true, true);
}
const auto surface = AddressOf(texels);
const auto chainBytes = GuestBytes(texels, 1u);

View File

@ -101,7 +101,7 @@ struct Block {
Require(data != nullptr, "cannot allocate the color target");
Require(!watched || AgcDriver::GuestMemory::Watched(Address(), bytes), "the color target is not write-watched");
GuestAllocations::Mutation mutation;
mutation.Add(data, bytes, true, true);
mutation.Add(data, bytes, true, true, true);
}
~Block() {
AgcDriver::Graphics::StorageTexture::FlushPending(Address(), bytes, nullptr, "test release");

View File

@ -58,7 +58,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(GuestBytes, 2 * GuestBytes));
#endif
Require(block != nullptr, "pixel interlock: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, GuestBytes, true, true);
GuestAllocations::Mutation().Add(block, GuestBytes, true, true, true);
}
~GuestCounters() {

View File

@ -107,7 +107,7 @@ struct Block {
Require(data != nullptr, "cannot allocate the color target");
Require(!watched || AgcDriver::GuestMemory::Watched(Address(), bytes), "the color target is not write-watched");
GuestAllocations::Mutation mutation;
mutation.Add(data, bytes, true, true);
mutation.Add(data, bytes, true, true, true);
}
~Block() {
AgcDriver::Graphics::StorageTexture::FlushPending(Address(), bytes, nullptr, "test release");

View File

@ -163,7 +163,7 @@ public:
#endif
Require(memory != nullptr, "cannot allocate the texture block");
std::memset(memory, 0x5a, BlockBytes);
GuestAllocations::Mutation().Add(memory, BlockBytes, true, true);
GuestAllocations::Mutation().Add(memory, BlockBytes, true, true, true);
}
~Block() {

View File

@ -159,7 +159,7 @@ public:
block = static_cast<std::uint8_t*>(std::aligned_alloc(BlockBytes, BlockBytes));
#endif
Require(block != nullptr, "scalar wide loads: cannot allocate the guest block");
GuestAllocations::Mutation().Add(block, BlockBytes, true, false);
GuestAllocations::Mutation().Add(block, BlockBytes, true, false, true);
}
~GuestBlock() {

View File

@ -127,7 +127,7 @@ struct Block {
Require(data != nullptr, "cannot allocate the color target");
Require(!watched || AgcDriver::GuestMemory::Watched(Address(), bytes), "the color target is not write-watched");
GuestAllocations::Mutation mutation;
mutation.Add(data, bytes, true, true);
mutation.Add(data, bytes, true, true, true);
}
~Block() {
AgcDriver::Graphics::StorageTexture::FlushPending(Address(), bytes, nullptr, "test release");

View File

@ -100,7 +100,7 @@ int main() {
const auto blockBytes = static_cast<std::size_t>(SurfaceBytes + (MaxSurfaces - 1u) * Stride);
{
GuestAllocations::Mutation mutation;
mutation.Add(texels, blockBytes, true, true);
mutation.Add(texels, blockBytes, true, true, true);
}
const auto release = [&] {
AgcDriver::Graphics::ClearCachedTextures(device->Device());

View File

@ -101,7 +101,7 @@ struct Block {
Require(data != nullptr, "cannot allocate the color target");
Require(!watched || AgcDriver::GuestMemory::Watched(Address(), bytes), "the color target is not write-watched");
GuestAllocations::Mutation mutation;
mutation.Add(data, bytes, true, true);
mutation.Add(data, bytes, true, true, true);
}
~Block() {
AgcDriver::Graphics::StorageTexture::FlushPending(Address(), bytes, nullptr, "test release");

View File

@ -37,7 +37,7 @@ public:
#endif
Require(block != nullptr, "unimported label order: cannot allocate the guest block");
std::memset(block, 0, BlockBytes);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true);
GuestAllocations::Mutation().Add(block, BlockBytes, true, true, true);
}
~GuestBlock() {

View File

@ -95,7 +95,7 @@ int main() {
std::fill(texels, texels + SurfaceBytes / 4u, Untouched);
{
GuestAllocations::Mutation mutation;
mutation.Add(texels, SurfaceBytes, true, true);
mutation.Add(texels, SurfaceBytes, true, true, true);
}
Store(*device, texels, 3u, StorePastCode);
Check(texels, false, "store through level 3");

View File

@ -18,6 +18,7 @@ struct Range {
std::uint64_t allocationAddress;
std::size_t allocationBytes;
bool releasable = true;
bool gpu = false;
};
using Lease = std::vector<std::shared_ptr<const Range>>;
@ -30,14 +31,14 @@ void GuestAllocationsRegisterImage_nid_postfix(void* mutation, const void* image
void GuestAllocationsUnregisterImage_nid_postfix(void* mutation, const void* image);
#endif
void GuestAllocationsEnd_nid_postfix(void* mutation) noexcept;
void GuestAllocationsAdd_nid_postfix(void* mutation, void* pointer, std::size_t bytes, bool readable, bool writable);
void GuestAllocationsAdd_nid_postfix(void* mutation, void* pointer, std::size_t bytes, bool readable, bool writable, bool gpu);
void GuestAllocationsRequireUnpinned_nid_postfix(void* mutation, const void* pointer, std::size_t bytes);
void GuestAllocationsRequireAvailable_nid_postfix(void* mutation, const void* pointer, std::size_t bytes);
bool GuestAllocationsCovers_nid_postfix(void* mutation, const void* pointer, std::size_t bytes);
bool GuestAllocationsOverlaps_nid_postfix(void* mutation, const void* pointer, std::size_t bytes);
Range GuestAllocationsFind_nid_postfix(void* mutation, const void* pointer);
void GuestAllocationsRemove_nid_postfix(void* mutation, const void* pointer);
void GuestAllocationsProtect_nid_postfix(void* mutation, const void* pointer, std::size_t bytes, bool readable, bool writable, const std::function<void()>& apply);
void GuestAllocationsProtect_nid_postfix(void* mutation, const void* pointer, std::size_t bytes, bool readable, bool writable, bool gpu, const std::function<void()>& apply);
void GuestAllocationsUnmap_nid_postfix(void* mutation, const void* pointer, std::size_t bytes, const std::function<void(const void*, std::size_t, const void*, bool)>& apply);
Lease GuestAllocationsAcquire_nid_postfix();
Lease GuestAllocationsAcquireAll_nid_postfix();
@ -66,7 +67,7 @@ public:
void RegisterImage(const void* image) { GuestAllocationsRegisterImage_nid_postfix(handle, image); }
void UnregisterImage(const void* image) { GuestAllocationsUnregisterImage_nid_postfix(handle, image); }
#endif
void Add(void* pointer, std::size_t bytes, bool readable, bool writable) { GuestAllocationsAdd_nid_postfix(handle, pointer, bytes, readable, writable); }
void Add(void* pointer, std::size_t bytes, bool readable, bool writable, bool gpu) { GuestAllocationsAdd_nid_postfix(handle, pointer, bytes, readable, writable, gpu); }
void RequireUnpinned(const void* pointer, std::size_t bytes) const { GuestAllocationsRequireUnpinned_nid_postfix(handle, pointer, bytes); }
void RequireAvailable(const void* pointer, std::size_t bytes) const { GuestAllocationsRequireAvailable_nid_postfix(handle, pointer, bytes); }
bool Covers(const void* pointer, std::size_t bytes) const { return GuestAllocationsCovers_nid_postfix(handle, pointer, bytes); }
@ -74,7 +75,7 @@ public:
Range Find(const void* pointer) const { return GuestAllocationsFind_nid_postfix(handle, pointer); }
void Remove(const void* pointer) { GuestAllocationsRemove_nid_postfix(handle, pointer); }
void Unmap(const void* pointer, std::size_t bytes, const std::function<void(const void*, std::size_t, const void*, bool)>& apply) { GuestAllocationsUnmap_nid_postfix(handle, pointer, bytes, apply); }
void Protect(const void* pointer, std::size_t bytes, bool readable, bool writable, const std::function<void()>& apply) { GuestAllocationsProtect_nid_postfix(handle, pointer, bytes, readable, writable, apply); }
void Protect(const void* pointer, std::size_t bytes, bool readable, bool writable, bool gpu, const std::function<void()>& apply) { GuestAllocationsProtect_nid_postfix(handle, pointer, bytes, readable, writable, gpu, apply); }
private:
void* handle;

View File

@ -242,7 +242,7 @@ void GuestAllocationsRegisterMainImage_nid_postfix(void*) {
}
#endif
void GuestAllocationsAdd_nid_postfix(void* mutation, void* pointer, std::size_t bytes, bool readable, bool writable) {
void GuestAllocationsAdd_nid_postfix(void* mutation, void* pointer, std::size_t bytes, bool readable, bool writable, bool gpu) {
recordChange(mutation, pointer, bytes);
const auto address = reinterpret_cast<std::uintptr_t>(pointer);
require(address != 0 && bytes <= std::numeric_limits<std::uint64_t>::max() - address, "invalid guest allocation range");
@ -254,7 +254,7 @@ void GuestAllocationsAdd_nid_postfix(void* mutation, void* pointer, std::size_t
const auto& previous = *std::prev(next)->second;
require(previous.address + previous.bytes <= address, "overlapping guest allocation");
}
ranges.emplace(address, std::make_shared<const Range>(Range{address, bytes, readable, writable, address, bytes}));
ranges.emplace(address, std::make_shared<const Range>(Range{address, bytes, readable, writable, address, bytes, true, gpu}));
}
[[noreturn]] void PinnedFailure(std::uintptr_t address, std::size_t bytes, const char* why) {
@ -355,12 +355,12 @@ Range GuestAllocationsFind_nid_postfix(void*, const void* pointer) {
if (exact != registry().ranges.end() && exact->second->allocationAddress == address && exact->second->allocationBytes == exact->second->bytes) {
const auto& range = *exact->second;
require(range.releasable, "guest image memory is not a releasable allocation");
return {address, range.allocationBytes, range.readable, range.writable, address, range.allocationBytes, range.releasable};
return {address, range.allocationBytes, range.readable, range.writable, address, range.allocationBytes, range.releasable, range.gpu};
}
for (const auto& [base, range] : registry().ranges) {
if (range->allocationAddress == address) {
require(range->releasable, "guest image memory is not a releasable allocation");
return {address, range->allocationBytes, range->readable, range->writable, address, range->allocationBytes, range->releasable};
return {address, range->allocationBytes, range->readable, range->writable, address, range->allocationBytes, range->releasable, range->gpu};
}
}
throw std::runtime_error("guest allocation is not registered");
@ -381,7 +381,7 @@ void GuestAllocationsRemove_nid_postfix(void* mutation, const void* pointer) {
namespace {
std::map<std::uint64_t, std::shared_ptr<const Range>> replaceRange(const void* pointer, std::size_t bytes, bool remove, bool readable, bool writable) {
std::map<std::uint64_t, std::shared_ptr<const Range>> replaceRange(const void* pointer, std::size_t bytes, bool remove, bool readable, bool writable, bool gpu) {
const auto address = reinterpret_cast<std::uintptr_t>(pointer);
require(bytes != 0 && bytes <= std::numeric_limits<std::uint64_t>::max() - address, "invalid guest protection or unmap range");
require(!writable || readable, "writable guest allocation must be readable");
@ -395,12 +395,12 @@ std::map<std::uint64_t, std::shared_ptr<const Range>> replaceRange(const void* p
if (base >= end) break;
require(base <= cursor, "guest protection or unmap range has a hole");
replacement.erase(base);
const auto insert = [&](std::uint64_t first, std::uint64_t last, bool canRead, bool canWrite) {
if (first < last) replacement.emplace(first, std::make_shared<const Range>(Range{first, static_cast<std::size_t>(last - first), canRead, canWrite, range.allocationAddress, range.allocationBytes, range.releasable}));
const auto insert = [&](std::uint64_t first, std::uint64_t last, bool canRead, bool canWrite, bool gpuMapped) {
if (first < last) replacement.emplace(first, std::make_shared<const Range>(Range{first, static_cast<std::size_t>(last - first), canRead, canWrite, range.allocationAddress, range.allocationBytes, range.releasable, gpuMapped}));
};
insert(base, std::max(base, address), range.readable, range.writable);
if (!remove) insert(std::max(base, address), std::min(finish, end), readable, writable);
insert(std::min(finish, end), finish, range.readable, range.writable);
insert(base, std::max(base, address), range.readable, range.writable, range.gpu);
if (!remove) insert(std::max(base, address), std::min(finish, end), readable, writable, gpu);
insert(std::min(finish, end), finish, range.readable, range.writable, range.gpu);
cursor = std::min(finish, end);
}
require(cursor == end, "guest protection or unmap range is not registered");
@ -409,10 +409,10 @@ std::map<std::uint64_t, std::shared_ptr<const Range>> replaceRange(const void* p
}
void GuestAllocationsProtect_nid_postfix(void* mutation, const void* pointer, std::size_t bytes, bool readable, bool writable, const std::function<void()>& apply) {
void GuestAllocationsProtect_nid_postfix(void* mutation, const void* pointer, std::size_t bytes, bool readable, bool writable, bool gpu, const std::function<void()>& apply) {
GuestAllocationsRequireUnpinned_nid_postfix(mutation, pointer, bytes);
recordChange(mutation, pointer, bytes);
auto replacement = replaceRange(pointer, bytes, false, readable, writable);
auto replacement = replaceRange(pointer, bytes, false, readable, writable, gpu);
apply();
registry().ranges.swap(replacement);
}
@ -438,7 +438,7 @@ void GuestAllocationsUnmap_nid_postfix(void* mutation, const void* pointer, std:
const auto range = *containing;
require(range.releasable, "guest image memory cannot be unmapped");
const auto pieceEnd = std::min<std::uint64_t>(end, range.allocationAddress + range.allocationBytes);
auto replacement = replaceRange(reinterpret_cast<const void*>(cursor), pieceEnd - cursor, true, false, false);
auto replacement = replaceRange(reinterpret_cast<const void*>(cursor), pieceEnd - cursor, true, false, false, false);
bool last = true;
for (const auto& [base, entry] : replacement) {
if (entry->allocationAddress == range.allocationAddress) last = false;

View File

@ -210,7 +210,7 @@ void* allocate(GuestAllocations::Mutation& mutation, std::size_t alignment, std:
reinterpret_cast<void**>(pointer)[-2] = raw;
reinterpret_cast<std::size_t*>(pointer)[-1] = blockBytes;
try {
mutation.Add(pointer, bytes, true, true);
mutation.Add(pointer, bytes, true, true, true);
} catch (...) {
rawFree(raw, blockBytes);
throw;

View File

@ -142,6 +142,7 @@ static int mprotect(void* addr, size_t len, int prot) {
namespace {
constexpr int GuestMapFixedFlag = 0x10;
constexpr int GuestProtGpuReadWrite = 0x30;
#if defined(__linux__)
void* MapAtOrAbove(std::uintptr_t start, size_t len, int prot, size_t alignment) {
@ -494,7 +495,7 @@ bool RemapFixedIntoRegistered(GuestAllocations::Mutation& mutation, void* addr,
ValidateRange(addr, len, PS5_PAGE_SIZE);
Trace("remap fixed %p+0x%zx prot=0x%x phys=0x%llx", addr, len, prot, static_cast<long long>(physStart));
const auto nativeProtection = LinuxProtFromSce(prot);
mutation.Protect(addr, len, (prot & 3) != 0, (prot & 2) != 0, [&] {
mutation.Protect(addr, len, (prot & 3) != 0, (prot & 2) != 0, (prot & GuestProtGpuReadWrite) != 0, [&] {
const auto address = reinterpret_cast<std::uintptr_t>(addr);
std::lock_guard lock(g_directLock);
if (physStart >= 0) {
@ -742,7 +743,7 @@ int DoMapDirect(void** addr, size_t len, int prot, int flags, int64_t physStart,
void* mapped = MapAligned(*addr, len, PROT_NONE, flags, alignment);
try {
AddMapping(reinterpret_cast<std::uintptr_t>(mapped), len, static_cast<std::uint64_t>(physStart), LinuxProtFromSce(prot));
mutation.Add(mapped, len, (prot & 3) != 0, (prot & 2) != 0);
mutation.Add(mapped, len, (prot & 3) != 0, (prot & 2) != 0, (prot & GuestProtGpuReadWrite) != 0);
} catch (...) {
EraseMappings(reinterpret_cast<std::uintptr_t>(mapped), reinterpret_cast<std::uintptr_t>(mapped) + len);
Unmap(mapped, len);
@ -768,7 +769,7 @@ int DoMapAnon(void** addr, size_t len, int prot, int flags, size_t alignment) {
ReplaceFixedOverlap(mutation, *addr, len, flags);
void* mapped = MapAligned(*addr, len, LinuxProtFromSce(prot), flags, alignment);
try {
mutation.Add(mapped, len, (prot & 3) != 0, (prot & 2) != 0);
mutation.Add(mapped, len, (prot & 3) != 0, (prot & 2) != 0, (prot & GuestProtGpuReadWrite) != 0);
} catch (...) {
Unmap(mapped, len);
throw;
@ -821,7 +822,7 @@ int DoMprotect(const void* addr, size_t len, int prot) {
#else
mutation.RegisterMainImage();
#endif
mutation.Protect(pointer, bytes, (prot & 3) != 0, (prot & 2) != 0, [&] {
mutation.Protect(pointer, bytes, (prot & 3) != 0, (prot & 2) != 0, (prot & GuestProtGpuReadWrite) != 0, [&] {
if (mprotect(const_cast<void*>(pointer), bytes, nativeProtection) != 0) throw std::system_error(errno, std::generic_category(), "mprotect failed");
});
RecordProtection(pointer, bytes, prot);
@ -881,7 +882,7 @@ int DoReserveVirtual(void** addr, size_t len, int flags, size_t alignment) {
constexpr int GuestMapNoCoalesce = 0x400000;
void* mapped = MapAligned(fixed ? *addr : nullptr, len, PROT_NONE, fixed ? GuestMapFixedFlag | (flags & GuestMapNoCoalesce) : 0, alignment);
try {
mutation.Add(mapped, len, false, false);
mutation.Add(mapped, len, false, false, false);
} catch (...) {
Unmap(mapped, len);
throw;

View File

@ -6,6 +6,7 @@
#include "prx/libkernel/File/include/FileFlags.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include "prx/libkernel/DirectMemory/DirectMemory.hpp"
#include <algorithm>
#include <array>
#include <atomic>
#include "SceTypes.hpp"
@ -423,6 +424,48 @@ static void CheckDirectMemoryGpuProtBits() {
Require(sceKernelReleaseDirectMemory(phys, page) == 0);
}
static bool GpuMapped(const void* pointer) {
GuestAllocations::Mutation mutation;
return mutation.Find(pointer).gpu;
}
static void CheckGpuAccessFollowsProtection() {
constexpr std::size_t page = 0x4000;
std::int64_t phys = 0;
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page * 2, 0, 0, &phys) == 0);
void* cpu = nullptr;
Require(sceKernelMapDirectMemory(&cpu, page, 3, 0, phys, 0) == 0);
Require(!GpuMapped(cpu));
void* gpu = nullptr;
Require(sceKernelMapDirectMemory(&gpu, page, 0x32, 0, phys + static_cast<std::int64_t>(page), 0) == 0);
Require(GpuMapped(gpu));
Require(sceKernelMprotect(cpu, page, 0x13) == 0);
Require(GpuMapped(cpu));
Require(sceKernelMprotect(gpu, page, 3) == 0);
Require(!GpuMapped(gpu));
static int imageProbe = 0;
const auto probe = reinterpret_cast<std::uintptr_t>(&imageProbe);
{
GuestAllocations::Mutation mutation;
mutation.RegisterMainImage();
}
{
const auto lease = GuestAllocations::GuestAllocationsAcquire_nid_postfix();
const auto image = std::find_if(lease.begin(), lease.end(), [&](const auto& range) { return probe >= range->address && probe - range->address < range->bytes; });
Require(image != lease.end() && !(*image)->releasable && !(*image)->gpu);
}
Require(sceKernelMunmap(cpu, page) == 0);
Require(sceKernelMunmap(gpu, page) == 0);
Require(sceKernelReleaseDirectMemory(phys, page * 2) == 0);
void* flexible = nullptr;
Require(sceKernelMapNamedFlexibleMemory(&flexible, page, 3, 0, "cpu") == 0);
Require(!GpuMapped(flexible));
Require(sceKernelMunmap(flexible, page) == 0);
Require(sceKernelMapNamedFlexibleMemory(&flexible, page, 0x33, 0, "gpu") == 0);
Require(GpuMapped(flexible));
Require(sceKernelMunmap(flexible, page) == 0);
}
static void CheckFixedVirtualReservation() {
constexpr std::size_t page = 0x4000;
void* probe = nullptr;
@ -603,7 +646,7 @@ static void CheckGuestModuleImageProtection() {
Require(!RegisteredGuestRange(target, page));
{
GuestAllocations::Mutation mutation;
mutation.Add(target, page, true, true);
mutation.Add(target, page, true, true, false);
mutation.Remove(target);
}
@ -1406,6 +1449,7 @@ int main() {
CheckReleaseDirectMemoryRejectsInvalidRanges();
CheckFixedMappingReplacesPartialOverlap();
CheckDirectMemoryGpuProtBits();
CheckGpuAccessFollowsProtection();
CheckFixedVirtualReservation();
CheckReservedRangeIsNotCommitted();
CheckNoOverwriteRefusesLiveMapping();