Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct ImportProbe {
std::uint32_t writtenAfterSettle = 0;
std::uint32_t writtenByCpu = 0;
std::uint32_t writtenAfterFill = 0;
std::uint32_t staleAfterFill = 0;
};

ImportProbe ProbeImportWriteProtection(const Context& context);
Expand Down
159 changes: 102 additions & 57 deletions core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions core/libs/prx/libc/include/GuestArena.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ namespace GuestArena {
#ifndef _WIN32
using SharedBackingResolver = bool (*)(std::uintptr_t address, std::size_t bytes, int* file, std::uint64_t* offset);
using SharedBackingWriter = bool (*)(std::uintptr_t address, const void* source, std::size_t bytes);
struct SharedBackingRun {
std::uint64_t offset;
std::size_t bytes;
};
using SharedBackingRunsResolver = bool (*)(std::uintptr_t address, std::size_t bytes, int* file, SharedBackingRun* runs, std::size_t capacity, std::size_t* count);
#endif

extern "C" {
Expand Down Expand Up @@ -52,6 +57,8 @@ void GuestArenaSetSharedBacking_nid_postfix(SharedBackingResolver resolver);
void GuestArenaSetSharedBackingWriter_nid_no_patch(SharedBackingWriter writer);
bool GuestArenaWriteSharedBacking_nid_no_patch(std::uintptr_t address, const void* source, std::size_t bytes);
bool GuestArenaSharedBacking_nid_postfix(std::uintptr_t address, std::size_t bytes, int* file, std::uint64_t* offset);
void GuestArenaSetSharedBackingRuns_nid_postfix(SharedBackingRunsResolver resolver);
bool GuestArenaSharedBackingRuns_nid_postfix(std::uintptr_t address, std::size_t bytes, int* file, SharedBackingRun* runs, std::size_t capacity, std::size_t* count);
#endif
bool GuestArenaBeginHostWrite_nid_postfix(void* pointer, std::size_t bytes);
void GuestArenaEndHostWrite_nid_postfix(void* pointer, std::size_t bytes);
Expand Down
10 changes: 10 additions & 0 deletions core/libs/prx/libc/src/GuestArena.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ namespace {

std::atomic<SharedBackingResolver> sharedBackingResolver{nullptr};
std::atomic<SharedBackingWriter> sharedBackingWriter{nullptr};
std::atomic<SharedBackingRunsResolver> sharedBackingRunsResolver{nullptr};

}

Expand All @@ -520,6 +521,15 @@ bool GuestArenaSharedBacking_nid_postfix(std::uintptr_t address, std::size_t byt
const auto resolver = sharedBackingResolver.load(std::memory_order_acquire);
return resolver != nullptr && resolver(address, bytes, file, offset);
}

void GuestArenaSetSharedBackingRuns_nid_postfix(SharedBackingRunsResolver resolver) {
sharedBackingRunsResolver.store(resolver, std::memory_order_release);
}

bool GuestArenaSharedBackingRuns_nid_postfix(std::uintptr_t address, std::size_t bytes, int* file, SharedBackingRun* runs, std::size_t capacity, std::size_t* count) {
const auto resolver = sharedBackingRunsResolver.load(std::memory_order_acquire);
return resolver != nullptr && resolver(address, bytes, file, runs, capacity, count);
}
#endif

}
34 changes: 34 additions & 0 deletions core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,39 @@ bool SharedBacking(std::uintptr_t address, std::size_t bytes, int* file, std::ui
return true;
}

bool SharedBackingRuns(std::uintptr_t address, std::size_t bytes, int* file, GuestArena::SharedBackingRun* runs, std::size_t capacity, std::size_t* count) {
std::lock_guard lock(g_directLock);
*count = 0;
const auto next = g_directMappings.upper_bound(address);
if (bytes == 0 || bytes > std::numeric_limits<std::uintptr_t>::max() - address || next == g_directMappings.begin()) return false;
auto it = std::prev(next);
if (address >= it->second.end) return false;
const int backingFile = it->second.backing->File();
const auto end = address + bytes;
std::size_t filled = 0;
for (auto cursor = address; cursor < end;) {
if (it == g_directMappings.end() || it->first > cursor || it->second.backing->File() != backingFile) return false;
const auto phys = it->second.phys + (cursor - it->first);
const auto page = g_physPages.find(phys - phys % PS5_PAGE_SIZE);
if (page == g_physPages.end() || page->second.backing != it->second.backing) return false;
const auto stop = std::min(end, static_cast<std::uintptr_t>(it->second.end));
const auto offset = it->second.backing->FileOffset() + page->second.offset + phys % PS5_PAGE_SIZE;
if (filled != 0 && runs[filled - 1].offset + runs[filled - 1].bytes == offset) {
runs[filled - 1].bytes += stop - cursor;
} else {
if (filled == capacity) return false;
runs[filled++] = GuestArena::SharedBackingRun{offset, static_cast<std::size_t>(stop - cursor)};
}
cursor = stop;
++it;
}
const int duplicate = fcntl(backingFile, F_DUPFD_CLOEXEC, 0);
if (duplicate < 0) throw std::system_error(errno, std::generic_category(), "duplicate direct memory backing");
*file = duplicate;
*count = filled;
return true;
}

bool WriteSharedBacking(std::uintptr_t address, const void* source, std::size_t bytes) {
if (bytes == 0 || bytes > std::numeric_limits<std::uintptr_t>::max() - address) return false;
std::lock_guard lock(g_directLock);
Expand Down Expand Up @@ -1210,6 +1243,7 @@ void CreateDirectMemoryBacking(int64_t start, size_t len, int memoryType) {
static std::once_flag registered;
std::call_once(registered, [] {
GuestArena::GuestArenaSetSharedBacking_nid_postfix(&SharedBacking);
GuestArena::GuestArenaSetSharedBackingRuns_nid_postfix(&SharedBackingRuns);
GuestArena::GuestArenaSetSharedBackingWriter_nid_no_patch(&WriteSharedBacking);
});
#endif
Expand Down
54 changes: 54 additions & 0 deletions core/libs/tests/GuestMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2202,6 +2202,59 @@ static void CheckDirectMemoryBackingNeedsNoFilesystem() {
Require(sceKernelReleaseDirectMemory(phys, page) == 0);
}

static void CheckDirectMemorySharedBackingRuns() {
constexpr std::size_t page = 0x4000;
std::int64_t first = 0;
std::int64_t second = 0;
std::int64_t third = 0;
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page * 2, 0, 0, &first) == 0);
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page, 0, 0, &second) == 0);
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page, 0, 0, &third) == 0);
void* reserved = nullptr;
Require(sceKernelReserveVirtualRange(&reserved, page * 4, 0, 0) == 0 && reserved != nullptr);
auto* bytes = static_cast<unsigned char*>(reserved);
constexpr int fixed = 0x10;
void* piece = bytes;
Require(sceKernelMapDirectMemory(&piece, page * 2, 3, fixed, first, 0) == 0 && piece == bytes);
piece = bytes + page * 2;
Require(sceKernelMapDirectMemory(&piece, page, 3, fixed, second, 0) == 0 && piece == bytes + page * 2);
piece = bytes + page * 3;
Require(sceKernelMapDirectMemory(&piece, page, 3, fixed, third, 0) == 0 && piece == bytes + page * 3);
bytes[page * 2 + 7] = 0x6b;
const auto address = reinterpret_cast<std::uintptr_t>(reserved);
int file = -1;
GuestArena::SharedBackingRun runs[4]{};
std::size_t count = 0;
Require(GuestArena::GuestArenaSharedBackingRuns_nid_postfix(address, page * 4, &file, runs, 4, &count));
const bool adjacent = static_cast<std::uint64_t>(second) == static_cast<std::uint64_t>(first) + page * 2;
Require(count == (adjacent ? 2u : 3u) || (adjacent && static_cast<std::uint64_t>(third) == static_cast<std::uint64_t>(second) + page && count == 1));
std::uint64_t total = 0;
for (std::size_t index = 0; index < count; ++index) total += runs[index].bytes;
Require(total == page * 4 && runs[0].offset == static_cast<std::uint64_t>(first));
unsigned char byte = 0;
std::uint64_t secondOffset = 0;
std::uint64_t covered = 0;
for (std::size_t index = 0; index < count; ++index) {
if (covered <= page * 2 && page * 2 < covered + runs[index].bytes) secondOffset = runs[index].offset + (page * 2 - covered);
covered += runs[index].bytes;
}
Require(secondOffset == static_cast<std::uint64_t>(second));
Require(pread(file, &byte, 1, static_cast<off_t>(secondOffset + 7)) == 1 && byte == 0x6b);
close(file);
Require(GuestArena::GuestArenaSharedBackingRuns_nid_postfix(address + page, page * 3, &file, runs, 4, &count) && runs[0].offset == static_cast<std::uint64_t>(first) + page);
close(file);
const bool oneRun = GuestArena::GuestArenaSharedBackingRuns_nid_postfix(address, page * 4, &file, runs, 1, &count);
if (oneRun) close(file);
Require(!oneRun || count == 1);
Require(!GuestArena::GuestArenaSharedBackingRuns_nid_postfix(address, page * 5, &file, runs, 4, &count));
Require(!GuestArena::GuestArenaSharedBackingRuns_nid_postfix(address, 0, &file, runs, 4, &count));
Require(sceKernelMunmap(reserved, page * 4) == 0);
Require(sceKernelReleaseDirectMemory(first, page * 2) == 0);
Require(sceKernelReleaseDirectMemory(second, page) == 0);
Require(sceKernelReleaseDirectMemory(third, page) == 0);
}


