mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
On Windows the write watch is dropped in whole 64 KiB blocks (WriteWatchCoverage::Exclude), but the unwatch hook was given the page range of the import. An image with results pending in the same block as an imported page, outside that page, lost the watch without its guest bytes saved, and storing its results threw. The hook now gets the span of blocks Exclude drops (ExcludedSpan, the same block math), so every image with results pending in those blocks saves its bytes first.
70 lines
6.1 KiB
C++
70 lines
6.1 KiB
C++
#include "prx/libSceAgcDriver/Execution/include/WriteWatchCoverage.hpp"
|
|
#include <iostream>
|
|
#include <limits>
|
|
#include <stdexcept>
|
|
|
|
void require(bool condition, const char* message) {
|
|
if (!condition) throw std::runtime_error(message);
|
|
}
|
|
|
|
int main() {
|
|
try {
|
|
using AgcDriver::GuestMemory::WriteWatchCoverage;
|
|
constexpr std::uint64_t base = 0x200000000;
|
|
constexpr std::size_t block = 65536;
|
|
WriteWatchCoverage coverage;
|
|
require(!coverage.Covers(base, 1), "an uninitialized arena is watched");
|
|
coverage.Initialize(base, 4 * block + 1);
|
|
require(coverage.Covers(base, 4 * block + 1), "the initialized arena is not watched");
|
|
require(!coverage.Covers(base - 1, 1) && !coverage.Covers(base + 4 * block + 1, 1), "outside memory is watched");
|
|
require(!coverage.Covers(base, 0) && !coverage.Covers(base, std::numeric_limits<std::size_t>::max()), "an empty or overflowing range is watched");
|
|
std::uint64_t spanBegin = 0;
|
|
std::uint64_t spanEnd = 0;
|
|
require(coverage.ExcludedSpan(base + block + 4096, 4096, &spanBegin, &spanEnd) && spanBegin == base + block && spanEnd == base + 2 * block, "the span of an imported page is not its whole block");
|
|
require(coverage.ExcludedSpan(base + 3 * block - 1, 2, &spanBegin, &spanEnd) && spanBegin == base + 2 * block && spanEnd == base + 4 * block, "the span of a boundary import is not both blocks");
|
|
require(coverage.ExcludedSpan(base + 4 * block, std::numeric_limits<std::size_t>::max(), &spanBegin, &spanEnd) && spanBegin == base + 4 * block && spanEnd == base + 4 * block + 1, "the span of the last partial block is not clipped to the arena");
|
|
require(coverage.ExcludedSpan(base - 1, 2, &spanBegin, &spanEnd) && spanBegin == base && spanEnd == base + block, "the span of an import crossing the arena start is not clipped");
|
|
require(!coverage.ExcludedSpan(base - block, block, &spanBegin, &spanEnd) && !coverage.ExcludedSpan(base + 5 * block, block, &spanBegin, &spanEnd) && !coverage.ExcludedSpan(base, 0, &spanBegin, &spanEnd), "an outside or empty import has a span");
|
|
require(coverage.Exclude(base + block + 4096, 1), "an imported page was not excluded");
|
|
require(!coverage.Covers(base + block, block), "the imported block is still watched");
|
|
require(!coverage.Covers(base + block - 1, 2), "a range crossing into the import is watched");
|
|
require(coverage.Covers(base, block) && coverage.Covers(base + 2 * block, 2 * block + 1), "unrelated blocks lost write watching");
|
|
require(!coverage.Exclude(base + block, block), "excluding an imported block again reports a change");
|
|
require(coverage.Exclude(base + 3 * block - 1, 2), "an import across a block boundary was not excluded");
|
|
require(!coverage.Covers(base + 2 * block, block) && !coverage.Covers(base + 3 * block, block), "a boundary import left one block watched");
|
|
require(coverage.Covers(base, block) && coverage.Covers(base + 4 * block, 1), "a boundary import excluded adjacent blocks");
|
|
require(!coverage.Exclude(base - block, block) && !coverage.Exclude(base + 5 * block, block), "an outside import changed coverage");
|
|
require(coverage.Exclude(base + 4 * block, std::numeric_limits<std::size_t>::max()), "the last partial block was not excluded");
|
|
require(!coverage.Covers(base + 4 * block, 1), "the last partial block is still watched");
|
|
coverage.Initialize(base, 2 * block);
|
|
require(coverage.Exclude(base - 1, 2) && !coverage.Covers(base, block), "an import crossing the arena start was not clipped");
|
|
require(coverage.Covers(base + block, block), "clipping an import excluded the rest of the arena");
|
|
coverage.Initialize(base, 4 * block + 1);
|
|
coverage.Exclude(base, 4 * block + 1);
|
|
require(!coverage.Restore(base + 1, block - 1) && !coverage.Covers(base, block), "a partial replacement restored an excluded block");
|
|
require(coverage.Restore(base + block, block) && coverage.Covers(base + block, block), "fresh private backing stayed excluded");
|
|
require(!coverage.Restore(base + block, block), "restoring an already watched block reports a change");
|
|
require(!coverage.Covers(base, block) && !coverage.Covers(base + 2 * block, block), "restoring one block restored its neighbours");
|
|
require(coverage.Restore(base + block - 1, 2 * block + 2), "a full interior replacement was not restored");
|
|
require(!coverage.Covers(base, block) && coverage.Covers(base + block, 2 * block) && !coverage.Covers(base + 3 * block, block), "a replacement restored partially covered boundary blocks");
|
|
require(!coverage.Restore(base - block, block) && !coverage.Restore(base + 5 * block, block), "an outside replacement changed coverage");
|
|
require(coverage.Restore(base + 3 * block, std::numeric_limits<std::size_t>::max()) && coverage.Covers(base + 3 * block, block + 1), "an oversized replacement was not clipped to the arena");
|
|
require(coverage.Restore(base - block, 2 * block) && coverage.Covers(base, block), "a replacement across the arena start was not clipped");
|
|
coverage.Exclude(base + block + 4096, 1);
|
|
require(!coverage.Covers(base + block, block), "a re-import did not exclude fresh backing again");
|
|
coverage.Initialize(base, 2 * block);
|
|
coverage.Exclude(base, block, 4);
|
|
require(!coverage.Restore(base, block, 3) && !coverage.Restore(base, block, 4), "a stale mapping notification restored a newer import");
|
|
require(coverage.Restore(base, block, 5) && coverage.Covers(base, block), "a newer private mapping did not restore an older exclusion");
|
|
coverage.Exclude(base, block, 5);
|
|
coverage.Exclude(base, block, 7);
|
|
require(!coverage.Restore(base, block, 6) && !coverage.Covers(base, block), "a delayed mapping notification erased a repeated import");
|
|
require(coverage.Restore(base, block, 8), "a fresh replacement did not restore the latest import exclusion");
|
|
std::cout << "write watch coverage passed\n";
|
|
return 0;
|
|
} catch (const std::exception& error) {
|
|
std::cerr << error.what() << '\n';
|
|
return 1;
|
|
}
|
|
}
|