Skip to content
Draft
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
16 changes: 16 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,22 @@ ImportRun windowRun(const HostImports& state, ImportRun run, std::uint64_t begin
}
}
if (windowBegin % alignment != 0 || (windowEnd - windowBegin) % alignment != 0) return run;
const auto committed = GuestMemory::DescribeCommitted(windowBegin, windowEnd - windowBegin);
if (!committed.whole) {
const auto part = std::find_if(committed.ranges.begin(), committed.ranges.end(), [&](const auto& span) { return span.first <= begin && span.second >= end; });
if (part == committed.ranges.end()) return run;
const auto clippedBegin = part->first + (alignment - part->first % alignment) % alignment;
const auto clippedEnd = part->second - part->second % alignment;
if (clippedBegin > begin || clippedEnd < end) return run;
auto it = state.imports.upper_bound(clippedBegin);
if (it != state.imports.begin()) --it;
for (; it != state.imports.end() && it->first < clippedEnd; ++it) {
const auto importEnd = it->second.base + it->second.bytes;
if (importEnd > clippedBegin && (it->second.base < clippedBegin || importEnd > clippedEnd)) return run;
}
windowBegin = clippedBegin;
windowEnd = clippedEnd;
}
run.base = windowBegin;
run.bytes = windowEnd - windowBegin;
return run;
Expand Down
61 changes: 61 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,66 @@ void releaseSparse(std::byte* block, std::size_t reserved) {
#endif
}

void importPartlyCommittedWindowTests(const Context& context) {
#ifdef _WIN32
constexpr std::size_t piece = 65536;
#else
constexpr std::size_t piece = 4096;
#endif
constexpr std::size_t reserved = 8 * piece;
auto importing = context;
importing.hostImportAlignment = piece;
auto* block = reserveSparse(reserved, piece);
struct Cleanup {
std::byte* block;
Context importing;
~Cleanup() {
{
GuestAllocations::Mutation mutation;
mutation.Remove(block);
}
HostImportFor(importing, reinterpret_cast<std::uintptr_t>(block), 1);
SetHostImportRangeWindow(std::uint64_t{64} << 20u);
releaseSparse(block, reserved);
}
} cleanup{block, importing};
{
GuestAllocations::Mutation mutation;
mutation.Add(block, reserved, true, false, true);
}
std::memset(block, 0x35, piece);
SetHostImportRangeWindow(2 * piece);
const auto base = reinterpret_cast<std::uintptr_t>(block);
Require(AgcDriver::GuestMemory::DescribeCommitted(base, piece).whole && !AgcDriver::GuestMemory::DescribeCommitted(base, 2 * piece).whole, "the import window test has no uncommitted tail");
const auto* imported = HostImportFor(importing, base + 16, 32);
Require(imported != nullptr && imported->base == base && imported->bytes == piece, "a committed access was refused because its import window includes uncommitted pages");
const auto buffer = imported->buffer;
Require(HostImportFor(importing, base + piece - 16, 32) == nullptr, "an import crossed from committed bytes into an uncommitted page");
const auto* repeated = HostImportFor(importing, base + 32, 32);
Require(repeated != nullptr && repeated->buffer == buffer, "a repeated committed access did not retain its narrowed import");
#ifdef _WIN32
Require(VirtualAlloc(block + 4 * piece, piece, MEM_COMMIT, PAGE_READWRITE) != nullptr, "cannot commit the separate import island");
#else
Require(mprotect(block + 4 * piece, piece, PROT_READ | PROT_WRITE) == 0, "cannot commit the separate import island");
#endif
const auto* island = HostImportFor(importing, base + 4 * piece + 16, 32);
Require(island != nullptr && island->base == base + 4 * piece && island->bytes == piece && island->buffer != buffer, "a separate committed island did not get its own import");
Require(HostImportFor(importing, base + piece - 16, 3 * piece + 32) == nullptr, "an import joined committed islands across uncommitted pages");
#ifdef _WIN32
Require(VirtualAlloc(block + piece, piece, MEM_COMMIT, PAGE_READWRITE) != nullptr, "cannot grow the committed import prefix");
#else
Require(mprotect(block + piece, piece, PROT_READ | PROT_WRITE) == 0, "cannot grow the committed import prefix");
#endif
Require(HostImportFor(importing, base + piece - 16, 32) == nullptr, "growing a window replaced a held import without a recorder");
{
GuestBufferMemory nextBuild(importing);
nextBuild.Upload(false);
nextBuild.WriteBack();
}
const auto* grown = HostImportFor(importing, base + piece - 16, 32);
Require(grown != nullptr && grown->base == base && grown->bytes == 2 * piece, "a newly committed window did not replace the narrower import");
}

