feat(agc): import shared direct memory through udmabuf on Linux
On Linux, direct memory is a memfd mapped MAP_SHARED. amdgpu accepts only private anonymous memory for a host pointer import (libdrm sets AMDGPU_GEM_USERPTR_ANONONLY), so every import of direct memory failed with VK_ERROR_INVALID_EXTERNAL_HANDLE and its ranges were copied. When a host pointer import fails, the range's memfd slice is turned into a dma-buf through /dev/udmabuf and imported with VK_EXT_external_memory_dma_buf. The direct memory backing is sealed against shrinking and growing, as udmabuf requires. libkernel reports the backing of a guest range through GuestArenaSharedBacking. Without /dev/udmabuf, or past its size limit, the range is copied as before. APS5_TRACE_HOST_IMPORT now prints the mappings behind a failed import on Linux too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6c48acecb3
commit
815d274736
|
|
@ -208,6 +208,7 @@ struct VulkanDevice::State {
|
|||
bool depthBiasClamp = false;
|
||||
bool occlusionQueryPrecise = false;
|
||||
VkDeviceSize hostImportAlignment = 0;
|
||||
bool dmaBufImport = false;
|
||||
bool depthRangeUnrestricted = false;
|
||||
bool samplerAnisotropy = false;
|
||||
bool textureCompressionBC = false;
|
||||
|
|
@ -832,6 +833,13 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
|
|||
}
|
||||
#endif
|
||||
deviceExtensions.push_back(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME);
|
||||
#ifndef _WIN32
|
||||
state->dmaBufImport = hasExtension(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME) && hasExtension(VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME);
|
||||
if (state->dmaBufImport) {
|
||||
deviceExtensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
|
||||
deviceExtensions.push_back(VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT listRestartFeatures{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT};
|
||||
if (hasExtension(VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME)) {
|
||||
|
|
@ -2445,6 +2453,7 @@ Graphics::Context VulkanDevice::buildContext() const {
|
|||
state->depthClamp,
|
||||
state->hostImportAlignment
|
||||
};
|
||||
context.dmaBufImport = state->dmaBufImport;
|
||||
context.recorder = state->recorder.get();
|
||||
context.descriptorCache = state->descriptorCache.get();
|
||||
context.samplerCache = state->samplerCache.get();
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ struct Context {
|
|||
bool depthClamp = false;
|
||||
// Nonzero when VK_EXT_external_memory_host is enabled: the required host pointer alignment.
|
||||
VkDeviceSize hostImportAlignment = 0;
|
||||
bool dmaBufImport = false;
|
||||
RenderCache* renderCache = nullptr;
|
||||
DrawQueue* drawQueue = nullptr;
|
||||
GraphicsPipelineCache* graphicsPipelines = nullptr;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@
|
|||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <linux/udmabuf.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
|
|
@ -170,9 +174,7 @@ void retireImport(const Context& context, HostImports& state, std::map<std::uint
|
|||
state.imports.erase(it);
|
||||
}
|
||||
|
||||
const char* createImport(const Context& context, HostImport& entry, VkResult& failure) {
|
||||
const auto bytes = entry.bytes;
|
||||
void* const host = entry.alias != nullptr ? entry.alias : reinterpret_cast<void*>(entry.base);
|
||||
const char* bindImport(const Context& context, HostImport& entry, VkExternalMemoryHandleTypeFlagBits handleType, const void* import, std::uint32_t importTypes, bool& allocated, VkResult& failure) {
|
||||
const auto failed = [&](const char* step, VkResult result) -> const char* {
|
||||
if (entry.buffer) context.Function<PFN_vkDestroyBuffer>("vkDestroyBuffer")(context.device, entry.buffer, nullptr);
|
||||
if (entry.memory) context.Function<PFN_vkFreeMemory>("vkFreeMemory")(context.device, entry.memory, nullptr);
|
||||
|
|
@ -181,25 +183,24 @@ const char* createImport(const Context& context, HostImport& entry, VkResult& fa
|
|||
failure = result;
|
||||
return step;
|
||||
};
|
||||
const VkExternalMemoryBufferCreateInfo external{VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, nullptr, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT};
|
||||
allocated = false;
|
||||
const VkExternalMemoryBufferCreateInfo external{VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, nullptr, static_cast<VkExternalMemoryHandleTypeFlags>(handleType)};
|
||||
VkBufferCreateInfo info{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, &external};
|
||||
info.size = bytes;
|
||||
info.size = entry.bytes;
|
||||
// INDIRECT_BUFFER: DISPATCH_INDIRECT group counts are read in place (VulkanDevice::DispatchIndirect).
|
||||
info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
|
||||
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
if (const auto result = context.Function<PFN_vkCreateBuffer>("vkCreateBuffer")(context.device, &info, nullptr, &entry.buffer); result != VK_SUCCESS) return failed("vkCreateBuffer", result);
|
||||
VkMemoryHostPointerPropertiesEXT pointer{VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT};
|
||||
if (const auto result = context.Function<PFN_vkGetMemoryHostPointerPropertiesEXT>("vkGetMemoryHostPointerPropertiesEXT")(context.device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, host, &pointer); result != VK_SUCCESS) return failed("vkGetMemoryHostPointerPropertiesEXT", result);
|
||||
VkMemoryRequirements requirements{};
|
||||
context.Function<PFN_vkGetBufferMemoryRequirements>("vkGetBufferMemoryRequirements")(context.device, entry.buffer, &requirements);
|
||||
const auto types = requirements.memoryTypeBits & pointer.memoryTypeBits;
|
||||
const auto types = requirements.memoryTypeBits & importTypes;
|
||||
if (types == 0) return failed("memory type selection", VK_ERROR_FORMAT_NOT_SUPPORTED);
|
||||
const VkImportMemoryHostPointerInfoEXT import{VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT, nullptr, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, host};
|
||||
const VkMemoryAllocateFlagsInfo flags{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, &import, VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, 0};
|
||||
const VkMemoryAllocateFlagsInfo flags{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, import, VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, 0};
|
||||
VkMemoryAllocateInfo allocation{VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, &flags};
|
||||
allocation.allocationSize = bytes;
|
||||
allocation.allocationSize = entry.bytes;
|
||||
allocation.memoryTypeIndex = static_cast<std::uint32_t>(std::countr_zero(types));
|
||||
if (const auto result = context.Function<PFN_vkAllocateMemory>("vkAllocateMemory")(context.device, &allocation, nullptr, &entry.memory); result != VK_SUCCESS) return failed("vkAllocateMemory", result);
|
||||
allocated = true;
|
||||
if (const auto result = context.Function<PFN_vkBindBufferMemory>("vkBindBufferMemory")(context.device, entry.buffer, entry.memory, 0); result != VK_SUCCESS) return failed("vkBindBufferMemory", result);
|
||||
const VkBufferDeviceAddressInfo addressInfo{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, entry.buffer};
|
||||
entry.address = context.Function<PFN_vkGetBufferDeviceAddressKHR>("vkGetBufferDeviceAddressKHR")(context.device, &addressInfo);
|
||||
|
|
@ -207,6 +208,61 @@ const char* createImport(const Context& context, HostImport& entry, VkResult& fa
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const char* createHostPointerImport(const Context& context, HostImport& entry, VkResult& failure) {
|
||||
void* const host = entry.alias != nullptr ? entry.alias : reinterpret_cast<void*>(entry.base);
|
||||
VkMemoryHostPointerPropertiesEXT pointer{VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT};
|
||||
if (const auto result = context.Function<PFN_vkGetMemoryHostPointerPropertiesEXT>("vkGetMemoryHostPointerPropertiesEXT")(context.device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, host, &pointer); result != VK_SUCCESS) {
|
||||
failure = result;
|
||||
return "vkGetMemoryHostPointerPropertiesEXT";
|
||||
}
|
||||
const VkImportMemoryHostPointerInfoEXT import{VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT, nullptr, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, host};
|
||||
bool allocated = false;
|
||||
return bindImport(context, entry, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, &import, pointer.memoryTypeBits, allocated, failure);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
int udmabufDevice() {
|
||||
static const int device = open("/dev/udmabuf", O_RDWR | O_CLOEXEC);
|
||||
return device;
|
||||
}
|
||||
|
||||
const char* createDmaBufImport(const Context& context, HostImport& entry, int file, std::uint64_t offset, VkResult& failure) {
|
||||
udmabuf_create request{};
|
||||
request.memfd = static_cast<std::uint32_t>(file);
|
||||
request.flags = UDMABUF_FLAGS_CLOEXEC;
|
||||
request.offset = offset;
|
||||
request.size = entry.bytes;
|
||||
const int device = udmabufDevice();
|
||||
const int buffer = device < 0 ? -1 : ioctl(device, UDMABUF_CREATE, &request);
|
||||
close(file);
|
||||
if (buffer < 0) {
|
||||
failure = VK_ERROR_INVALID_EXTERNAL_HANDLE;
|
||||
return device < 0 ? "open /dev/udmabuf" : "UDMABUF_CREATE";
|
||||
}
|
||||
VkMemoryFdPropertiesKHR properties{VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR};
|
||||
if (const auto result = context.Function<PFN_vkGetMemoryFdPropertiesKHR>("vkGetMemoryFdPropertiesKHR")(context.device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, buffer, &properties); result != VK_SUCCESS) {
|
||||
close(buffer);
|
||||
failure = result;
|
||||
return "vkGetMemoryFdPropertiesKHR";
|
||||
}
|
||||
const VkImportMemoryFdInfoKHR import{VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, nullptr, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, buffer};
|
||||
bool allocated = false;
|
||||
const char* step = bindImport(context, entry, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, &import, properties.memoryTypeBits, allocated, failure);
|
||||
if (!allocated) close(buffer);
|
||||
return step;
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* createImport(const Context& context, HostImport& entry, VkResult& failure) {
|
||||
const char* step = createHostPointerImport(context, entry, failure);
|
||||
#ifndef _WIN32
|
||||
int file = -1;
|
||||
std::uint64_t offset = 0;
|
||||
if (step != nullptr && context.dmaBufImport && GuestArena::GuestArenaSharedBacking_nid_postfix(static_cast<std::uintptr_t>(entry.base), static_cast<std::size_t>(entry.bytes), &file, &offset)) step = createDmaBufImport(context, entry, file, offset, failure);
|
||||
#endif
|
||||
return step;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
enum class ImportWatchRequest : std::uint8_t { Probe, Watch, Unwatch };
|
||||
|
||||
|
|
@ -337,6 +393,18 @@ const HostImport* importAllocation(const Context& context, HostImports& state, s
|
|||
std::fprintf(stderr, "[gpu] 0x%llx+0x%llx state 0x%lx protect 0x%lx type 0x%lx allocation 0x%llx\n", static_cast<unsigned long long>(cursor), static_cast<unsigned long long>(std::min(regionEnd, base + bytes) - cursor), info.State, info.Protect, info.Type, reinterpret_cast<unsigned long long>(info.AllocationBase));
|
||||
cursor = regionEnd;
|
||||
}
|
||||
#else
|
||||
static const bool trace = std::getenv("APS5_TRACE_HOST_IMPORT") != nullptr;
|
||||
if (std::FILE* maps = trace ? std::fopen("/proc/self/maps", "r") : nullptr) {
|
||||
char line[512];
|
||||
while (std::fgets(line, sizeof(line), maps) != nullptr) {
|
||||
unsigned long long first = 0;
|
||||
unsigned long long last = 0;
|
||||
if (std::sscanf(line, "%llx-%llx", &first, &last) != 2 || last <= base || first >= base + bytes) continue;
|
||||
std::fprintf(stderr, "[gpu] %s", line);
|
||||
}
|
||||
std::fclose(maps);
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@
|
|||
// absolute address and breaks on host addresses outside that range.
|
||||
namespace GuestArena {
|
||||
|
||||
#ifndef _WIN32
|
||||
using SharedBackingResolver = bool (*)(std::uintptr_t address, std::size_t bytes, int* file, std::uint64_t* offset);
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
bool GuestArenaAvailable_nid_postfix();
|
||||
|
|
@ -32,6 +36,9 @@ void GuestArenaReset_nid_postfix(void* pointer, std::size_t bytes);
|
|||
void GuestArenaMap_nid_postfix(void* pointer, std::size_t bytes, void* section, std::uint64_t offset, std::uint32_t protection);
|
||||
void* GuestArenaMapAlias_nid_postfix(std::uintptr_t address, std::size_t bytes);
|
||||
void GuestArenaUnmapAlias_nid_postfix(void* alias);
|
||||
#else
|
||||
void GuestArenaSetSharedBacking_nid_postfix(SharedBackingResolver resolver);
|
||||
bool GuestArenaSharedBacking_nid_postfix(std::uintptr_t address, std::size_t bytes, int* file, std::uint64_t* offset);
|
||||
#endif
|
||||
bool GuestArenaBeginHostWrite_nid_postfix(void* pointer, std::size_t bytes);
|
||||
void GuestArenaEndHostWrite_nid_postfix(void* pointer, std::size_t bytes);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "prx/libc/include/GuestArena.hpp"
|
||||
#include "prx/libc/include/WindowsMappings.hpp"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iterator>
|
||||
|
|
@ -267,4 +268,21 @@ void GuestArenaEndHostWrite_nid_postfix(void* pointer, std::size_t bytes) {
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
namespace {
|
||||
|
||||
std::atomic<SharedBackingResolver> sharedBackingResolver{nullptr};
|
||||
|
||||
}
|
||||
|
||||
void GuestArenaSetSharedBacking_nid_postfix(SharedBackingResolver resolver) {
|
||||
sharedBackingResolver.store(resolver, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool GuestArenaSharedBacking_nid_postfix(std::uintptr_t address, std::size_t bytes, int* file, std::uint64_t* offset) {
|
||||
const auto resolver = sharedBackingResolver.load(std::memory_order_acquire);
|
||||
return resolver != nullptr && resolver(address, bytes, file, offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <vector>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <fstream>
|
||||
|
|
@ -233,18 +234,27 @@ public:
|
|||
throw std::system_error(error, std::system_category(), message);
|
||||
}
|
||||
#else
|
||||
file = memfd_create("direct memory", MFD_CLOEXEC);
|
||||
file = memfd_create("direct memory", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
if (file < 0) throw std::system_error(errno, std::generic_category(), "create direct memory backing");
|
||||
if (ftruncate(file, static_cast<off_t>(bytes)) != 0) {
|
||||
const int error = errno;
|
||||
::close(file);
|
||||
throw std::system_error(error, std::generic_category(), "size direct memory backing");
|
||||
}
|
||||
if (fcntl(file, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW) != 0) {
|
||||
const int error = errno;
|
||||
::close(file);
|
||||
throw std::system_error(error, std::generic_category(), "seal direct memory backing");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int MemoryType() const { return memoryType; }
|
||||
|
||||
#if defined(__linux__)
|
||||
int File() const { return file; }
|
||||
#endif
|
||||
|
||||
~PhysicalBacking() {
|
||||
#ifdef _WIN32
|
||||
CloseHandle(section);
|
||||
|
|
@ -353,6 +363,32 @@ void WatchMapping(std::uintptr_t address, std::size_t len) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
bool SharedBacking(std::uintptr_t address, std::size_t bytes, int* file, std::uint64_t* offset) {
|
||||
std::lock_guard lock(g_directLock);
|
||||
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 auto backing = it->second.backing;
|
||||
const auto phys = it->second.phys + (address - it->first);
|
||||
const auto page = g_physPages.find(phys - phys % PS5_PAGE_SIZE);
|
||||
if (page == g_physPages.end() || page->second.backing != backing) return false;
|
||||
const auto end = address + bytes;
|
||||
for (auto covered = it->second.end; covered < end;) {
|
||||
const auto following = std::next(it);
|
||||
if (following == g_directMappings.end() || following->first != covered || following->second.backing != backing || following->second.phys != it->second.phys + (it->second.end - it->first)) return false;
|
||||
it = following;
|
||||
covered = it->second.end;
|
||||
}
|
||||
const int duplicate = fcntl(backing->File(), F_DUPFD_CLOEXEC, 0);
|
||||
if (duplicate < 0) throw std::system_error(errno, std::generic_category(), "duplicate direct memory backing");
|
||||
*file = duplicate;
|
||||
*offset = page->second.offset + phys % PS5_PAGE_SIZE;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void AddMapping(std::uintptr_t address, std::size_t len, std::uint64_t phys, int nativeProt) {
|
||||
ValidatePhysicalRange(phys, len);
|
||||
EraseMappings(address, address + len);
|
||||
|
|
@ -754,6 +790,10 @@ void CreateDirectMemoryBacking(int64_t start, size_t len, int memoryType) {
|
|||
for (std::size_t offset = 0; offset < len; offset += PS5_PAGE_SIZE) {
|
||||
if (g_physPages.contains(first + offset)) throw std::runtime_error("physical allocation overlaps live direct memory");
|
||||
}
|
||||
#if defined(__linux__)
|
||||
static std::once_flag registered;
|
||||
std::call_once(registered, [] { GuestArena::GuestArenaSetSharedBacking_nid_postfix(&SharedBacking); });
|
||||
#endif
|
||||
const auto backing = std::make_shared<PhysicalBacking>(len, memoryType);
|
||||
std::map<std::uint64_t, PhysicalPage> pages;
|
||||
for (std::size_t offset = 0; offset < len; offset += PS5_PAGE_SIZE) pages.emplace(first + offset, PhysicalPage{backing, offset});
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
#if defined(__linux__)
|
||||
#include <fcntl.h>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <sys/mman.h>
|
||||
|
|
@ -936,6 +937,48 @@ static void CheckDirectMemoryBackingNeedsNoFilesystem() {
|
|||
Require(sceKernelReleaseDirectMemory(phys, page) == 0);
|
||||
}
|
||||
|
||||
static void CheckDirectMemorySharedBacking() {
|
||||
constexpr std::size_t page = 0x4000;
|
||||
std::int64_t phys = 0;
|
||||
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page * 3, 0, 0, &phys) == 0);
|
||||
void* first = nullptr;
|
||||
Require(sceKernelMapDirectMemory(&first, page * 2, 3, 0, phys + page, 0) == 0);
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(first);
|
||||
static_cast<volatile unsigned char*>(first)[page + 5] = 0x5a;
|
||||
int file = -1;
|
||||
std::uint64_t offset = 0;
|
||||
Require(GuestArena::GuestArenaSharedBacking_nid_postfix(address + page, page, &file, &offset));
|
||||
Require(offset == page * 2);
|
||||
unsigned char byte = 0;
|
||||
Require(pread(file, &byte, 1, static_cast<off_t>(offset + 5)) == 1 && byte == 0x5a);
|
||||
const int seals = fcntl(file, F_GET_SEALS);
|
||||
Require(seals >= 0 && (seals & F_SEAL_SHRINK) != 0 && (seals & F_SEAL_GROW) != 0);
|
||||
Require((fcntl(file, F_GETFD) & FD_CLOEXEC) != 0);
|
||||
close(file);
|
||||
Require(GuestArena::GuestArenaSharedBacking_nid_postfix(address, page * 2, &file, &offset) && offset == page);
|
||||
close(file);
|
||||
Require(!GuestArena::GuestArenaSharedBacking_nid_postfix(address, page * 3, &file, &offset));
|
||||
Require(!GuestArena::GuestArenaSharedBacking_nid_postfix(address, 0, &file, &offset));
|
||||
void* flexible = nullptr;
|
||||
Require(sceKernelMapFlexibleMemory(&flexible, page * 2, 3, 0) == 0);
|
||||
const auto flexibleAddress = reinterpret_cast<std::uintptr_t>(flexible);
|
||||
Require(!GuestArena::GuestArenaSharedBacking_nid_postfix(flexibleAddress, page, &file, &offset));
|
||||
void* low = flexible;
|
||||
Require(sceKernelMapDirectMemory(&low, page, 3, 0x10, phys, 0) == 0);
|
||||
void* high = static_cast<unsigned char*>(flexible) + page;
|
||||
Require(sceKernelMapDirectMemory(&high, page, 3, 0x10, phys + page, 0) == 0);
|
||||
Require(GuestArena::GuestArenaSharedBacking_nid_postfix(flexibleAddress, page * 2, &file, &offset) && offset == 0);
|
||||
close(file);
|
||||
Require(sceKernelMapDirectMemory(&high, page, 3, 0x10, phys + page * 2, 0) == 0);
|
||||
Require(!GuestArena::GuestArenaSharedBacking_nid_postfix(flexibleAddress, page * 2, &file, &offset));
|
||||
Require(GuestArena::GuestArenaSharedBacking_nid_postfix(flexibleAddress + page, page, &file, &offset) && offset == page * 2);
|
||||
close(file);
|
||||
Require(sceKernelMunmap(flexible, page * 2) == 0);
|
||||
Require(sceKernelMunmap(first, page * 2) == 0);
|
||||
Require(!GuestArena::GuestArenaSharedBacking_nid_postfix(address, page, &file, &offset));
|
||||
Require(sceKernelReleaseDirectMemory(phys, page * 3) == 0);
|
||||
}
|
||||
|
||||
static void CheckDirectMemoryWriteWatch() {
|
||||
if (!GuestWriteWatch::GuestWriteWatchAvailable_nid_postfix()) {
|
||||
std::puts("write watch unavailable: direct memory not tested");
|
||||
|
|
@ -1016,6 +1059,7 @@ int main() {
|
|||
CheckWriteWatch();
|
||||
CheckDirectMemoryWriteWatch();
|
||||
CheckDirectMemoryBackingNeedsNoFilesystem();
|
||||
CheckDirectMemorySharedBacking();
|
||||
#endif
|
||||
constexpr std::size_t page = 0x4000;
|
||||
const auto failed = reinterpret_cast<void*>(static_cast<std::uintptr_t>(-1));
|
||||
|
|
|
|||
Loading…
Reference in New Issue