static void CheckRegularFileMapping() {
constexpr std::size_t page = 0x4000;
const auto path = std::filesystem::path("anyps5-file-mapping-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + ".bin");
Expand Down Expand Up @@ -2847,6 +2900,7 @@ int main(int argc, char** argv) {
CheckDirectMemoryWriteWatchFollowsRemovedMappings();
CheckMappingCostFollowsTheMappingCount();
CheckDirectMemoryBackingNeedsNoFilesystem();
CheckDirectMemorySharedBackingRuns();
CheckDirectMemorySharedBacking();
CheckVirtualQueryFindsNeighbours();
CheckReleaseFindsEveryDirectMapping();
Expand Down
1 change: 1 addition & 0 deletions docs/dev/debt/libSceAgcDriver.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,4 @@
- [Unclamped normalized color targets](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - CB_COLOR_INFO targets with BLEND_CLAMP clear (bit 15) on UNORM and SNORM formats are accepted when blending is disabled (ENABLE clear or BLEND_BYPASS set). Vulkan converts unblended shader values and clamps to the normalized range automatically; blending into an unclamped normalized target remains unmodeled because Vulkan always clamps inputs prior to blending.

- [Thick SW_4KB_S_X volumes](../../../core/libs/prx/libSceAgcDriver/Graphics/src/TextureTiling.cpp) use AMD addrlib's GFX10 non-RB+ 16-pipe `GFX10_SW_4K_S3_X_PATINFO` equations for 1, 2, 4, 8 and 16-byte elements, including packed mip tails. The layout follows [AMD PAL c5e80007](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/imported/addrlib/src/gfx10/gfx10addrlib.cpp); it has not been measured on console hardware. Other thick swizzle modes outside the supported families still throw. Mipmapped 3D color targets remain refused by the color-register decoder.
- [dma-buf imports](../../../core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp) (libSceAgcDriver) - the udmabuf of a guest range lists the runs of the direct memory backing file behind it (`SharedBackingRuns`, one per physical allocation the range was mapped from, up to udmabuf's 1024), where it took the first run's offset for the whole range. The start-up probe also stores to the import from the GPU after CPU stores to the same pages and counts pages the CPU still reads stale: i915 maps a dma-buf without snooping the CPU caches (4 of 4 pages stale on Meteor Lake with Mesa 26.2.3 and main), so such a device does not fall back to dma-buf imports and copies instead. The memory type the import reports is host-coherent for the driver's own mappings; the guest's mapping of the same pages is what goes stale.
Loading