Files
Xenia-Canary/src/xenia/base/memory_win.cc

250 lines
7.4 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/base/memory.h"
#include "xenia/base/platform_win.h"
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | \
WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
#define XE_BASE_MEMORY_WIN_USE_DESKTOP_FUNCTIONS
#endif
namespace xe {
namespace memory {
size_t page_size() {
static size_t value = 0;
if (!value) {
SYSTEM_INFO si;
GetSystemInfo(&si);
value = si.dwPageSize;
}
return value;
}
size_t allocation_granularity() {
static size_t value = 0;
if (!value) {
SYSTEM_INFO si;
GetSystemInfo(&si);
value = si.dwAllocationGranularity;
}
return value;
}
DWORD ToWin32ProtectFlags(PageAccess access) {
switch (access) {
case PageAccess::kNoAccess:
return PAGE_NOACCESS;
case PageAccess::kReadOnly:
return PAGE_READONLY;
case PageAccess::kReadWrite:
return PAGE_READWRITE;
case PageAccess::kExecuteReadOnly:
return PAGE_EXECUTE_READ;
case PageAccess::kExecuteReadWrite:
return PAGE_EXECUTE_READWRITE;
default:
assert_unhandled_case(access);
return PAGE_NOACCESS;
}
}
PageAccess ToXeniaProtectFlags(DWORD access) {
if (access & PAGE_GUARD) {
// Strip the page guard flag for now...
access &= ~PAGE_GUARD;
}
switch (access) {
case PAGE_NOACCESS:
return PageAccess::kNoAccess;
case PAGE_READONLY:
return PageAccess::kReadOnly;
case PAGE_READWRITE:
return PageAccess::kReadWrite;
case PAGE_EXECUTE_READ:
return PageAccess::kExecuteReadOnly;
case PAGE_EXECUTE_READWRITE:
return PageAccess::kExecuteReadWrite;
default:
return PageAccess::kNoAccess;
}
}
bool IsWritableExecutableMemorySupported() {
#ifdef XE_BASE_MEMORY_WIN_USE_DESKTOP_FUNCTIONS
return true;
#else
// To test FromApp functions on desktop, undefine
// XE_BASE_MEMORY_WIN_USE_DESKTOP_FUNCTIONS and link to WindowsApp.lib.
return false;
#endif
}
void* AllocFixed(void* base_address, size_t length,
AllocationType allocation_type, PageAccess access) {
DWORD alloc_type = 0;
switch (allocation_type) {
case AllocationType::kReserve:
alloc_type = MEM_RESERVE;
break;
case AllocationType::kCommit:
alloc_type = MEM_COMMIT;
break;
case AllocationType::kReserveCommit:
alloc_type = MEM_RESERVE | MEM_COMMIT;
break;
default:
assert_unhandled_case(allocation_type);
break;
}
DWORD protect = ToWin32ProtectFlags(access);
#ifdef XE_BASE_MEMORY_WIN_USE_DESKTOP_FUNCTIONS
return VirtualAlloc(base_address, length, alloc_type, protect);
#else
return VirtualAllocFromApp(base_address, length, ULONG(alloc_type),
ULONG(protect));
#endif
}
bool DeallocFixed(void* base_address, size_t length,
DeallocationType deallocation_type) {
DWORD free_type = 0;
switch (deallocation_type) {
case DeallocationType::kRelease:
free_type = MEM_RELEASE;
length = 0;
break;
case DeallocationType::kDecommit:
free_type = MEM_DECOMMIT;
break;
default:
assert_unhandled_case(deallocation_type);
break;
}
return VirtualFree(base_address, length, free_type) ? true : false;
}
bool Protect(void* base_address, size_t length, PageAccess access,
PageAccess* out_old_access) {
if (out_old_access) {
*out_old_access = PageAccess::kNoAccess;
}
DWORD new_protect = ToWin32ProtectFlags(access);
#ifdef XE_BASE_MEMORY_WIN_USE_DESKTOP_FUNCTIONS
DWORD old_protect = 0;
BOOL result = VirtualProtect(base_address, length, new_protect, &old_protect);
#else
ULONG old_protect = 0;
BOOL result = VirtualProtectFromApp(base_address, length, ULONG(new_protect),
&old_protect);
#endif
if (!result) {
return false;
}
if (out_old_access) {
*out_old_access = ToXeniaProtectFlags(DWORD(old_protect));
}
return true;
}
bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) {
access_out = PageAccess::kNoAccess;
MEMORY_BASIC_INFORMATION info;
ZeroMemory(&info, sizeof(info));
SIZE_T result = VirtualQuery(base_address, &info, length);
if (!result) {
return false;
}
length = info.RegionSize;
access_out = ToXeniaProtectFlags(info.Protect);
return true;
}
FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
size_t length, PageAccess access,
bool commit) {
DWORD protect =
ToWin32ProtectFlags(access) | (commit ? SEC_COMMIT : SEC_RESERVE);
auto full_path = "Local" / path;
#ifdef XE_BASE_MEMORY_WIN_USE_DESKTOP_FUNCTIONS
return CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, protect,
static_cast<DWORD>(length >> 32),
static_cast<DWORD>(length), full_path.c_str());
#else
return CreateFileMappingFromApp(INVALID_HANDLE_VALUE, nullptr, ULONG(protect),
ULONG64(length), full_path.c_str());
#endif
}
void CloseFileMappingHandle(FileMappingHandle handle,
const std::filesystem::path& path) {
CloseHandle(handle);
}
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
PageAccess access, size_t file_offset) {
#ifdef XE_BASE_MEMORY_WIN_USE_DESKTOP_FUNCTIONS
DWORD target_address_low = static_cast<DWORD>(file_offset);
DWORD target_address_high = static_cast<DWORD>(file_offset >> 32);
DWORD file_access = 0;
switch (access) {
case PageAccess::kReadOnly:
file_access = FILE_MAP_READ;
break;
case PageAccess::kReadWrite:
file_access = FILE_MAP_ALL_ACCESS;
break;
case PageAccess::kExecuteReadOnly:
file_access = FILE_MAP_READ | FILE_MAP_EXECUTE;
break;
case PageAccess::kExecuteReadWrite:
file_access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE;
break;
case PageAccess::kNoAccess:
default:
assert_unhandled_case(access);
return nullptr;
}
return MapViewOfFileEx(handle, file_access, target_address_high,
target_address_low, length, base_address);
#else
// VirtualAlloc2FromApp and MapViewOfFile3FromApp were added in 10.0.17134.0.
// https://docs.microsoft.com/en-us/uwp/win32-and-com/win32-apis
HANDLE process = GetCurrentProcess();
void* placeholder = VirtualAlloc2FromApp(
process, base_address, length, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, nullptr, 0);
if (!placeholder) {
return nullptr;
}
void* mapping = MapViewOfFile3FromApp(
handle, process, placeholder, ULONG64(file_offset), length,
MEM_REPLACE_PLACEHOLDER, ULONG(ToWin32ProtectFlags(access)), nullptr, 0);
if (!mapping) {
VirtualFree(placeholder, length, MEM_RELEASE);
return nullptr;
}
return mapping;
#endif
}
bool UnmapFileView(FileMappingHandle handle, void* base_address,
size_t length) {
return UnmapViewOfFile(base_address) ? true : false;
}
} // namespace memory
} // namespace xe