mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
On Linux the write watch protects pages, so a write-back from shadow storage into guest memory took a fault at the first store into every page. plat_mem_prepare_write clears the protection of a range with one UFFDIO_WRITEPROTECT call; the pages count as written, as they would after the faults. Off by default; Windows has nothing to clear. Pull request #327 by theurbanninja1993-sketch (a draft on GitHub). The platform function and its Linux test are taken as they are. The call moved: the write-back it was written for is now shadow_write_back in runtime/vk_memory.c (a file other sessions are changing), where it covers the runs of pages a submission changed, and the setting is registered in cfg.c and described in nx2navi.ini.example, which also says that the lazy write-back stays off with shadow storage. The Windows files compile; the Linux code and its test are not compiled or run here. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
33 lines
1.7 KiB
C
33 lines
1.7 KiB
C
// plat_mem_prepare_write on Linux (runtime/platform/linux.c): every page of a prepared range counts as written, a
|
|
// reset leaves it clean, and a later store to one page is still seen. No game files. Build and run from the
|
|
// repository root:
|
|
// clang -O2 -ffunction-sections -fdata-sections -Iruntime -c runtime/platform/linux.c -o /tmp/plat.o
|
|
// clang -O2 -Wall -Iruntime tests/platform_bulk_write_test.c /tmp/plat.o -Wl,--gc-sections -lpthread -ldl -o /tmp/bulk
|
|
// /tmp/bulk (exit 77: the write watch is not available on this kernel)
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "platform/platform.h"
|
|
int main(void) {
|
|
const uint64_t a = 0x6000000000ull, n = 4 * 4096;
|
|
uint64_t pages[4];
|
|
assert(plat_mem_reserve(a, n, 1));
|
|
assert(plat_mem_commit(a, n));
|
|
memset((void *)(uintptr_t)a, 0, n);
|
|
uint64_t count = plat_mem_written(a, n, pages, 4, 1);
|
|
if (count == UINT64_MAX) { puts("SKIP: asynchronous write watch unavailable"); return 77; }
|
|
assert(plat_mem_written(a, n, pages, 4, 0) == 0);
|
|
assert(plat_mem_prepare_write(a + 7, n - 14));
|
|
memset((void *)(uintptr_t)(a + 7), 0x5a, n - 14);
|
|
assert(plat_mem_written(a, n, pages, 4, 1) == 4);
|
|
for (unsigned i = 0; i < 4; i++) assert(pages[i] == a + i * 4096);
|
|
assert(plat_mem_written(a, n, pages, 4, 0) == 0);
|
|
*(volatile unsigned char *)(uintptr_t)(a + 2 * 4096) = 0xa5;
|
|
assert(plat_mem_written(a, n, pages, 4, 1) == 1 && pages[0] == a + 2 * 4096);
|
|
assert(!plat_mem_prepare_write(a, 0));
|
|
assert(!plat_mem_prepare_write(UINT64_MAX - 7, 16));
|
|
puts("bulk write watch: full range tracked, reset clean, subsequent single-page write tracked");
|
|
return 0;
|
|
}
|