void sparseHeapTests(const Context& context, const BdaTestAccess& access) {
constexpr std::size_t reserved = 64u << 20u;
constexpr std::size_t committed = 65536;
Expand Down Expand Up @@ -1936,6 +1996,7 @@ void RunBdaResourceTests(const Context& context, const BdaTestAccess& access) {
importJoinTests(context);
importWholeRangeOverWindowTests(context);
importRefusedWholeRangeTests(context);
importPartlyCommittedWindowTests(context);
importNeighbouringEdgesTests(context);
importAtMapOverBudgetTests(context);
heapMirrorTests(context, access);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <sys/mman.h>
#endif
#include <array>
#include <cstdint>
Expand Down Expand Up @@ -40,14 +42,18 @@ std::string Hex(std::uint64_t value) {

class GuestBlock {
public:
explicit GuestBlock(std::uint64_t size) : bytes(size) {
explicit GuestBlock(std::uint64_t size, std::uint64_t committedBytes = 0) : bytes(size), committed(committedBytes == 0 ? size : committedBytes) {
#ifdef _WIN32
block = static_cast<std::uint8_t*>(VirtualAlloc(nullptr, bytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
block = static_cast<std::uint8_t*>(VirtualAlloc(nullptr, bytes, MEM_RESERVE, PAGE_NOACCESS));
Require(block != nullptr && VirtualAlloc(block, committed, MEM_COMMIT, PAGE_READWRITE) != nullptr, "host import windows: cannot commit guest memory");
#else
block = static_cast<std::uint8_t*>(std::aligned_alloc(GuestAlignment, bytes));
#endif
Require(block != nullptr, "host import windows: cannot allocate a guest block of " + Hex(bytes));
std::memset(block, 0, bytes);
#ifndef _WIN32
if (committed < bytes) Require(mprotect(block + committed, bytes - committed, PROT_NONE) == 0, "host import windows: cannot protect uncommitted guest memory");
#endif
std::memset(block, 0, committed);
GuestAllocations::Mutation().Add(block, bytes, true, true, true);
}

Expand All @@ -56,6 +62,7 @@ class GuestBlock {
#ifdef _WIN32
VirtualFree(block, 0, MEM_RELEASE);
#else
if (committed < bytes) mprotect(block + committed, bytes - committed, PROT_READ | PROT_WRITE);
std::free(block);
#endif
}
Expand All @@ -69,6 +76,7 @@ class GuestBlock {
private:
std::uint8_t* block = nullptr;
std::uint64_t bytes = 0;
std::uint64_t committed = 0;
};

enum class Outcome : std::uint8_t { NoDevice, NoImport, Passed };
Expand Down Expand Up @@ -124,6 +132,21 @@ Outcome WindowsFollowTheirRange() {
return Outcome::Passed;
}

Outcome PartlyCommittedWindow() {
GuestBlock guest(BlockBytes, GuestAlignment);
auto device = OpenVulkanTestDevice();
if (!device) return Outcome::NoDevice;
Require(!AgcDriver::GuestMemory::DescribeCommitted(guest.Address(), WindowBytes).whole, "host import windows: the partial window has no inaccessible tail");
Require(Fill(*device, guest.Address()), "host import windows: cannot fill the committed prefix");
const auto probe = Probe(*device);
Require(Graphics::HostImportCovers(probe, guest.Address(), ProbeBytes), "host import windows: a committed prefix fell back to copies because of an inaccessible tail");
std::array<std::uint32_t, 4> actual{};
std::memcpy(actual.data(), guest.Data(), sizeof(actual));
Require(actual == std::array<std::uint32_t, 4>{0x11111111u, 0x22222222u, 0x33333333u, 0x44444444u}, "host import windows: the imported committed prefix lost its GPU stores");
Graphics::ClearHostImports(device->Device());
return Outcome::Passed;
}

}

int main() {
Expand All @@ -136,6 +159,7 @@ int main() {
}
const auto windows = WindowsFollowTheirRange();
Require(windows == Outcome::Passed, "host import windows: the device stopped importing guest memory between two scenarios");
Require(PartlyCommittedWindow() == Outcome::Passed, "host import windows: the partial window lost its device");
std::puts("host import windows tests passed");
return 0;
} catch (const std::exception& error) {
Expand Down
Loading