Merge branch 'master' into vulkan
This commit is contained in:
commit
43d11816b8
|
@ -61,6 +61,9 @@
|
||||||
[submodule "third_party/premake-androidmk"]
|
[submodule "third_party/premake-androidmk"]
|
||||||
path = third_party/premake-androidmk
|
path = third_party/premake-androidmk
|
||||||
url = https://github.com/Triang3l/premake-androidmk.git
|
url = https://github.com/Triang3l/premake-androidmk.git
|
||||||
|
[submodule "third_party/date"]
|
||||||
|
path = third_party/date
|
||||||
|
url = https://github.com/HowardHinnant/date.git
|
||||||
[submodule "third_party/glslang"]
|
[submodule "third_party/glslang"]
|
||||||
path = third_party/glslang
|
path = third_party/glslang
|
||||||
url = https://github.com/KhronosGroup/glslang.git
|
url = https://github.com/KhronosGroup/glslang.git
|
||||||
|
|
|
@ -22,6 +22,11 @@ namespace xe {
|
||||||
|
|
||||||
class Win32MappedMemory : public MappedMemory {
|
class Win32MappedMemory : public MappedMemory {
|
||||||
public:
|
public:
|
||||||
|
// CreateFile returns INVALID_HANDLE_VALUE in case of failure.
|
||||||
|
static constexpr HANDLE kFileHandleInvalid = INVALID_HANDLE_VALUE;
|
||||||
|
// CreateFileMapping returns nullptr in case of failure.
|
||||||
|
static constexpr HANDLE kMappingHandleInvalid = nullptr;
|
||||||
|
|
||||||
Win32MappedMemory(const std::filesystem::path& path, Mode mode)
|
Win32MappedMemory(const std::filesystem::path& path, Mode mode)
|
||||||
: MappedMemory(path, mode) {}
|
: MappedMemory(path, mode) {}
|
||||||
|
|
||||||
|
@ -29,10 +34,10 @@ class Win32MappedMemory : public MappedMemory {
|
||||||
if (data_) {
|
if (data_) {
|
||||||
UnmapViewOfFile(data_);
|
UnmapViewOfFile(data_);
|
||||||
}
|
}
|
||||||
if (mapping_handle != INVALID_HANDLE_VALUE) {
|
if (mapping_handle != kMappingHandleInvalid) {
|
||||||
CloseHandle(mapping_handle);
|
CloseHandle(mapping_handle);
|
||||||
}
|
}
|
||||||
if (file_handle != INVALID_HANDLE_VALUE) {
|
if (file_handle != kFileHandleInvalid) {
|
||||||
CloseHandle(file_handle);
|
CloseHandle(file_handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,11 +47,11 @@ class Win32MappedMemory : public MappedMemory {
|
||||||
UnmapViewOfFile(data_);
|
UnmapViewOfFile(data_);
|
||||||
data_ = nullptr;
|
data_ = nullptr;
|
||||||
}
|
}
|
||||||
if (mapping_handle != INVALID_HANDLE_VALUE) {
|
if (mapping_handle != kMappingHandleInvalid) {
|
||||||
CloseHandle(mapping_handle);
|
CloseHandle(mapping_handle);
|
||||||
mapping_handle = INVALID_HANDLE_VALUE;
|
mapping_handle = kMappingHandleInvalid;
|
||||||
}
|
}
|
||||||
if (file_handle != INVALID_HANDLE_VALUE) {
|
if (file_handle != kFileHandleInvalid) {
|
||||||
if (truncate_size) {
|
if (truncate_size) {
|
||||||
LONG distance_high = truncate_size >> 32;
|
LONG distance_high = truncate_size >> 32;
|
||||||
SetFilePointer(file_handle, truncate_size & 0xFFFFFFFF, &distance_high,
|
SetFilePointer(file_handle, truncate_size & 0xFFFFFFFF, &distance_high,
|
||||||
|
@ -55,7 +60,7 @@ class Win32MappedMemory : public MappedMemory {
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(file_handle);
|
CloseHandle(file_handle);
|
||||||
file_handle = INVALID_HANDLE_VALUE;
|
file_handle = kFileHandleInvalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,8 +70,13 @@ class Win32MappedMemory : public MappedMemory {
|
||||||
size_t aligned_length = length + (offset - aligned_offset);
|
size_t aligned_length = length + (offset - aligned_offset);
|
||||||
|
|
||||||
UnmapViewOfFile(data_);
|
UnmapViewOfFile(data_);
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
data_ = MapViewOfFile(mapping_handle, view_access_, aligned_offset >> 32,
|
data_ = MapViewOfFile(mapping_handle, view_access_, aligned_offset >> 32,
|
||||||
aligned_offset & 0xFFFFFFFF, aligned_length);
|
aligned_offset & 0xFFFFFFFF, aligned_length);
|
||||||
|
#else
|
||||||
|
data_ = MapViewOfFileFromApp(mapping_handle, ULONG(view_access_),
|
||||||
|
ULONG64(aligned_offset), aligned_length);
|
||||||
|
#endif
|
||||||
if (!data_) {
|
if (!data_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -83,8 +93,8 @@ class Win32MappedMemory : public MappedMemory {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLE file_handle = INVALID_HANDLE_VALUE;
|
HANDLE file_handle = kFileHandleInvalid;
|
||||||
HANDLE mapping_handle = INVALID_HANDLE_VALUE;
|
HANDLE mapping_handle = kMappingHandleInvalid;
|
||||||
DWORD view_access_ = 0;
|
DWORD view_access_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -125,20 +135,32 @@ std::unique_ptr<MappedMemory> MappedMemory::Open(
|
||||||
|
|
||||||
mm->file_handle = CreateFile(path.c_str(), file_access, file_share, nullptr,
|
mm->file_handle = CreateFile(path.c_str(), file_access, file_share, nullptr,
|
||||||
create_mode, FILE_ATTRIBUTE_NORMAL, nullptr);
|
create_mode, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
if (mm->file_handle == INVALID_HANDLE_VALUE) {
|
if (mm->file_handle == Win32MappedMemory::kFileHandleInvalid) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
mm->mapping_handle = CreateFileMapping(mm->file_handle, nullptr,
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
mapping_protect, aligned_length >> 32,
|
mm->mapping_handle = CreateFileMapping(
|
||||||
aligned_length & 0xFFFFFFFF, nullptr);
|
mm->file_handle, nullptr, mapping_protect, DWORD(aligned_length >> 32),
|
||||||
if (mm->mapping_handle == INVALID_HANDLE_VALUE) {
|
DWORD(aligned_length), nullptr);
|
||||||
|
#else
|
||||||
|
mm->mapping_handle =
|
||||||
|
CreateFileMappingFromApp(mm->file_handle, nullptr, ULONG(mapping_protect),
|
||||||
|
ULONG64(aligned_length), nullptr);
|
||||||
|
#endif
|
||||||
|
if (mm->mapping_handle == Win32MappedMemory::kMappingHandleInvalid) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
mm->data_ = reinterpret_cast<uint8_t*>(MapViewOfFile(
|
mm->data_ = reinterpret_cast<uint8_t*>(MapViewOfFile(
|
||||||
mm->mapping_handle, view_access, static_cast<DWORD>(aligned_offset >> 32),
|
mm->mapping_handle, view_access, DWORD(aligned_offset >> 32),
|
||||||
static_cast<DWORD>(aligned_offset & 0xFFFFFFFF), aligned_length));
|
DWORD(aligned_offset), aligned_length));
|
||||||
|
#else
|
||||||
|
mm->data_ = reinterpret_cast<uint8_t*>(
|
||||||
|
MapViewOfFileFromApp(mm->mapping_handle, ULONG(view_access),
|
||||||
|
ULONG64(aligned_offset), aligned_length));
|
||||||
|
#endif
|
||||||
if (!mm->data_) {
|
if (!mm->data_) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -203,8 +225,8 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
|
||||||
class Chunk {
|
class Chunk {
|
||||||
public:
|
public:
|
||||||
explicit Chunk(size_t capacity)
|
explicit Chunk(size_t capacity)
|
||||||
: file_handle_(0),
|
: file_handle_(Win32MappedMemory::kFileHandleInvalid),
|
||||||
mapping_handle_(0),
|
mapping_handle_(Win32MappedMemory::kMappingHandleInvalid),
|
||||||
data_(nullptr),
|
data_(nullptr),
|
||||||
offset_(0),
|
offset_(0),
|
||||||
capacity_(capacity),
|
capacity_(capacity),
|
||||||
|
@ -214,10 +236,10 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
|
||||||
if (data_) {
|
if (data_) {
|
||||||
UnmapViewOfFile(data_);
|
UnmapViewOfFile(data_);
|
||||||
}
|
}
|
||||||
if (mapping_handle_) {
|
if (mapping_handle_ != Win32MappedMemory::kMappingHandleInvalid) {
|
||||||
CloseHandle(mapping_handle_);
|
CloseHandle(mapping_handle_);
|
||||||
}
|
}
|
||||||
if (file_handle_) {
|
if (file_handle_ != Win32MappedMemory::kFileHandleInvalid) {
|
||||||
CloseHandle(file_handle_);
|
CloseHandle(file_handle_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -231,14 +253,20 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
|
||||||
|
|
||||||
file_handle_ = CreateFile(path.c_str(), file_access, file_share, nullptr,
|
file_handle_ = CreateFile(path.c_str(), file_access, file_share, nullptr,
|
||||||
create_mode, FILE_ATTRIBUTE_NORMAL, nullptr);
|
create_mode, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
if (!file_handle_) {
|
if (file_handle_ == Win32MappedMemory::kFileHandleInvalid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
mapping_handle_ =
|
mapping_handle_ =
|
||||||
CreateFileMapping(file_handle_, nullptr, mapping_protect, 0,
|
CreateFileMapping(file_handle_, nullptr, mapping_protect,
|
||||||
static_cast<DWORD>(capacity_), nullptr);
|
DWORD(capacity_ >> 32), DWORD(capacity_), nullptr);
|
||||||
if (!mapping_handle_) {
|
#else
|
||||||
|
mapping_handle_ = CreateFileMappingFromApp(file_handle_, nullptr,
|
||||||
|
ULONG(mapping_protect),
|
||||||
|
ULONG64(capacity_), nullptr);
|
||||||
|
#endif
|
||||||
|
if (mapping_handle_ == Win32MappedMemory::kMappingHandleInvalid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,10 +275,32 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
|
||||||
if (low_address_space) {
|
if (low_address_space) {
|
||||||
bool successful = false;
|
bool successful = false;
|
||||||
data_ = reinterpret_cast<uint8_t*>(0x10000000);
|
data_ = reinterpret_cast<uint8_t*>(0x10000000);
|
||||||
|
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
HANDLE process = GetCurrentProcess();
|
||||||
|
#endif
|
||||||
for (int i = 0; i < 1000; ++i) {
|
for (int i = 0; i < 1000; ++i) {
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
if (MapViewOfFileEx(mapping_handle_, view_access, 0, 0, capacity_,
|
if (MapViewOfFileEx(mapping_handle_, view_access, 0, 0, capacity_,
|
||||||
data_)) {
|
data_)) {
|
||||||
successful = true;
|
successful = true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// VirtualAlloc2FromApp and MapViewOfFile3FromApp were added in
|
||||||
|
// 10.0.17134.0.
|
||||||
|
// https://docs.microsoft.com/en-us/uwp/win32-and-com/win32-apis
|
||||||
|
if (VirtualAlloc2FromApp(process, data_, capacity_,
|
||||||
|
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||||
|
PAGE_NOACCESS, nullptr, 0)) {
|
||||||
|
if (MapViewOfFile3FromApp(mapping_handle_, process, data_, 0,
|
||||||
|
capacity_, MEM_REPLACE_PLACEHOLDER,
|
||||||
|
ULONG(mapping_protect), nullptr, 0)) {
|
||||||
|
successful = true;
|
||||||
|
} else {
|
||||||
|
VirtualFree(data_, capacity_, MEM_RELEASE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (successful) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
data_ += capacity_;
|
data_ += capacity_;
|
||||||
|
@ -261,8 +311,13 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
data_ = reinterpret_cast<uint8_t*>(
|
data_ = reinterpret_cast<uint8_t*>(
|
||||||
MapViewOfFile(mapping_handle_, view_access, 0, 0, capacity_));
|
MapViewOfFile(mapping_handle_, view_access, 0, 0, capacity_));
|
||||||
|
#else
|
||||||
|
data_ = reinterpret_cast<uint8_t*>(MapViewOfFileFromApp(
|
||||||
|
mapping_handle_, ULONG(view_access), 0, capacity_));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if (!data_) {
|
if (!data_) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -8,11 +8,26 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/base/memory.h"
|
#include "xenia/base/memory.h"
|
||||||
|
#include "xenia/base/cvar.h"
|
||||||
#include "xenia/base/platform.h"
|
#include "xenia/base/platform.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
DEFINE_bool(
|
||||||
|
writable_executable_memory, true,
|
||||||
|
"Allow mapping memory with both write and execute access, for simulating "
|
||||||
|
"behavior on platforms where that's not supported",
|
||||||
|
"Memory");
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
namespace memory {
|
||||||
|
|
||||||
|
bool IsWritableExecutableMemoryPreferred() {
|
||||||
|
return IsWritableExecutableMemorySupported() &&
|
||||||
|
cvars::writable_executable_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace memory
|
||||||
|
|
||||||
// TODO(benvanik): fancy AVX versions.
|
// TODO(benvanik): fancy AVX versions.
|
||||||
// https://github.com/gnuradio/volk/blob/master/kernels/volk/volk_16u_byteswap.h
|
// https://github.com/gnuradio/volk/blob/master/kernels/volk/volk_16u_byteswap.h
|
||||||
|
|
|
@ -35,6 +35,7 @@ enum class PageAccess {
|
||||||
kNoAccess = 0,
|
kNoAccess = 0,
|
||||||
kReadOnly = 1 << 0,
|
kReadOnly = 1 << 0,
|
||||||
kReadWrite = kReadOnly | 1 << 1,
|
kReadWrite = kReadOnly | 1 << 1,
|
||||||
|
kExecuteReadOnly = kReadOnly | 1 << 2,
|
||||||
kExecuteReadWrite = kReadWrite | 1 << 2,
|
kExecuteReadWrite = kReadWrite | 1 << 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,6 +50,16 @@ enum class DeallocationType {
|
||||||
kDecommit = 1 << 1,
|
kDecommit = 1 << 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Whether the host allows the pages to be allocated or mapped with
|
||||||
|
// PageAccess::kExecuteReadWrite - if not, separate mappings backed by the same
|
||||||
|
// memory-mapped file must be used to write to executable pages.
|
||||||
|
bool IsWritableExecutableMemorySupported();
|
||||||
|
|
||||||
|
// Whether PageAccess::kExecuteReadWrite is a supported and preferred way of
|
||||||
|
// writing executable memory, useful for simulating how Xenia would work without
|
||||||
|
// writable executable memory on a system with it.
|
||||||
|
bool IsWritableExecutableMemoryPreferred();
|
||||||
|
|
||||||
// Allocates a block of memory at the given page-aligned base address.
|
// Allocates a block of memory at the given page-aligned base address.
|
||||||
// Fails if the memory is not available.
|
// Fails if the memory is not available.
|
||||||
// Specify nullptr for base_address to leave it up to the system.
|
// Specify nullptr for base_address to leave it up to the system.
|
||||||
|
|
|
@ -39,6 +39,8 @@ uint32_t ToPosixProtectFlags(PageAccess access) {
|
||||||
return PROT_READ;
|
return PROT_READ;
|
||||||
case PageAccess::kReadWrite:
|
case PageAccess::kReadWrite:
|
||||||
return PROT_READ | PROT_WRITE;
|
return PROT_READ | PROT_WRITE;
|
||||||
|
case PageAccess::kExecuteReadOnly:
|
||||||
|
return PROT_READ | PROT_EXEC;
|
||||||
case PageAccess::kExecuteReadWrite:
|
case PageAccess::kExecuteReadWrite:
|
||||||
return PROT_READ | PROT_WRITE | PROT_EXEC;
|
return PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||||
default:
|
default:
|
||||||
|
@ -47,6 +49,8 @@ uint32_t ToPosixProtectFlags(PageAccess access) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsWritableExecutableMemorySupported() { return true; }
|
||||||
|
|
||||||
void* AllocFixed(void* base_address, size_t length,
|
void* AllocFixed(void* base_address, size_t length,
|
||||||
AllocationType allocation_type, PageAccess access) {
|
AllocationType allocation_type, PageAccess access) {
|
||||||
// mmap does not support reserve / commit, so ignore allocation_type.
|
// mmap does not support reserve / commit, so ignore allocation_type.
|
||||||
|
@ -112,6 +116,7 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
|
||||||
oflag = 0;
|
oflag = 0;
|
||||||
break;
|
break;
|
||||||
case PageAccess::kReadOnly:
|
case PageAccess::kReadOnly:
|
||||||
|
case PageAccess::kExecuteReadOnly:
|
||||||
oflag = O_RDONLY;
|
oflag = O_RDONLY;
|
||||||
break;
|
break;
|
||||||
case PageAccess::kReadWrite:
|
case PageAccess::kReadWrite:
|
||||||
|
|
|
@ -42,6 +42,8 @@ DWORD ToWin32ProtectFlags(PageAccess access) {
|
||||||
return PAGE_READONLY;
|
return PAGE_READONLY;
|
||||||
case PageAccess::kReadWrite:
|
case PageAccess::kReadWrite:
|
||||||
return PAGE_READWRITE;
|
return PAGE_READWRITE;
|
||||||
|
case PageAccess::kExecuteReadOnly:
|
||||||
|
return PAGE_EXECUTE_READ;
|
||||||
case PageAccess::kExecuteReadWrite:
|
case PageAccess::kExecuteReadWrite:
|
||||||
return PAGE_EXECUTE_READWRITE;
|
return PAGE_EXECUTE_READWRITE;
|
||||||
default:
|
default:
|
||||||
|
@ -63,6 +65,8 @@ PageAccess ToXeniaProtectFlags(DWORD access) {
|
||||||
return PageAccess::kReadOnly;
|
return PageAccess::kReadOnly;
|
||||||
case PAGE_READWRITE:
|
case PAGE_READWRITE:
|
||||||
return PageAccess::kReadWrite;
|
return PageAccess::kReadWrite;
|
||||||
|
case PAGE_EXECUTE_READ:
|
||||||
|
return PageAccess::kExecuteReadOnly;
|
||||||
case PAGE_EXECUTE_READWRITE:
|
case PAGE_EXECUTE_READWRITE:
|
||||||
return PageAccess::kExecuteReadWrite;
|
return PageAccess::kExecuteReadWrite;
|
||||||
default:
|
default:
|
||||||
|
@ -70,6 +74,17 @@ PageAccess ToXeniaProtectFlags(DWORD access) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsWritableExecutableMemorySupported() {
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
// To test FromApp functions on desktop, replace
|
||||||
|
// WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) with 0 in the #ifs and
|
||||||
|
// link to WindowsApp.lib.
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void* AllocFixed(void* base_address, size_t length,
|
void* AllocFixed(void* base_address, size_t length,
|
||||||
AllocationType allocation_type, PageAccess access) {
|
AllocationType allocation_type, PageAccess access) {
|
||||||
DWORD alloc_type = 0;
|
DWORD alloc_type = 0;
|
||||||
|
@ -88,7 +103,12 @@ void* AllocFixed(void* base_address, size_t length,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
DWORD protect = ToWin32ProtectFlags(access);
|
DWORD protect = ToWin32ProtectFlags(access);
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
return VirtualAlloc(base_address, length, alloc_type, protect);
|
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,
|
bool DeallocFixed(void* base_address, size_t length,
|
||||||
|
@ -115,13 +135,19 @@ bool Protect(void* base_address, size_t length, PageAccess access,
|
||||||
*out_old_access = PageAccess::kNoAccess;
|
*out_old_access = PageAccess::kNoAccess;
|
||||||
}
|
}
|
||||||
DWORD new_protect = ToWin32ProtectFlags(access);
|
DWORD new_protect = ToWin32ProtectFlags(access);
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
DWORD old_protect = 0;
|
DWORD old_protect = 0;
|
||||||
BOOL result = VirtualProtect(base_address, length, new_protect, &old_protect);
|
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) {
|
if (!result) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (out_old_access) {
|
if (out_old_access) {
|
||||||
*out_old_access = ToXeniaProtectFlags(old_protect);
|
*out_old_access = ToXeniaProtectFlags(DWORD(old_protect));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -148,9 +174,14 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
|
||||||
DWORD protect =
|
DWORD protect =
|
||||||
ToWin32ProtectFlags(access) | (commit ? SEC_COMMIT : SEC_RESERVE);
|
ToWin32ProtectFlags(access) | (commit ? SEC_COMMIT : SEC_RESERVE);
|
||||||
auto full_path = "Local" / path;
|
auto full_path = "Local" / path;
|
||||||
return CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, protect,
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
return CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, protect,
|
||||||
static_cast<DWORD>(length >> 32),
|
static_cast<DWORD>(length >> 32),
|
||||||
static_cast<DWORD>(length), full_path.c_str());
|
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,
|
void CloseFileMappingHandle(FileMappingHandle handle,
|
||||||
|
@ -160,6 +191,7 @@ void CloseFileMappingHandle(FileMappingHandle handle,
|
||||||
|
|
||||||
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
||||||
PageAccess access, size_t file_offset) {
|
PageAccess access, size_t file_offset) {
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
DWORD target_address_low = static_cast<DWORD>(file_offset);
|
DWORD target_address_low = static_cast<DWORD>(file_offset);
|
||||||
DWORD target_address_high = static_cast<DWORD>(file_offset >> 32);
|
DWORD target_address_high = static_cast<DWORD>(file_offset >> 32);
|
||||||
DWORD file_access = 0;
|
DWORD file_access = 0;
|
||||||
|
@ -170,6 +202,9 @@ void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
||||||
case PageAccess::kReadWrite:
|
case PageAccess::kReadWrite:
|
||||||
file_access = FILE_MAP_ALL_ACCESS;
|
file_access = FILE_MAP_ALL_ACCESS;
|
||||||
break;
|
break;
|
||||||
|
case PageAccess::kExecuteReadOnly:
|
||||||
|
file_access = FILE_MAP_READ | FILE_MAP_EXECUTE;
|
||||||
|
break;
|
||||||
case PageAccess::kExecuteReadWrite:
|
case PageAccess::kExecuteReadWrite:
|
||||||
file_access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE;
|
file_access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE;
|
||||||
break;
|
break;
|
||||||
|
@ -180,6 +215,25 @@ void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
||||||
}
|
}
|
||||||
return MapViewOfFileEx(handle, file_access, target_address_high,
|
return MapViewOfFileEx(handle, file_access, target_address_high,
|
||||||
target_address_low, length, base_address);
|
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,
|
bool UnmapFileView(FileMappingHandle handle, void* base_address,
|
||||||
|
|
|
@ -48,6 +48,7 @@ void Initialize(const ANativeActivity* activity) {
|
||||||
dlsym(lib, #name)); \
|
dlsym(lib, #name)); \
|
||||||
assert_not_null(api_functions_.api_##api.name);
|
assert_not_null(api_functions_.api_##api.name);
|
||||||
XE_PLATFORM_ANDROID_LOAD_API_FUNCTION(libandroid, ASharedMemory_create, 26);
|
XE_PLATFORM_ANDROID_LOAD_API_FUNCTION(libandroid, ASharedMemory_create, 26);
|
||||||
|
// pthreads are a part of Bionic libc on Android.
|
||||||
XE_PLATFORM_ANDROID_LOAD_API_FUNCTION(libc, pthread_getname_np, 26);
|
XE_PLATFORM_ANDROID_LOAD_API_FUNCTION(libc, pthread_getname_np, 26);
|
||||||
#undef XE_PLATFORM_ANDROID_LOAD_API_FUNCTION
|
#undef XE_PLATFORM_ANDROID_LOAD_API_FUNCTION
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#ifndef XENIA_CPU_BACKEND_CODE_CACHE_H_
|
#ifndef XENIA_CPU_BACKEND_CODE_CACHE_H_
|
||||||
#define XENIA_CPU_BACKEND_CODE_CACHE_H_
|
#define XENIA_CPU_BACKEND_CODE_CACHE_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "xenia/cpu/function.h"
|
#include "xenia/cpu/function.h"
|
||||||
|
@ -24,8 +26,8 @@ class CodeCache {
|
||||||
virtual ~CodeCache() = default;
|
virtual ~CodeCache() = default;
|
||||||
|
|
||||||
virtual const std::filesystem::path& file_name() const = 0;
|
virtual const std::filesystem::path& file_name() const = 0;
|
||||||
virtual uint32_t base_address() const = 0;
|
virtual uintptr_t execute_base_address() const = 0;
|
||||||
virtual uint32_t total_size() const = 0;
|
virtual size_t total_size() const = 0;
|
||||||
|
|
||||||
// Finds a function based on the given host PC (that may be within a
|
// Finds a function based on the given host PC (that may be within a
|
||||||
// function).
|
// function).
|
||||||
|
|
|
@ -41,8 +41,15 @@ X64CodeCache::~X64CodeCache() {
|
||||||
|
|
||||||
// Unmap all views and close mapping.
|
// Unmap all views and close mapping.
|
||||||
if (mapping_ != xe::memory::kFileMappingHandleInvalid) {
|
if (mapping_ != xe::memory::kFileMappingHandleInvalid) {
|
||||||
xe::memory::UnmapFileView(mapping_, generated_code_base_,
|
if (generated_code_write_base_ &&
|
||||||
|
generated_code_write_base_ != generated_code_execute_base_) {
|
||||||
|
xe::memory::UnmapFileView(mapping_, generated_code_write_base_,
|
||||||
kGeneratedCodeSize);
|
kGeneratedCodeSize);
|
||||||
|
}
|
||||||
|
if (generated_code_execute_base_) {
|
||||||
|
xe::memory::UnmapFileView(mapping_, generated_code_execute_base_,
|
||||||
|
kGeneratedCodeSize);
|
||||||
|
}
|
||||||
xe::memory::CloseFileMappingHandle(mapping_, file_name_);
|
xe::memory::CloseFileMappingHandle(mapping_, file_name_);
|
||||||
mapping_ = xe::memory::kFileMappingHandleInvalid;
|
mapping_ = xe::memory::kFileMappingHandleInvalid;
|
||||||
}
|
}
|
||||||
|
@ -73,18 +80,42 @@ bool X64CodeCache::Initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map generated code region into the file. Pages are committed as required.
|
// Map generated code region into the file. Pages are committed as required.
|
||||||
generated_code_base_ = reinterpret_cast<uint8_t*>(xe::memory::MapFileView(
|
if (xe::memory::IsWritableExecutableMemoryPreferred()) {
|
||||||
mapping_, reinterpret_cast<void*>(kGeneratedCodeBase), kGeneratedCodeSize,
|
generated_code_execute_base_ =
|
||||||
xe::memory::PageAccess::kExecuteReadWrite, 0));
|
reinterpret_cast<uint8_t*>(xe::memory::MapFileView(
|
||||||
if (!generated_code_base_) {
|
mapping_, reinterpret_cast<void*>(kGeneratedCodeExecuteBase),
|
||||||
|
kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadWrite, 0));
|
||||||
|
generated_code_write_base_ = generated_code_execute_base_;
|
||||||
|
if (!generated_code_execute_base_ || !generated_code_write_base_) {
|
||||||
XELOGE("Unable to allocate code cache generated code storage");
|
XELOGE("Unable to allocate code cache generated code storage");
|
||||||
XELOGE(
|
XELOGE(
|
||||||
"This is likely because the {:X}-{:X} range is in use by some other "
|
"This is likely because the {:X}-{:X} range is in use by some other "
|
||||||
"system DLL",
|
"system DLL",
|
||||||
static_cast<uint64_t>(kGeneratedCodeBase),
|
uint64_t(kGeneratedCodeExecuteBase),
|
||||||
kGeneratedCodeBase + kGeneratedCodeSize);
|
uint64_t(kGeneratedCodeExecuteBase + kGeneratedCodeSize));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
generated_code_execute_base_ =
|
||||||
|
reinterpret_cast<uint8_t*>(xe::memory::MapFileView(
|
||||||
|
mapping_, reinterpret_cast<void*>(kGeneratedCodeExecuteBase),
|
||||||
|
kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadOnly, 0));
|
||||||
|
generated_code_write_base_ =
|
||||||
|
reinterpret_cast<uint8_t*>(xe::memory::MapFileView(
|
||||||
|
mapping_, reinterpret_cast<void*>(kGeneratedCodeWriteBase),
|
||||||
|
kGeneratedCodeSize, xe::memory::PageAccess::kReadWrite, 0));
|
||||||
|
if (!generated_code_execute_base_ || !generated_code_write_base_) {
|
||||||
|
XELOGE("Unable to allocate code cache generated code storage");
|
||||||
|
XELOGE(
|
||||||
|
"This is likely because the {:X}-{:X} and {:X}-{:X} ranges are in "
|
||||||
|
"use by some other system DLL",
|
||||||
|
uint64_t(kGeneratedCodeExecuteBase),
|
||||||
|
uint64_t(kGeneratedCodeExecuteBase + kGeneratedCodeSize),
|
||||||
|
uint64_t(kGeneratedCodeWriteBase),
|
||||||
|
uint64_t(kGeneratedCodeWriteBase + kGeneratedCodeSize));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Preallocate the function map to a large, reasonable size.
|
// Preallocate the function map to a large, reasonable size.
|
||||||
generated_code_map_.reserve(kMaximumFunctionCount);
|
generated_code_map_.reserve(kMaximumFunctionCount);
|
||||||
|
@ -117,7 +148,7 @@ void X64CodeCache::CommitExecutableRange(uint32_t guest_low,
|
||||||
xe::memory::AllocFixed(
|
xe::memory::AllocFixed(
|
||||||
indirection_table_base_ + (guest_low - kIndirectionTableBase),
|
indirection_table_base_ + (guest_low - kIndirectionTableBase),
|
||||||
guest_high - guest_low, xe::memory::AllocationType::kCommit,
|
guest_high - guest_low, xe::memory::AllocationType::kCommit,
|
||||||
xe::memory::PageAccess::kExecuteReadWrite);
|
xe::memory::PageAccess::kReadWrite);
|
||||||
|
|
||||||
// Fill memory with the default value.
|
// Fill memory with the default value.
|
||||||
uint32_t* p = reinterpret_cast<uint32_t*>(indirection_table_base_);
|
uint32_t* p = reinterpret_cast<uint32_t*>(indirection_table_base_);
|
||||||
|
@ -126,21 +157,26 @@ void X64CodeCache::CommitExecutableRange(uint32_t guest_low,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* X64CodeCache::PlaceHostCode(uint32_t guest_address, void* machine_code,
|
void X64CodeCache::PlaceHostCode(uint32_t guest_address, void* machine_code,
|
||||||
const EmitFunctionInfo& func_info) {
|
const EmitFunctionInfo& func_info,
|
||||||
|
void*& code_execute_address_out,
|
||||||
|
void*& code_write_address_out) {
|
||||||
// Same for now. We may use different pools or whatnot later on, like when
|
// Same for now. We may use different pools or whatnot later on, like when
|
||||||
// we only want to place guest code in a serialized cache on disk.
|
// we only want to place guest code in a serialized cache on disk.
|
||||||
return PlaceGuestCode(guest_address, machine_code, func_info, nullptr);
|
PlaceGuestCode(guest_address, machine_code, func_info, nullptr,
|
||||||
|
code_execute_address_out, code_write_address_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
void X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||||
const EmitFunctionInfo& func_info,
|
const EmitFunctionInfo& func_info,
|
||||||
GuestFunction* function_info) {
|
GuestFunction* function_info,
|
||||||
|
void*& code_execute_address_out,
|
||||||
|
void*& code_write_address_out) {
|
||||||
// Hold a lock while we bump the pointers up. This is important as the
|
// Hold a lock while we bump the pointers up. This is important as the
|
||||||
// unwind table requires entries AND code to be sorted in order.
|
// unwind table requires entries AND code to be sorted in order.
|
||||||
size_t low_mark;
|
size_t low_mark;
|
||||||
size_t high_mark;
|
size_t high_mark;
|
||||||
uint8_t* code_address;
|
uint8_t* code_execute_address;
|
||||||
UnwindReservation unwind_reservation;
|
UnwindReservation unwind_reservation;
|
||||||
{
|
{
|
||||||
auto global_lock = global_critical_region_.Acquire();
|
auto global_lock = global_critical_region_.Acquire();
|
||||||
|
@ -149,26 +185,33 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||||
|
|
||||||
// Reserve code.
|
// Reserve code.
|
||||||
// Always move the code to land on 16b alignment.
|
// Always move the code to land on 16b alignment.
|
||||||
code_address = generated_code_base_ + generated_code_offset_;
|
code_execute_address =
|
||||||
|
generated_code_execute_base_ + generated_code_offset_;
|
||||||
|
code_execute_address_out = code_execute_address;
|
||||||
|
uint8_t* code_write_address =
|
||||||
|
generated_code_write_base_ + generated_code_offset_;
|
||||||
|
code_write_address_out = code_write_address;
|
||||||
generated_code_offset_ += xe::round_up(func_info.code_size.total, 16);
|
generated_code_offset_ += xe::round_up(func_info.code_size.total, 16);
|
||||||
|
|
||||||
auto tail_address = generated_code_base_ + generated_code_offset_;
|
auto tail_write_address =
|
||||||
|
generated_code_write_base_ + generated_code_offset_;
|
||||||
|
|
||||||
// Reserve unwind info.
|
// Reserve unwind info.
|
||||||
// We go on the high size of the unwind info as we don't know how big we
|
// We go on the high size of the unwind info as we don't know how big we
|
||||||
// need it, and a few extra bytes of padding isn't the worst thing.
|
// need it, and a few extra bytes of padding isn't the worst thing.
|
||||||
unwind_reservation =
|
unwind_reservation = RequestUnwindReservation(generated_code_write_base_ +
|
||||||
RequestUnwindReservation(generated_code_base_ + generated_code_offset_);
|
generated_code_offset_);
|
||||||
generated_code_offset_ += xe::round_up(unwind_reservation.data_size, 16);
|
generated_code_offset_ += xe::round_up(unwind_reservation.data_size, 16);
|
||||||
|
|
||||||
auto end_address = generated_code_base_ + generated_code_offset_;
|
auto end_write_address =
|
||||||
|
generated_code_write_base_ + generated_code_offset_;
|
||||||
|
|
||||||
high_mark = generated_code_offset_;
|
high_mark = generated_code_offset_;
|
||||||
|
|
||||||
// Store in map. It is maintained in sorted order of host PC dependent on
|
// Store in map. It is maintained in sorted order of host PC dependent on
|
||||||
// us also being append-only.
|
// us also being append-only.
|
||||||
generated_code_map_.emplace_back(
|
generated_code_map_.emplace_back(
|
||||||
(uint64_t(code_address - generated_code_base_) << 32) |
|
(uint64_t(code_execute_address - generated_code_execute_base_) << 32) |
|
||||||
generated_code_offset_,
|
generated_code_offset_,
|
||||||
function_info);
|
function_info);
|
||||||
|
|
||||||
|
@ -185,21 +228,30 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||||
if (high_mark <= old_commit_mark) break;
|
if (high_mark <= old_commit_mark) break;
|
||||||
|
|
||||||
new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
|
new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
|
||||||
xe::memory::AllocFixed(generated_code_base_, new_commit_mark,
|
if (generated_code_execute_base_ == generated_code_write_base_) {
|
||||||
|
xe::memory::AllocFixed(generated_code_execute_base_, new_commit_mark,
|
||||||
xe::memory::AllocationType::kCommit,
|
xe::memory::AllocationType::kCommit,
|
||||||
xe::memory::PageAccess::kExecuteReadWrite);
|
xe::memory::PageAccess::kExecuteReadWrite);
|
||||||
|
} else {
|
||||||
|
xe::memory::AllocFixed(generated_code_execute_base_, new_commit_mark,
|
||||||
|
xe::memory::AllocationType::kCommit,
|
||||||
|
xe::memory::PageAccess::kExecuteReadOnly);
|
||||||
|
xe::memory::AllocFixed(generated_code_write_base_, new_commit_mark,
|
||||||
|
xe::memory::AllocationType::kCommit,
|
||||||
|
xe::memory::PageAccess::kReadWrite);
|
||||||
|
}
|
||||||
} while (generated_code_commit_mark_.compare_exchange_weak(
|
} while (generated_code_commit_mark_.compare_exchange_weak(
|
||||||
old_commit_mark, new_commit_mark));
|
old_commit_mark, new_commit_mark));
|
||||||
|
|
||||||
// Copy code.
|
// Copy code.
|
||||||
std::memcpy(code_address, machine_code, func_info.code_size.total);
|
std::memcpy(code_write_address, machine_code, func_info.code_size.total);
|
||||||
|
|
||||||
// Fill unused slots with 0xCC
|
// Fill unused slots with 0xCC
|
||||||
std::memset(tail_address, 0xCC,
|
std::memset(tail_write_address, 0xCC,
|
||||||
static_cast<size_t>(end_address - tail_address));
|
static_cast<size_t>(end_write_address - tail_write_address));
|
||||||
|
|
||||||
// Notify subclasses of placed code.
|
// Notify subclasses of placed code.
|
||||||
PlaceCode(guest_address, machine_code, func_info, code_address,
|
PlaceCode(guest_address, machine_code, func_info, code_execute_address,
|
||||||
unwind_reservation);
|
unwind_reservation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +266,7 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||||
|
|
||||||
iJIT_Method_Load_V2 method = {0};
|
iJIT_Method_Load_V2 method = {0};
|
||||||
method.method_id = iJIT_GetNewMethodID();
|
method.method_id = iJIT_GetNewMethodID();
|
||||||
method.method_load_address = code_address;
|
method.method_load_address = code_execute_address;
|
||||||
method.method_size = uint32_t(code_size);
|
method.method_size = uint32_t(code_size);
|
||||||
method.method_name = const_cast<char*>(method_name.data());
|
method.method_name = const_cast<char*>(method_name.data());
|
||||||
method.module_name = function_info
|
method.module_name = function_info
|
||||||
|
@ -230,10 +282,9 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||||
if (guest_address && indirection_table_base_) {
|
if (guest_address && indirection_table_base_) {
|
||||||
uint32_t* indirection_slot = reinterpret_cast<uint32_t*>(
|
uint32_t* indirection_slot = reinterpret_cast<uint32_t*>(
|
||||||
indirection_table_base_ + (guest_address - kIndirectionTableBase));
|
indirection_table_base_ + (guest_address - kIndirectionTableBase));
|
||||||
*indirection_slot = uint32_t(reinterpret_cast<uint64_t>(code_address));
|
*indirection_slot =
|
||||||
|
uint32_t(reinterpret_cast<uint64_t>(code_execute_address));
|
||||||
}
|
}
|
||||||
|
|
||||||
return code_address;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||||
|
@ -245,7 +296,7 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||||
|
|
||||||
// Reserve code.
|
// Reserve code.
|
||||||
// Always move the code to land on 16b alignment.
|
// Always move the code to land on 16b alignment.
|
||||||
data_address = generated_code_base_ + generated_code_offset_;
|
data_address = generated_code_write_base_ + generated_code_offset_;
|
||||||
generated_code_offset_ += xe::round_up(length, 16);
|
generated_code_offset_ += xe::round_up(length, 16);
|
||||||
|
|
||||||
high_mark = generated_code_offset_;
|
high_mark = generated_code_offset_;
|
||||||
|
@ -260,9 +311,18 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||||
if (high_mark <= old_commit_mark) break;
|
if (high_mark <= old_commit_mark) break;
|
||||||
|
|
||||||
new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
|
new_commit_mark = old_commit_mark + 16 * 1024 * 1024;
|
||||||
xe::memory::AllocFixed(generated_code_base_, new_commit_mark,
|
if (generated_code_execute_base_ == generated_code_write_base_) {
|
||||||
|
xe::memory::AllocFixed(generated_code_execute_base_, new_commit_mark,
|
||||||
xe::memory::AllocationType::kCommit,
|
xe::memory::AllocationType::kCommit,
|
||||||
xe::memory::PageAccess::kExecuteReadWrite);
|
xe::memory::PageAccess::kExecuteReadWrite);
|
||||||
|
} else {
|
||||||
|
xe::memory::AllocFixed(generated_code_execute_base_, new_commit_mark,
|
||||||
|
xe::memory::AllocationType::kCommit,
|
||||||
|
xe::memory::PageAccess::kExecuteReadOnly);
|
||||||
|
xe::memory::AllocFixed(generated_code_write_base_, new_commit_mark,
|
||||||
|
xe::memory::AllocationType::kCommit,
|
||||||
|
xe::memory::PageAccess::kReadWrite);
|
||||||
|
}
|
||||||
} while (generated_code_commit_mark_.compare_exchange_weak(old_commit_mark,
|
} while (generated_code_commit_mark_.compare_exchange_weak(old_commit_mark,
|
||||||
new_commit_mark));
|
new_commit_mark));
|
||||||
|
|
||||||
|
@ -273,7 +333,7 @@ uint32_t X64CodeCache::PlaceData(const void* data, size_t length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
GuestFunction* X64CodeCache::LookupFunction(uint64_t host_pc) {
|
GuestFunction* X64CodeCache::LookupFunction(uint64_t host_pc) {
|
||||||
uint32_t key = uint32_t(host_pc - kGeneratedCodeBase);
|
uint32_t key = uint32_t(host_pc - kGeneratedCodeExecuteBase);
|
||||||
void* fn_entry = std::bsearch(
|
void* fn_entry = std::bsearch(
|
||||||
&key, generated_code_map_.data(), generated_code_map_.size() + 1,
|
&key, generated_code_map_.data(), generated_code_map_.size() + 1,
|
||||||
sizeof(std::pair<uint32_t, Function*>),
|
sizeof(std::pair<uint32_t, Function*>),
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#define XENIA_CPU_BACKEND_X64_X64_CODE_CACHE_H_
|
#define XENIA_CPU_BACKEND_X64_X64_CODE_CACHE_H_
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -46,8 +48,10 @@ class X64CodeCache : public CodeCache {
|
||||||
virtual bool Initialize();
|
virtual bool Initialize();
|
||||||
|
|
||||||
const std::filesystem::path& file_name() const override { return file_name_; }
|
const std::filesystem::path& file_name() const override { return file_name_; }
|
||||||
uint32_t base_address() const override { return kGeneratedCodeBase; }
|
uintptr_t execute_base_address() const override {
|
||||||
uint32_t total_size() const override { return kGeneratedCodeSize; }
|
return kGeneratedCodeExecuteBase;
|
||||||
|
}
|
||||||
|
size_t total_size() const override { return kGeneratedCodeSize; }
|
||||||
|
|
||||||
// TODO(benvanik): ELF serialization/etc
|
// TODO(benvanik): ELF serialization/etc
|
||||||
// TODO(benvanik): keep track of code blocks
|
// TODO(benvanik): keep track of code blocks
|
||||||
|
@ -59,11 +63,15 @@ class X64CodeCache : public CodeCache {
|
||||||
|
|
||||||
void CommitExecutableRange(uint32_t guest_low, uint32_t guest_high);
|
void CommitExecutableRange(uint32_t guest_low, uint32_t guest_high);
|
||||||
|
|
||||||
void* PlaceHostCode(uint32_t guest_address, void* machine_code,
|
void PlaceHostCode(uint32_t guest_address, void* machine_code,
|
||||||
const EmitFunctionInfo& func_info);
|
|
||||||
void* PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
|
||||||
const EmitFunctionInfo& func_info,
|
const EmitFunctionInfo& func_info,
|
||||||
GuestFunction* function_info);
|
void*& code_execute_address_out,
|
||||||
|
void*& code_write_address_out);
|
||||||
|
void PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||||
|
const EmitFunctionInfo& func_info,
|
||||||
|
GuestFunction* function_info,
|
||||||
|
void*& code_execute_address_out,
|
||||||
|
void*& code_write_address_out);
|
||||||
uint32_t PlaceData(const void* data, size_t length);
|
uint32_t PlaceData(const void* data, size_t length);
|
||||||
|
|
||||||
GuestFunction* LookupFunction(uint64_t host_pc) override;
|
GuestFunction* LookupFunction(uint64_t host_pc) override;
|
||||||
|
@ -71,13 +79,16 @@ class X64CodeCache : public CodeCache {
|
||||||
protected:
|
protected:
|
||||||
// All executable code falls within 0x80000000 to 0x9FFFFFFF, so we can
|
// All executable code falls within 0x80000000 to 0x9FFFFFFF, so we can
|
||||||
// only map enough for lookups within that range.
|
// only map enough for lookups within that range.
|
||||||
static const uint64_t kIndirectionTableBase = 0x80000000;
|
static const size_t kIndirectionTableSize = 0x1FFFFFFF;
|
||||||
static const uint64_t kIndirectionTableSize = 0x1FFFFFFF;
|
static const uintptr_t kIndirectionTableBase = 0x80000000;
|
||||||
// The code range is 512MB, but we know the total code games will have is
|
// The code range is 512MB, but we know the total code games will have is
|
||||||
// pretty small (dozens of mb at most) and our expansion is reasonablish
|
// pretty small (dozens of mb at most) and our expansion is reasonablish
|
||||||
// so 256MB should be more than enough.
|
// so 256MB should be more than enough.
|
||||||
static const uint64_t kGeneratedCodeBase = 0xA0000000;
|
static const size_t kGeneratedCodeSize = 0x0FFFFFFF;
|
||||||
static const uint64_t kGeneratedCodeSize = 0x0FFFFFFF;
|
static const uintptr_t kGeneratedCodeExecuteBase = 0xA0000000;
|
||||||
|
// Used for writing when PageAccess::kExecuteReadWrite is not supported.
|
||||||
|
static const uintptr_t kGeneratedCodeWriteBase =
|
||||||
|
kGeneratedCodeExecuteBase + kGeneratedCodeSize + 1;
|
||||||
|
|
||||||
// This is picked to be high enough to cover whatever we can reasonably
|
// This is picked to be high enough to cover whatever we can reasonably
|
||||||
// expect. If we hit issues with this it probably means some corner case
|
// expect. If we hit issues with this it probably means some corner case
|
||||||
|
@ -96,7 +107,8 @@ class X64CodeCache : public CodeCache {
|
||||||
return UnwindReservation();
|
return UnwindReservation();
|
||||||
}
|
}
|
||||||
virtual void PlaceCode(uint32_t guest_address, void* machine_code,
|
virtual void PlaceCode(uint32_t guest_address, void* machine_code,
|
||||||
const EmitFunctionInfo& func_info, void* code_address,
|
const EmitFunctionInfo& func_info,
|
||||||
|
void* code_execute_address,
|
||||||
UnwindReservation unwind_reservation) {}
|
UnwindReservation unwind_reservation) {}
|
||||||
|
|
||||||
std::filesystem::path file_name_;
|
std::filesystem::path file_name_;
|
||||||
|
@ -114,9 +126,13 @@ class X64CodeCache : public CodeCache {
|
||||||
// the generated code table that correspond to the PPC functions in guest
|
// the generated code table that correspond to the PPC functions in guest
|
||||||
// space.
|
// space.
|
||||||
uint8_t* indirection_table_base_ = nullptr;
|
uint8_t* indirection_table_base_ = nullptr;
|
||||||
// Fixed at kGeneratedCodeBase and holding all generated code, growing as
|
// Fixed at kGeneratedCodeExecuteBase and holding all generated code, growing
|
||||||
// needed.
|
// as needed.
|
||||||
uint8_t* generated_code_base_ = nullptr;
|
uint8_t* generated_code_execute_base_ = nullptr;
|
||||||
|
// View of the memory that backs generated_code_execute_base_ when
|
||||||
|
// PageAccess::kExecuteReadWrite is not supported, for writing the generated
|
||||||
|
// code. Equals to generated_code_execute_base_ when it's supported.
|
||||||
|
uint8_t* generated_code_write_base_ = nullptr;
|
||||||
// Current offset to empty space in generated code.
|
// Current offset to empty space in generated code.
|
||||||
size_t generated_code_offset_ = 0;
|
size_t generated_code_offset_ = 0;
|
||||||
// Current high water mark of COMMITTED code.
|
// Current high water mark of COMMITTED code.
|
||||||
|
|
|
@ -27,7 +27,7 @@ class PosixX64CodeCache : public X64CodeCache {
|
||||||
/*
|
/*
|
||||||
UnwindReservation RequestUnwindReservation(uint8_t* entry_address) override;
|
UnwindReservation RequestUnwindReservation(uint8_t* entry_address) override;
|
||||||
void PlaceCode(uint32_t guest_address, void* machine_code, size_t code_size,
|
void PlaceCode(uint32_t guest_address, void* machine_code, size_t code_size,
|
||||||
size_t stack_size, void* code_address,
|
size_t stack_size, void* code_execute_address,
|
||||||
UnwindReservation unwind_reservation) override;
|
UnwindReservation unwind_reservation) override;
|
||||||
|
|
||||||
void InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
void InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||||
|
|
|
@ -107,11 +107,12 @@ class Win32X64CodeCache : public X64CodeCache {
|
||||||
private:
|
private:
|
||||||
UnwindReservation RequestUnwindReservation(uint8_t* entry_address) override;
|
UnwindReservation RequestUnwindReservation(uint8_t* entry_address) override;
|
||||||
void PlaceCode(uint32_t guest_address, void* machine_code,
|
void PlaceCode(uint32_t guest_address, void* machine_code,
|
||||||
const EmitFunctionInfo& func_info, void* code_address,
|
const EmitFunctionInfo& func_info, void* code_execute_address,
|
||||||
UnwindReservation unwind_reservation) override;
|
UnwindReservation unwind_reservation) override;
|
||||||
|
|
||||||
void InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
void InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||||
size_t unwind_table_slot, void* code_address,
|
size_t unwind_table_slot,
|
||||||
|
void* code_execute_address,
|
||||||
const EmitFunctionInfo& func_info);
|
const EmitFunctionInfo& func_info);
|
||||||
|
|
||||||
// Growable function table system handle.
|
// Growable function table system handle.
|
||||||
|
@ -140,9 +141,9 @@ Win32X64CodeCache::~Win32X64CodeCache() {
|
||||||
delete_growable_table_(unwind_table_handle_);
|
delete_growable_table_(unwind_table_handle_);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (generated_code_base_) {
|
if (generated_code_execute_base_) {
|
||||||
RtlDeleteFunctionTable(reinterpret_cast<PRUNTIME_FUNCTION>(
|
RtlDeleteFunctionTable(reinterpret_cast<PRUNTIME_FUNCTION>(
|
||||||
reinterpret_cast<DWORD64>(generated_code_base_) | 0x3));
|
reinterpret_cast<DWORD64>(generated_code_execute_base_) | 0x3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,10 +177,11 @@ bool Win32X64CodeCache::Initialize() {
|
||||||
// Create table and register with the system. It's empty now, but we'll grow
|
// Create table and register with the system. It's empty now, but we'll grow
|
||||||
// it as functions are added.
|
// it as functions are added.
|
||||||
if (supports_growable_table_) {
|
if (supports_growable_table_) {
|
||||||
if (add_growable_table_(&unwind_table_handle_, unwind_table_.data(),
|
if (add_growable_table_(
|
||||||
unwind_table_count_, DWORD(unwind_table_.size()),
|
&unwind_table_handle_, unwind_table_.data(), unwind_table_count_,
|
||||||
reinterpret_cast<ULONG_PTR>(generated_code_base_),
|
DWORD(unwind_table_.size()),
|
||||||
reinterpret_cast<ULONG_PTR>(generated_code_base_ +
|
reinterpret_cast<ULONG_PTR>(generated_code_execute_base_),
|
||||||
|
reinterpret_cast<ULONG_PTR>(generated_code_execute_base_ +
|
||||||
kGeneratedCodeSize))) {
|
kGeneratedCodeSize))) {
|
||||||
XELOGE("Unable to create unwind function table");
|
XELOGE("Unable to create unwind function table");
|
||||||
return false;
|
return false;
|
||||||
|
@ -188,8 +190,9 @@ bool Win32X64CodeCache::Initialize() {
|
||||||
// Install a callback that the debugger will use to lookup unwind info on
|
// Install a callback that the debugger will use to lookup unwind info on
|
||||||
// demand.
|
// demand.
|
||||||
if (!RtlInstallFunctionTableCallback(
|
if (!RtlInstallFunctionTableCallback(
|
||||||
reinterpret_cast<DWORD64>(generated_code_base_) | 0x3,
|
reinterpret_cast<DWORD64>(generated_code_execute_base_) | 0x3,
|
||||||
reinterpret_cast<DWORD64>(generated_code_base_), kGeneratedCodeSize,
|
reinterpret_cast<DWORD64>(generated_code_execute_base_),
|
||||||
|
kGeneratedCodeSize,
|
||||||
[](DWORD64 control_pc, PVOID context) {
|
[](DWORD64 control_pc, PVOID context) {
|
||||||
auto code_cache = reinterpret_cast<Win32X64CodeCache*>(context);
|
auto code_cache = reinterpret_cast<Win32X64CodeCache*>(context);
|
||||||
return reinterpret_cast<PRUNTIME_FUNCTION>(
|
return reinterpret_cast<PRUNTIME_FUNCTION>(
|
||||||
|
@ -216,11 +219,12 @@ Win32X64CodeCache::RequestUnwindReservation(uint8_t* entry_address) {
|
||||||
|
|
||||||
void Win32X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
void Win32X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
||||||
const EmitFunctionInfo& func_info,
|
const EmitFunctionInfo& func_info,
|
||||||
void* code_address,
|
void* code_execute_address,
|
||||||
UnwindReservation unwind_reservation) {
|
UnwindReservation unwind_reservation) {
|
||||||
// Add unwind info.
|
// Add unwind info.
|
||||||
InitializeUnwindEntry(unwind_reservation.entry_address,
|
InitializeUnwindEntry(unwind_reservation.entry_address,
|
||||||
unwind_reservation.table_slot, code_address, func_info);
|
unwind_reservation.table_slot, code_execute_address,
|
||||||
|
func_info);
|
||||||
|
|
||||||
if (supports_growable_table_) {
|
if (supports_growable_table_) {
|
||||||
// Notify that the unwind table has grown.
|
// Notify that the unwind table has grown.
|
||||||
|
@ -229,13 +233,15 @@ void Win32X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This isn't needed on x64 (probably), but is convention.
|
// This isn't needed on x64 (probably), but is convention.
|
||||||
FlushInstructionCache(GetCurrentProcess(), code_address,
|
// On UWP, FlushInstructionCache available starting from 10.0.16299.0.
|
||||||
|
// https://docs.microsoft.com/en-us/uwp/win32-and-com/win32-apis
|
||||||
|
FlushInstructionCache(GetCurrentProcess(), code_execute_address,
|
||||||
func_info.code_size.total);
|
func_info.code_size.total);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Win32X64CodeCache::InitializeUnwindEntry(
|
void Win32X64CodeCache::InitializeUnwindEntry(
|
||||||
uint8_t* unwind_entry_address, size_t unwind_table_slot, void* code_address,
|
uint8_t* unwind_entry_address, size_t unwind_table_slot,
|
||||||
const EmitFunctionInfo& func_info) {
|
void* code_execute_address, const EmitFunctionInfo& func_info) {
|
||||||
auto unwind_info = reinterpret_cast<UNWIND_INFO*>(unwind_entry_address);
|
auto unwind_info = reinterpret_cast<UNWIND_INFO*>(unwind_entry_address);
|
||||||
UNWIND_CODE* unwind_code = nullptr;
|
UNWIND_CODE* unwind_code = nullptr;
|
||||||
|
|
||||||
|
@ -299,10 +305,12 @@ void Win32X64CodeCache::InitializeUnwindEntry(
|
||||||
// Add entry.
|
// Add entry.
|
||||||
auto& fn_entry = unwind_table_[unwind_table_slot];
|
auto& fn_entry = unwind_table_[unwind_table_slot];
|
||||||
fn_entry.BeginAddress =
|
fn_entry.BeginAddress =
|
||||||
(DWORD)(reinterpret_cast<uint8_t*>(code_address) - generated_code_base_);
|
DWORD(reinterpret_cast<uint8_t*>(code_execute_address) -
|
||||||
|
generated_code_execute_base_);
|
||||||
fn_entry.EndAddress =
|
fn_entry.EndAddress =
|
||||||
(DWORD)(fn_entry.BeginAddress + func_info.code_size.total);
|
DWORD(fn_entry.BeginAddress + func_info.code_size.total);
|
||||||
fn_entry.UnwindData = (DWORD)(unwind_entry_address - generated_code_base_);
|
fn_entry.UnwindData =
|
||||||
|
DWORD(unwind_entry_address - generated_code_execute_base_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* Win32X64CodeCache::LookupUnwindInfo(uint64_t host_pc) {
|
void* Win32X64CodeCache::LookupUnwindInfo(uint64_t host_pc) {
|
||||||
|
@ -310,8 +318,8 @@ void* Win32X64CodeCache::LookupUnwindInfo(uint64_t host_pc) {
|
||||||
&host_pc, unwind_table_.data(), unwind_table_count_,
|
&host_pc, unwind_table_.data(), unwind_table_count_,
|
||||||
sizeof(RUNTIME_FUNCTION),
|
sizeof(RUNTIME_FUNCTION),
|
||||||
[](const void* key_ptr, const void* element_ptr) {
|
[](const void* key_ptr, const void* element_ptr) {
|
||||||
auto key =
|
auto key = *reinterpret_cast<const uintptr_t*>(key_ptr) -
|
||||||
*reinterpret_cast<const uintptr_t*>(key_ptr) - kGeneratedCodeBase;
|
kGeneratedCodeExecuteBase;
|
||||||
auto element = reinterpret_cast<const RUNTIME_FUNCTION*>(element_ptr);
|
auto element = reinterpret_cast<const RUNTIME_FUNCTION*>(element_ptr);
|
||||||
if (key < element->BeginAddress) {
|
if (key < element->BeginAddress) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -125,20 +125,26 @@ void* X64Emitter::Emplace(const EmitFunctionInfo& func_info,
|
||||||
// top_ points to the Xbyak buffer, and since we are in AutoGrow mode
|
// top_ points to the Xbyak buffer, and since we are in AutoGrow mode
|
||||||
// it has pending relocations. We copy the top_ to our buffer, swap the
|
// it has pending relocations. We copy the top_ to our buffer, swap the
|
||||||
// pointer, relocate, then return the original scratch pointer for use.
|
// pointer, relocate, then return the original scratch pointer for use.
|
||||||
|
// top_ is used by Xbyak's ready() as both write base pointer and the absolute
|
||||||
|
// address base, which would not work on platforms not supporting writable
|
||||||
|
// executable memory, but Xenia doesn't use absolute label addresses in the
|
||||||
|
// generated code.
|
||||||
uint8_t* old_address = top_;
|
uint8_t* old_address = top_;
|
||||||
void* new_address;
|
void* new_execute_address;
|
||||||
|
void* new_write_address;
|
||||||
assert_true(func_info.code_size.total == size_);
|
assert_true(func_info.code_size.total == size_);
|
||||||
if (function) {
|
if (function) {
|
||||||
new_address = code_cache_->PlaceGuestCode(function->address(), top_,
|
code_cache_->PlaceGuestCode(function->address(), top_, func_info, function,
|
||||||
func_info, function);
|
new_execute_address, new_write_address);
|
||||||
} else {
|
} else {
|
||||||
new_address = code_cache_->PlaceHostCode(0, top_, func_info);
|
code_cache_->PlaceHostCode(0, top_, func_info, new_execute_address,
|
||||||
|
new_write_address);
|
||||||
}
|
}
|
||||||
top_ = reinterpret_cast<uint8_t*>(new_address);
|
top_ = reinterpret_cast<uint8_t*>(new_write_address);
|
||||||
ready();
|
ready();
|
||||||
top_ = old_address;
|
top_ = old_address;
|
||||||
reset();
|
reset();
|
||||||
return new_address;
|
return new_execute_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
|
bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
|
||||||
|
|
|
@ -177,6 +177,9 @@ class TestRunner {
|
||||||
public:
|
public:
|
||||||
TestRunner() {
|
TestRunner() {
|
||||||
memory_size_ = 64 * 1024 * 1024;
|
memory_size_ = 64 * 1024 * 1024;
|
||||||
|
// FIXME(Triang3l): If this is ever compiled for a platform without
|
||||||
|
// xe::memory::IsWritableExecutableMemorySupported, two memory mappings must
|
||||||
|
// be used.
|
||||||
memory_ = memory::AllocFixed(nullptr, memory_size_,
|
memory_ = memory::AllocFixed(nullptr, memory_size_,
|
||||||
memory::AllocationType::kReserveCommit,
|
memory::AllocationType::kReserveCommit,
|
||||||
memory::PageAccess::kExecuteReadWrite);
|
memory::PageAccess::kExecuteReadWrite);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "xenia/cpu/stack_walker.h"
|
#include "xenia/cpu/stack_walker.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
@ -120,8 +121,8 @@ class Win32StackWalker : public StackWalker {
|
||||||
// They never change, so it's fine even if they are touched from multiple
|
// They never change, so it's fine even if they are touched from multiple
|
||||||
// threads.
|
// threads.
|
||||||
code_cache_ = code_cache;
|
code_cache_ = code_cache;
|
||||||
code_cache_min_ = code_cache_->base_address();
|
code_cache_min_ = code_cache_->execute_base_address();
|
||||||
code_cache_max_ = code_cache_->base_address() + code_cache_->total_size();
|
code_cache_max_ = code_cache_min_ + code_cache_->total_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Initialize() {
|
bool Initialize() {
|
||||||
|
@ -297,13 +298,13 @@ class Win32StackWalker : public StackWalker {
|
||||||
std::mutex dbghelp_mutex_;
|
std::mutex dbghelp_mutex_;
|
||||||
|
|
||||||
static xe::cpu::backend::CodeCache* code_cache_;
|
static xe::cpu::backend::CodeCache* code_cache_;
|
||||||
static uint32_t code_cache_min_;
|
static uintptr_t code_cache_min_;
|
||||||
static uint32_t code_cache_max_;
|
static uintptr_t code_cache_max_;
|
||||||
};
|
};
|
||||||
|
|
||||||
xe::cpu::backend::CodeCache* Win32StackWalker::code_cache_ = nullptr;
|
xe::cpu::backend::CodeCache* Win32StackWalker::code_cache_ = nullptr;
|
||||||
uint32_t Win32StackWalker::code_cache_min_ = 0;
|
uintptr_t Win32StackWalker::code_cache_min_ = 0;
|
||||||
uint32_t Win32StackWalker::code_cache_max_ = 0;
|
uintptr_t Win32StackWalker::code_cache_max_ = 0;
|
||||||
|
|
||||||
std::unique_ptr<StackWalker> StackWalker::Create(
|
std::unique_ptr<StackWalker> StackWalker::Create(
|
||||||
backend::CodeCache* code_cache) {
|
backend::CodeCache* code_cache) {
|
||||||
|
|
|
@ -1454,7 +1454,7 @@ void DebugWindow::UpdateCache() {
|
||||||
// Fetch module listing.
|
// Fetch module listing.
|
||||||
// We hold refs so that none are unloaded.
|
// We hold refs so that none are unloaded.
|
||||||
cache_.modules =
|
cache_.modules =
|
||||||
object_table->GetObjectsByType<XModule>(XObject::Type::kTypeModule);
|
object_table->GetObjectsByType<XModule>(XObject::Type::Module);
|
||||||
|
|
||||||
cache_.thread_debug_infos = processor_->QueryThreadDebugInfos();
|
cache_.thread_debug_infos = processor_->QueryThreadDebugInfos();
|
||||||
|
|
||||||
|
|
|
@ -358,7 +358,7 @@ void Emulator::Pause() {
|
||||||
auto lock = global_critical_region::AcquireDirect();
|
auto lock = global_critical_region::AcquireDirect();
|
||||||
auto threads =
|
auto threads =
|
||||||
kernel_state()->object_table()->GetObjectsByType<kernel::XThread>(
|
kernel_state()->object_table()->GetObjectsByType<kernel::XThread>(
|
||||||
kernel::XObject::kTypeThread);
|
kernel::XObject::Type::Thread);
|
||||||
auto current_thread = kernel::XThread::IsInThread()
|
auto current_thread = kernel::XThread::IsInThread()
|
||||||
? kernel::XThread::GetCurrentThread()
|
? kernel::XThread::GetCurrentThread()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
@ -388,7 +388,7 @@ void Emulator::Resume() {
|
||||||
|
|
||||||
auto threads =
|
auto threads =
|
||||||
kernel_state()->object_table()->GetObjectsByType<kernel::XThread>(
|
kernel_state()->object_table()->GetObjectsByType<kernel::XThread>(
|
||||||
kernel::XObject::kTypeThread);
|
kernel::XObject::Type::Thread);
|
||||||
for (auto thread : threads) {
|
for (auto thread : threads) {
|
||||||
if (!thread->can_debugger_suspend()) {
|
if (!thread->can_debugger_suspend()) {
|
||||||
// Don't pause host threads.
|
// Don't pause host threads.
|
||||||
|
@ -513,7 +513,7 @@ bool Emulator::ExceptionCallbackThunk(Exception* ex, void* data) {
|
||||||
bool Emulator::ExceptionCallback(Exception* ex) {
|
bool Emulator::ExceptionCallback(Exception* ex) {
|
||||||
// Check to see if the exception occurred in guest code.
|
// Check to see if the exception occurred in guest code.
|
||||||
auto code_cache = processor()->backend()->code_cache();
|
auto code_cache = processor()->backend()->code_cache();
|
||||||
auto code_base = code_cache->base_address();
|
auto code_base = code_cache->execute_base_address();
|
||||||
auto code_end = code_base + code_cache->total_size();
|
auto code_end = code_base + code_cache->total_size();
|
||||||
|
|
||||||
if (!processor()->is_debugger_attached() && debugging::IsDebuggerAttached()) {
|
if (!processor()->is_debugger_attached() && debugging::IsDebuggerAttached()) {
|
||||||
|
|
|
@ -763,13 +763,13 @@ bool KernelState::Save(ByteStream* stream) {
|
||||||
for (auto object : objects) {
|
for (auto object : objects) {
|
||||||
auto prev_offset = stream->offset();
|
auto prev_offset = stream->offset();
|
||||||
|
|
||||||
if (object->is_host_object() || object->type() == XObject::kTypeThread) {
|
if (object->is_host_object() || object->type() == XObject::Type::Thread) {
|
||||||
// Don't save host objects or save XThreads again
|
// Don't save host objects or save XThreads again
|
||||||
num_objects--;
|
num_objects--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->Write<uint32_t>(object->type());
|
stream->Write<uint32_t>(static_cast<uint32_t>(object->type()));
|
||||||
if (!object->Save(stream)) {
|
if (!object->Save(stream)) {
|
||||||
XELOGD("Did not save object of type {}", object->type());
|
XELOGD("Did not save object of type {}", object->type());
|
||||||
assert_always();
|
assert_always();
|
||||||
|
@ -804,7 +804,7 @@ bool KernelState::Restore(ByteStream* stream) {
|
||||||
uint32_t num_threads = stream->Read<uint32_t>();
|
uint32_t num_threads = stream->Read<uint32_t>();
|
||||||
XELOGD("Loading {} threads...", num_threads);
|
XELOGD("Loading {} threads...", num_threads);
|
||||||
for (uint32_t i = 0; i < num_threads; i++) {
|
for (uint32_t i = 0; i < num_threads; i++) {
|
||||||
auto thread = XObject::Restore(this, XObject::kTypeThread, stream);
|
auto thread = XObject::Restore(this, XObject::Type::Thread, stream);
|
||||||
if (!thread) {
|
if (!thread) {
|
||||||
// Can't continue the restore or we risk misalignment.
|
// Can't continue the restore or we risk misalignment.
|
||||||
assert_always();
|
assert_always();
|
||||||
|
|
|
@ -51,7 +51,7 @@ class ObjectTable {
|
||||||
object_ref<T> LookupObject(X_HANDLE handle) {
|
object_ref<T> LookupObject(X_HANDLE handle) {
|
||||||
auto object = LookupObject(handle, false);
|
auto object = LookupObject(handle, false);
|
||||||
if (object) {
|
if (object) {
|
||||||
assert_true(object->type() == T::kType);
|
assert_true(object->type() == T::kObjectType);
|
||||||
}
|
}
|
||||||
auto result = object_ref<T>(reinterpret_cast<T*>(object));
|
auto result = object_ref<T>(reinterpret_cast<T*>(object));
|
||||||
return result;
|
return result;
|
||||||
|
@ -72,7 +72,7 @@ class ObjectTable {
|
||||||
std::vector<object_ref<T>> GetObjectsByType() {
|
std::vector<object_ref<T>> GetObjectsByType() {
|
||||||
std::vector<object_ref<T>> results;
|
std::vector<object_ref<T>> results;
|
||||||
GetObjectsByType(
|
GetObjectsByType(
|
||||||
T::kType,
|
T::kObjectType,
|
||||||
reinterpret_cast<std::vector<object_ref<XObject>>*>(&results));
|
reinterpret_cast<std::vector<object_ref<XObject>>*>(&results));
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,15 @@ void HandleSetThreadName(pointer_t<X_EXCEPTION_RECORD> record) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto name =
|
// Shadowrun (and its demo) has a bug where it ends up passing freed memory
|
||||||
kernel_memory()->TranslateVirtual<const char*>(thread_info->name_ptr);
|
// for the name, so at the point of SetThreadName it's filled with junk.
|
||||||
|
|
||||||
|
// TODO(gibbed): cvar for thread name encoding for conversion, some games use
|
||||||
|
// SJIS and there's no way to automatically know this.
|
||||||
|
auto name = std::string(
|
||||||
|
kernel_memory()->TranslateVirtual<const char*>(thread_info->name_ptr));
|
||||||
|
std::replace_if(
|
||||||
|
name.begin(), name.end(), [](auto c) { return c < 32 || c > 127; }, '?');
|
||||||
|
|
||||||
object_ref<XThread> thread;
|
object_ref<XThread> thread;
|
||||||
if (thread_info->thread_id == -1) {
|
if (thread_info->thread_id == -1) {
|
||||||
|
|
|
@ -131,7 +131,7 @@ dword_result_t NtCreateFile(lpdword_t handle_out, dword_t desired_access,
|
||||||
auto root_file = kernel_state()->object_table()->LookupObject<XFile>(
|
auto root_file = kernel_state()->object_table()->LookupObject<XFile>(
|
||||||
object_attrs->root_directory);
|
object_attrs->root_directory);
|
||||||
assert_not_null(root_file);
|
assert_not_null(root_file);
|
||||||
assert_true(root_file->type() == XObject::Type::kTypeFile);
|
assert_true(root_file->type() == XObject::Type::File);
|
||||||
|
|
||||||
root_entry = root_file->entry();
|
root_entry = root_file->entry();
|
||||||
}
|
}
|
||||||
|
@ -399,7 +399,7 @@ dword_result_t NtQueryFullAttributesFile(
|
||||||
root_file = kernel_state()->object_table()->LookupObject<XFile>(
|
root_file = kernel_state()->object_table()->LookupObject<XFile>(
|
||||||
obj_attribs->root_directory);
|
obj_attribs->root_directory);
|
||||||
assert_not_null(root_file);
|
assert_not_null(root_file);
|
||||||
assert_true(root_file->type() == XObject::Type::kTypeFile);
|
assert_true(root_file->type() == XObject::Type::File);
|
||||||
assert_always();
|
assert_always();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,9 @@ dword_result_t NtAllocateVirtualMemory(lpdword_t base_addr_ptr,
|
||||||
if (*base_addr_ptr != 0) {
|
if (*base_addr_ptr != 0) {
|
||||||
// ignore specified page size when base address is specified.
|
// ignore specified page size when base address is specified.
|
||||||
auto heap = kernel_memory()->LookupHeap(*base_addr_ptr);
|
auto heap = kernel_memory()->LookupHeap(*base_addr_ptr);
|
||||||
|
if (heap->heap_type() != HeapType::kGuestVirtual) {
|
||||||
|
return X_STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
page_size = heap->page_size();
|
page_size = heap->page_size();
|
||||||
} else {
|
} else {
|
||||||
// Adjust size.
|
// Adjust size.
|
||||||
|
@ -192,7 +195,9 @@ dword_result_t NtProtectVirtualMemory(lpdword_t base_addr_ptr,
|
||||||
}
|
}
|
||||||
|
|
||||||
auto heap = kernel_memory()->LookupHeap(*base_addr_ptr);
|
auto heap = kernel_memory()->LookupHeap(*base_addr_ptr);
|
||||||
|
if (heap->heap_type() != HeapType::kGuestVirtual) {
|
||||||
|
return X_STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
// Adjust the base downwards to the nearest page boundary.
|
// Adjust the base downwards to the nearest page boundary.
|
||||||
uint32_t adjusted_base =
|
uint32_t adjusted_base =
|
||||||
*base_addr_ptr - (*base_addr_ptr % heap->page_size());
|
*base_addr_ptr - (*base_addr_ptr % heap->page_size());
|
||||||
|
@ -240,6 +245,9 @@ dword_result_t NtFreeVirtualMemory(lpdword_t base_addr_ptr,
|
||||||
}
|
}
|
||||||
|
|
||||||
auto heap = kernel_state()->memory()->LookupHeap(base_addr_value);
|
auto heap = kernel_state()->memory()->LookupHeap(base_addr_value);
|
||||||
|
if (heap->heap_type() != HeapType::kGuestVirtual) {
|
||||||
|
return X_STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
bool result = false;
|
bool result = false;
|
||||||
if (free_type == X_MEM_DECOMMIT) {
|
if (free_type == X_MEM_DECOMMIT) {
|
||||||
// If zero, we may need to query size (free whole region).
|
// If zero, we may need to query size (free whole region).
|
||||||
|
@ -401,6 +409,11 @@ DECLARE_XBOXKRNL_EXPORT2(MmQueryAddressProtect, kMemory, kImplemented,
|
||||||
|
|
||||||
void MmSetAddressProtect(lpvoid_t base_address, dword_t region_size,
|
void MmSetAddressProtect(lpvoid_t base_address, dword_t region_size,
|
||||||
dword_t protect_bits) {
|
dword_t protect_bits) {
|
||||||
|
if (!protect_bits) {
|
||||||
|
XELOGE("MmSetAddressProtect: Failed due to incorrect protect_bits");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t protect = FromXdkProtectFlags(protect_bits);
|
uint32_t protect = FromXdkProtectFlags(protect_bits);
|
||||||
auto heap = kernel_memory()->LookupHeap(base_address);
|
auto heap = kernel_memory()->LookupHeap(base_address);
|
||||||
heap->Protect(base_address.guest_address(), region_size, protect);
|
heap->Protect(base_address.guest_address(), region_size, protect);
|
||||||
|
|
|
@ -78,22 +78,21 @@ DECLARE_XBOXKRNL_EXPORT1(ObLookupThreadByThreadId, kNone, kImplemented);
|
||||||
dword_result_t ObReferenceObjectByHandle(dword_t handle,
|
dword_result_t ObReferenceObjectByHandle(dword_t handle,
|
||||||
dword_t object_type_ptr,
|
dword_t object_type_ptr,
|
||||||
lpdword_t out_object_ptr) {
|
lpdword_t out_object_ptr) {
|
||||||
const static std::unordered_map<XObject::Type, uint32_t> obj_type_match = {
|
// These values come from how Xenia handles uninitialized kernel data exports.
|
||||||
{XObject::kTypeEvent, 0xD00EBEEF},
|
// D###BEEF where ### is the ordinal.
|
||||||
{XObject::kTypeSemaphore, 0xD017BEEF},
|
const static std::unordered_map<XObject::Type, uint32_t> object_types = {
|
||||||
{XObject::kTypeThread, 0xD01BBEEF}};
|
{XObject::Type::Event, 0xD00EBEEF},
|
||||||
|
{XObject::Type::Semaphore, 0xD017BEEF},
|
||||||
|
{XObject::Type::Thread, 0xD01BBEEF}};
|
||||||
auto object = kernel_state()->object_table()->LookupObject<XObject>(handle);
|
auto object = kernel_state()->object_table()->LookupObject<XObject>(handle);
|
||||||
|
|
||||||
if (!object) {
|
if (!object) {
|
||||||
return X_STATUS_INVALID_HANDLE;
|
return X_STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t native_ptr = object->guest_object();
|
uint32_t native_ptr = object->guest_object();
|
||||||
auto obj_type = obj_type_match.find(object->type());
|
auto object_type = object_types.find(object->type());
|
||||||
|
if (object_type != object_types.end()) {
|
||||||
if (obj_type != obj_type_match.end()) {
|
if (object_type_ptr && object_type_ptr != object_type->second) {
|
||||||
if (object_type_ptr && object_type_ptr != obj_type->second) {
|
|
||||||
return X_STATUS_OBJECT_TYPE_MISMATCH;
|
return X_STATUS_OBJECT_TYPE_MISMATCH;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -21,14 +21,10 @@
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
||||||
|
#include "xenia/kernel/xclock.h"
|
||||||
#include "xenia/kernel/xevent.h"
|
#include "xenia/kernel/xevent.h"
|
||||||
#include "xenia/kernel/xthread.h"
|
#include "xenia/kernel/xthread.h"
|
||||||
|
|
||||||
#if XE_PLATFORM_WIN32
|
|
||||||
#include "xenia/base/platform_win.h"
|
|
||||||
#define timegm _mkgmtime
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace xboxkrnl {
|
namespace xboxkrnl {
|
||||||
|
@ -507,44 +503,51 @@ struct X_TIME_FIELDS {
|
||||||
xe::be<uint16_t> milliseconds;
|
xe::be<uint16_t> milliseconds;
|
||||||
xe::be<uint16_t> weekday;
|
xe::be<uint16_t> weekday;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(X_TIME_FIELDS) == 16, "Must be LARGEINTEGER");
|
static_assert_size(X_TIME_FIELDS, 16);
|
||||||
|
|
||||||
// https://support.microsoft.com/en-us/kb/167296
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtltimetotimefields
|
||||||
void RtlTimeToTimeFields(lpqword_t time_ptr,
|
void RtlTimeToTimeFields(lpqword_t time_ptr,
|
||||||
pointer_t<X_TIME_FIELDS> time_fields_ptr) {
|
pointer_t<X_TIME_FIELDS> time_fields_ptr) {
|
||||||
int64_t time_ms = time_ptr.value() / 10000 - 11644473600000LL;
|
auto tp = XClock::to_sys(XClock::from_file_time(time_ptr.value()));
|
||||||
time_t timet = time_ms / 1000;
|
auto dp = date::floor<date::days>(tp);
|
||||||
struct tm* tm = gmtime(&timet);
|
auto year_month_day = date::year_month_day{dp};
|
||||||
|
auto weekday = date::weekday{dp};
|
||||||
time_fields_ptr->year = tm->tm_year + 1900;
|
auto time = date::hh_mm_ss{date::floor<std::chrono::milliseconds>(tp - dp)};
|
||||||
time_fields_ptr->month = tm->tm_mon + 1;
|
time_fields_ptr->year = static_cast<int>(year_month_day.year());
|
||||||
time_fields_ptr->day = tm->tm_mday;
|
time_fields_ptr->month = static_cast<unsigned>(year_month_day.month());
|
||||||
time_fields_ptr->hour = tm->tm_hour;
|
time_fields_ptr->day = static_cast<unsigned>(year_month_day.day());
|
||||||
time_fields_ptr->minute = tm->tm_min;
|
time_fields_ptr->weekday = weekday.c_encoding();
|
||||||
time_fields_ptr->second = tm->tm_sec;
|
time_fields_ptr->hour = time.hours().count();
|
||||||
time_fields_ptr->milliseconds = time_ms % 1000;
|
time_fields_ptr->minute = time.minutes().count();
|
||||||
time_fields_ptr->weekday = tm->tm_wday;
|
time_fields_ptr->second = static_cast<uint16_t>(time.seconds().count());
|
||||||
|
time_fields_ptr->milliseconds =
|
||||||
|
static_cast<uint16_t>(time.subseconds().count());
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT1(RtlTimeToTimeFields, kNone, kImplemented);
|
DECLARE_XBOXKRNL_EXPORT1(RtlTimeToTimeFields, kNone, kImplemented);
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtltimefieldstotime
|
||||||
dword_result_t RtlTimeFieldsToTime(pointer_t<X_TIME_FIELDS> time_fields_ptr,
|
dword_result_t RtlTimeFieldsToTime(pointer_t<X_TIME_FIELDS> time_fields_ptr,
|
||||||
lpqword_t time_ptr) {
|
lpqword_t time_ptr) {
|
||||||
struct tm tm;
|
if (time_fields_ptr->year < 1601 || time_fields_ptr->month < 1 ||
|
||||||
tm.tm_year = time_fields_ptr->year - 1900;
|
time_fields_ptr->month > 11 || time_fields_ptr->day < 1 ||
|
||||||
tm.tm_mon = time_fields_ptr->month - 1;
|
time_fields_ptr->hour > 23 || time_fields_ptr->minute > 59 ||
|
||||||
tm.tm_mday = time_fields_ptr->day;
|
time_fields_ptr->second > 59 || time_fields_ptr->milliseconds > 999) {
|
||||||
tm.tm_hour = time_fields_ptr->hour;
|
|
||||||
tm.tm_min = time_fields_ptr->minute;
|
|
||||||
tm.tm_sec = time_fields_ptr->second;
|
|
||||||
tm.tm_isdst = 0;
|
|
||||||
time_t timet = timegm(&tm);
|
|
||||||
if (timet == -1) {
|
|
||||||
// set last error = ERROR_INVALID_PARAMETER
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
uint64_t time =
|
auto year = date::year{time_fields_ptr->year};
|
||||||
((timet + 11644473600LL) * 1000 + time_fields_ptr->milliseconds) * 10000;
|
auto month = date::month{time_fields_ptr->month};
|
||||||
*time_ptr = time;
|
auto day = date::day{time_fields_ptr->day};
|
||||||
|
auto year_month_day = date::year_month_day{year, month, day};
|
||||||
|
if (!year_month_day.ok()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto dp = static_cast<date::sys_days>(year_month_day);
|
||||||
|
std::chrono::system_clock::time_point time = dp;
|
||||||
|
time += std::chrono::hours{time_fields_ptr->hour};
|
||||||
|
time += std::chrono::minutes{time_fields_ptr->minute};
|
||||||
|
time += std::chrono::seconds{time_fields_ptr->second};
|
||||||
|
time += std::chrono::milliseconds{time_fields_ptr->milliseconds};
|
||||||
|
*time_ptr = XClock::to_file_time(XClock::from_sys(time));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
DECLARE_XBOXKRNL_EXPORT1(RtlTimeFieldsToTime, kNone, kImplemented);
|
DECLARE_XBOXKRNL_EXPORT1(RtlTimeFieldsToTime, kNone, kImplemented);
|
||||||
|
|
|
@ -432,7 +432,7 @@ dword_result_t NtCreateEvent(lpdword_t handle_ptr,
|
||||||
auto existing_object =
|
auto existing_object =
|
||||||
LookupNamedObject<XEvent>(kernel_state(), obj_attributes_ptr);
|
LookupNamedObject<XEvent>(kernel_state(), obj_attributes_ptr);
|
||||||
if (existing_object) {
|
if (existing_object) {
|
||||||
if (existing_object->type() == XObject::kTypeEvent) {
|
if (existing_object->type() == XObject::Type::Event) {
|
||||||
if (handle_ptr) {
|
if (handle_ptr) {
|
||||||
existing_object->RetainHandle();
|
existing_object->RetainHandle();
|
||||||
*handle_ptr = existing_object->handle();
|
*handle_ptr = existing_object->handle();
|
||||||
|
@ -559,7 +559,7 @@ dword_result_t NtCreateSemaphore(lpdword_t handle_ptr,
|
||||||
auto existing_object =
|
auto existing_object =
|
||||||
LookupNamedObject<XSemaphore>(kernel_state(), obj_attributes_ptr);
|
LookupNamedObject<XSemaphore>(kernel_state(), obj_attributes_ptr);
|
||||||
if (existing_object) {
|
if (existing_object) {
|
||||||
if (existing_object->type() == XObject::kTypeSemaphore) {
|
if (existing_object->type() == XObject::Type::Semaphore) {
|
||||||
if (handle_ptr) {
|
if (handle_ptr) {
|
||||||
existing_object->RetainHandle();
|
existing_object->RetainHandle();
|
||||||
*handle_ptr = existing_object->handle();
|
*handle_ptr = existing_object->handle();
|
||||||
|
@ -613,7 +613,7 @@ dword_result_t NtCreateMutant(lpdword_t handle_out,
|
||||||
auto existing_object = LookupNamedObject<XMutant>(
|
auto existing_object = LookupNamedObject<XMutant>(
|
||||||
kernel_state(), obj_attributes.guest_address());
|
kernel_state(), obj_attributes.guest_address());
|
||||||
if (existing_object) {
|
if (existing_object) {
|
||||||
if (existing_object->type() == XObject::kTypeMutant) {
|
if (existing_object->type() == XObject::Type::Mutant) {
|
||||||
if (handle_out) {
|
if (handle_out) {
|
||||||
existing_object->RetainHandle();
|
existing_object->RetainHandle();
|
||||||
*handle_out = existing_object->handle();
|
*handle_out = existing_object->handle();
|
||||||
|
@ -674,7 +674,7 @@ dword_result_t NtCreateTimer(lpdword_t handle_ptr, lpvoid_t obj_attributes_ptr,
|
||||||
auto existing_object =
|
auto existing_object =
|
||||||
LookupNamedObject<XTimer>(kernel_state(), obj_attributes_ptr);
|
LookupNamedObject<XTimer>(kernel_state(), obj_attributes_ptr);
|
||||||
if (existing_object) {
|
if (existing_object) {
|
||||||
if (existing_object->type() == XObject::kTypeTimer) {
|
if (existing_object->type() == XObject::Type::Timer) {
|
||||||
if (handle_ptr) {
|
if (handle_ptr) {
|
||||||
existing_object->RetainHandle();
|
existing_object->RetainHandle();
|
||||||
*handle_ptr = existing_object->handle();
|
*handle_ptr = existing_object->handle();
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* 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. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_KERNEL_XCLOCK_H_
|
||||||
|
#define XENIA_KERNEL_XCLOCK_H_
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "xenia/base/clock.h"
|
||||||
|
|
||||||
|
#include "third_party/date/include/date/date.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
|
||||||
|
struct XClock {
|
||||||
|
using rep = int64_t;
|
||||||
|
using period = std::ratio_multiply<std::ratio<100>, std::nano>;
|
||||||
|
using duration = std::chrono::duration<rep, period>;
|
||||||
|
using time_point = std::chrono::time_point<XClock>;
|
||||||
|
static constexpr bool is_steady = false;
|
||||||
|
|
||||||
|
static time_point now() noexcept {
|
||||||
|
return from_file_time(Clock::QueryGuestSystemTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t to_file_time(time_point const& tp) noexcept {
|
||||||
|
return static_cast<uint64_t>(tp.time_since_epoch().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
static time_point from_file_time(uint64_t const& tp) noexcept {
|
||||||
|
return time_point{duration{tp}};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::chrono::system_clock::time_point to_sys(time_point const& tp) {
|
||||||
|
// TODO(gibbed): verify behavior under Linux
|
||||||
|
using sys_duration = std::chrono::system_clock::duration;
|
||||||
|
using sys_time = std::chrono::system_clock::time_point;
|
||||||
|
auto dp = tp;
|
||||||
|
dp += system_clock_delta();
|
||||||
|
auto cdp = std::chrono::time_point_cast<sys_duration>(dp);
|
||||||
|
return sys_time{cdp.time_since_epoch()};
|
||||||
|
}
|
||||||
|
|
||||||
|
static time_point from_sys(std::chrono::system_clock::time_point const& tp) {
|
||||||
|
// TODO(gibbed): verify behavior under Linux
|
||||||
|
auto ctp = std::chrono::time_point_cast<duration>(tp);
|
||||||
|
auto dp = time_point{ctp.time_since_epoch()};
|
||||||
|
dp -= system_clock_delta();
|
||||||
|
return dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The delta between std::chrono::system_clock (Jan 1 1970) and Xenon file
|
||||||
|
// time (Jan 1 1601), in seconds. In the spec std::chrono::system_clock's
|
||||||
|
// epoch is undefined, but C++20 cements it as Jan 1 1970.
|
||||||
|
static constexpr std::chrono::seconds system_clock_delta() {
|
||||||
|
auto filetime_epoch = date::year{1601} / date::month{1} / date::day{1};
|
||||||
|
auto system_clock_epoch = date::year{1970} / date::month{1} / date::day{1};
|
||||||
|
std::chrono::system_clock::time_point fp{
|
||||||
|
static_cast<date::sys_days>(filetime_epoch)};
|
||||||
|
std::chrono::system_clock::time_point sp{
|
||||||
|
static_cast<date::sys_days>(system_clock_epoch)};
|
||||||
|
return std::chrono::floor<std::chrono::seconds>(fp.time_since_epoch() -
|
||||||
|
sp.time_since_epoch());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace kernel
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif // XENIA_KERNEL_XCLOCK_H_
|
|
@ -14,7 +14,7 @@ namespace kernel {
|
||||||
|
|
||||||
XEnumerator::XEnumerator(KernelState* kernel_state, size_t items_per_enumerate,
|
XEnumerator::XEnumerator(KernelState* kernel_state, size_t items_per_enumerate,
|
||||||
size_t item_size)
|
size_t item_size)
|
||||||
: XObject(kernel_state, kType),
|
: XObject(kernel_state, kObjectType),
|
||||||
items_per_enumerate_(items_per_enumerate),
|
items_per_enumerate_(items_per_enumerate),
|
||||||
item_size_(item_size) {}
|
item_size_(item_size) {}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace kernel {
|
||||||
|
|
||||||
class XEnumerator : public XObject {
|
class XEnumerator : public XObject {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeEnumerator;
|
static const XObject::Type kObjectType = XObject::Type::Enumerator;
|
||||||
|
|
||||||
XEnumerator(KernelState* kernel_state, size_t items_per_enumerate,
|
XEnumerator(KernelState* kernel_state, size_t items_per_enumerate,
|
||||||
size_t item_size);
|
size_t item_size);
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
XEvent::XEvent(KernelState* kernel_state) : XObject(kernel_state, kType) {}
|
XEvent::XEvent(KernelState* kernel_state)
|
||||||
|
: XObject(kernel_state, kObjectType) {}
|
||||||
|
|
||||||
XEvent::~XEvent() = default;
|
XEvent::~XEvent() = default;
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ static_assert_size(X_KEVENT, 0x10);
|
||||||
|
|
||||||
class XEvent : public XObject {
|
class XEvent : public XObject {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeEvent;
|
static const XObject::Type kObjectType = XObject::Type::Event;
|
||||||
|
|
||||||
explicit XEvent(KernelState* kernel_state);
|
explicit XEvent(KernelState* kernel_state);
|
||||||
~XEvent() override;
|
~XEvent() override;
|
||||||
|
|
|
@ -22,11 +22,13 @@ namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
XFile::XFile(KernelState* kernel_state, vfs::File* file, bool synchronous)
|
XFile::XFile(KernelState* kernel_state, vfs::File* file, bool synchronous)
|
||||||
: XObject(kernel_state, kType), file_(file), is_synchronous_(synchronous) {
|
: XObject(kernel_state, kObjectType),
|
||||||
|
file_(file),
|
||||||
|
is_synchronous_(synchronous) {
|
||||||
async_event_ = threading::Event::CreateAutoResetEvent(false);
|
async_event_ = threading::Event::CreateAutoResetEvent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
XFile::XFile() : XObject(kType) {
|
XFile::XFile() : XObject(kObjectType) {
|
||||||
async_event_ = threading::Event::CreateAutoResetEvent(false);
|
async_event_ = threading::Event::CreateAutoResetEvent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,14 +124,14 @@ X_STATUS XFile::Read(uint32_t buffer_guest_address, uint32_t buffer_length,
|
||||||
const xe::BaseHeap* buffer_end_heap =
|
const xe::BaseHeap* buffer_end_heap =
|
||||||
memory()->LookupHeap(buffer_guest_high_address);
|
memory()->LookupHeap(buffer_guest_high_address);
|
||||||
if (!buffer_start_heap || !buffer_end_heap ||
|
if (!buffer_start_heap || !buffer_end_heap ||
|
||||||
buffer_start_heap->IsGuestPhysicalHeap() !=
|
(buffer_start_heap->heap_type() == HeapType::kGuestPhysical) !=
|
||||||
buffer_end_heap->IsGuestPhysicalHeap() ||
|
(buffer_end_heap->heap_type() == HeapType::kGuestPhysical) ||
|
||||||
(buffer_start_heap->IsGuestPhysicalHeap() &&
|
(buffer_start_heap->heap_type() == HeapType::kGuestPhysical &&
|
||||||
buffer_start_heap != buffer_end_heap)) {
|
buffer_start_heap != buffer_end_heap)) {
|
||||||
result = X_STATUS_ACCESS_VIOLATION;
|
result = X_STATUS_ACCESS_VIOLATION;
|
||||||
} else {
|
} else {
|
||||||
xe::PhysicalHeap* buffer_physical_heap =
|
xe::PhysicalHeap* buffer_physical_heap =
|
||||||
buffer_start_heap->IsGuestPhysicalHeap()
|
buffer_start_heap->heap_type() == HeapType::kGuestPhysical
|
||||||
? static_cast<xe::PhysicalHeap*>(buffer_start_heap)
|
? static_cast<xe::PhysicalHeap*>(buffer_start_heap)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
if (buffer_physical_heap &&
|
if (buffer_physical_heap &&
|
||||||
|
|
|
@ -75,7 +75,7 @@ class X_FILE_DIRECTORY_INFORMATION {
|
||||||
|
|
||||||
class XFile : public XObject {
|
class XFile : public XObject {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeFile;
|
static const XObject::Type kObjectType = XObject::Type::File;
|
||||||
|
|
||||||
XFile(KernelState* kernel_state, vfs::File* file, bool synchronous);
|
XFile(KernelState* kernel_state, vfs::File* file, bool synchronous);
|
||||||
~XFile() override;
|
~XFile() override;
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
XIOCompletion::XIOCompletion(KernelState* kernel_state)
|
XIOCompletion::XIOCompletion(KernelState* kernel_state)
|
||||||
: XObject(kernel_state, kType) {
|
: XObject(kernel_state, kObjectType) {
|
||||||
notification_semaphore_ = threading::Semaphore::Create(0, kMaxNotifications);
|
notification_semaphore_ = threading::Semaphore::Create(0, kMaxNotifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace kernel {
|
||||||
|
|
||||||
class XIOCompletion : public XObject {
|
class XIOCompletion : public XObject {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeIOCompletion;
|
static const XObject::Type kObjectType = XObject::Type::IOCompletion;
|
||||||
|
|
||||||
explicit XIOCompletion(KernelState* kernel_state);
|
explicit XIOCompletion(KernelState* kernel_state);
|
||||||
~XIOCompletion() override;
|
~XIOCompletion() override;
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
XModule::XModule(KernelState* kernel_state, ModuleType module_type)
|
XModule::XModule(KernelState* kernel_state, ModuleType module_type)
|
||||||
: XObject(kernel_state, kType),
|
: XObject(kernel_state, kObjectType),
|
||||||
module_type_(module_type),
|
module_type_(module_type),
|
||||||
processor_module_(nullptr),
|
processor_module_(nullptr),
|
||||||
hmodule_ptr_(0) {
|
hmodule_ptr_(0) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ class XModule : public XObject {
|
||||||
kUserModule = 1,
|
kUserModule = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const Type kType = kTypeModule;
|
static const XObject::Type kObjectType = XObject::Type::Module;
|
||||||
|
|
||||||
XModule(KernelState* kernel_state, ModuleType module_type);
|
XModule(KernelState* kernel_state, ModuleType module_type);
|
||||||
virtual ~XModule();
|
virtual ~XModule();
|
||||||
|
|
|
@ -17,9 +17,10 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
XMutant::XMutant() : XObject(kType) {}
|
XMutant::XMutant(KernelState* kernel_state)
|
||||||
|
: XObject(kernel_state, kObjectType) {}
|
||||||
|
|
||||||
XMutant::XMutant(KernelState* kernel_state) : XObject(kernel_state, kType) {}
|
XMutant::XMutant() : XObject(kObjectType) {}
|
||||||
|
|
||||||
XMutant::~XMutant() = default;
|
XMutant::~XMutant() = default;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ class XThread;
|
||||||
|
|
||||||
class XMutant : public XObject {
|
class XMutant : public XObject {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeMutant;
|
static const XObject::Type kObjectType = XObject::Type::Mutant;
|
||||||
|
|
||||||
explicit XMutant(KernelState* kernel_state);
|
explicit XMutant(KernelState* kernel_state);
|
||||||
~XMutant() override;
|
~XMutant() override;
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
XNotifyListener::XNotifyListener(KernelState* kernel_state)
|
XNotifyListener::XNotifyListener(KernelState* kernel_state)
|
||||||
: XObject(kernel_state, kType) {}
|
: XObject(kernel_state, kObjectType) {}
|
||||||
|
|
||||||
XNotifyListener::~XNotifyListener() {}
|
XNotifyListener::~XNotifyListener() {}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace kernel {
|
||||||
|
|
||||||
class XNotifyListener : public XObject {
|
class XNotifyListener : public XObject {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeNotifyListener;
|
static const XObject::Type kObjectType = XObject::Type::NotifyListener;
|
||||||
|
|
||||||
explicit XNotifyListener(KernelState* kernel_state);
|
explicit XNotifyListener(KernelState* kernel_state);
|
||||||
~XNotifyListener() override;
|
~XNotifyListener() override;
|
||||||
|
|
|
@ -129,33 +129,33 @@ bool XObject::RestoreObject(ByteStream* stream) {
|
||||||
object_ref<XObject> XObject::Restore(KernelState* kernel_state, Type type,
|
object_ref<XObject> XObject::Restore(KernelState* kernel_state, Type type,
|
||||||
ByteStream* stream) {
|
ByteStream* stream) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case kTypeEnumerator:
|
case Type::Enumerator:
|
||||||
break;
|
break;
|
||||||
case kTypeEvent:
|
case Type::Event:
|
||||||
return XEvent::Restore(kernel_state, stream);
|
return XEvent::Restore(kernel_state, stream);
|
||||||
case kTypeFile:
|
case Type::File:
|
||||||
return XFile::Restore(kernel_state, stream);
|
return XFile::Restore(kernel_state, stream);
|
||||||
case kTypeIOCompletion:
|
case Type::IOCompletion:
|
||||||
break;
|
break;
|
||||||
case kTypeModule:
|
case Type::Module:
|
||||||
return XModule::Restore(kernel_state, stream);
|
return XModule::Restore(kernel_state, stream);
|
||||||
case kTypeMutant:
|
case Type::Mutant:
|
||||||
return XMutant::Restore(kernel_state, stream);
|
return XMutant::Restore(kernel_state, stream);
|
||||||
case kTypeNotifyListener:
|
case Type::NotifyListener:
|
||||||
return XNotifyListener::Restore(kernel_state, stream);
|
return XNotifyListener::Restore(kernel_state, stream);
|
||||||
case kTypeSemaphore:
|
case Type::Semaphore:
|
||||||
return XSemaphore::Restore(kernel_state, stream);
|
return XSemaphore::Restore(kernel_state, stream);
|
||||||
case kTypeSession:
|
case Type::Session:
|
||||||
break;
|
break;
|
||||||
case kTypeSocket:
|
case Type::Socket:
|
||||||
break;
|
break;
|
||||||
case kTypeSymbolicLink:
|
case Type::SymbolicLink:
|
||||||
return XSymbolicLink::Restore(kernel_state, stream);
|
return XSymbolicLink::Restore(kernel_state, stream);
|
||||||
case kTypeThread:
|
case Type::Thread:
|
||||||
return XThread::Restore(kernel_state, stream);
|
return XThread::Restore(kernel_state, stream);
|
||||||
case kTypeTimer:
|
case Type::Timer:
|
||||||
break;
|
break;
|
||||||
case kTypeUndefined:
|
case Type::Undefined:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,21 +116,21 @@ class XObject {
|
||||||
// one with 0x8A... which causes crash
|
// one with 0x8A... which causes crash
|
||||||
static constexpr uint32_t kHandleBase = 0xF8000000;
|
static constexpr uint32_t kHandleBase = 0xF8000000;
|
||||||
|
|
||||||
enum Type {
|
enum class Type : uint32_t {
|
||||||
kTypeUndefined,
|
Undefined,
|
||||||
kTypeEnumerator,
|
Enumerator,
|
||||||
kTypeEvent,
|
Event,
|
||||||
kTypeFile,
|
File,
|
||||||
kTypeIOCompletion,
|
IOCompletion,
|
||||||
kTypeModule,
|
Module,
|
||||||
kTypeMutant,
|
Mutant,
|
||||||
kTypeNotifyListener,
|
NotifyListener,
|
||||||
kTypeSemaphore,
|
Semaphore,
|
||||||
kTypeSession,
|
Session,
|
||||||
kTypeSocket,
|
Socket,
|
||||||
kTypeSymbolicLink,
|
SymbolicLink,
|
||||||
kTypeThread,
|
Thread,
|
||||||
kTypeTimer,
|
Timer,
|
||||||
};
|
};
|
||||||
|
|
||||||
XObject(Type type);
|
XObject(Type type);
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
XSemaphore::XSemaphore(KernelState* kernel_state)
|
XSemaphore::XSemaphore(KernelState* kernel_state)
|
||||||
: XObject(kernel_state, kTypeSemaphore) {}
|
: XObject(kernel_state, kObjectType) {}
|
||||||
|
|
||||||
XSemaphore::~XSemaphore() = default;
|
XSemaphore::~XSemaphore() = default;
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ static_assert_size(X_KSEMAPHORE, 0x14);
|
||||||
|
|
||||||
class XSemaphore : public XObject {
|
class XSemaphore : public XObject {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeSemaphore;
|
static const XObject::Type kObjectType = XObject::Type::Semaphore;
|
||||||
|
|
||||||
explicit XSemaphore(KernelState* kernel_state);
|
explicit XSemaphore(KernelState* kernel_state);
|
||||||
~XSemaphore() override;
|
~XSemaphore() override;
|
||||||
|
|
|
@ -31,10 +31,11 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
XSocket::XSocket(KernelState* kernel_state) : XObject(kernel_state, kType) {}
|
XSocket::XSocket(KernelState* kernel_state)
|
||||||
|
: XObject(kernel_state, kObjectType) {}
|
||||||
|
|
||||||
XSocket::XSocket(KernelState* kernel_state, uint64_t native_handle)
|
XSocket::XSocket(KernelState* kernel_state, uint64_t native_handle)
|
||||||
: XObject(kernel_state, kType), native_handle_(native_handle) {}
|
: XObject(kernel_state, kObjectType), native_handle_(native_handle) {}
|
||||||
|
|
||||||
XSocket::~XSocket() { Close(); }
|
XSocket::~XSocket() { Close(); }
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ struct N_XSOCKADDR_IN {
|
||||||
|
|
||||||
class XSocket : public XObject {
|
class XSocket : public XObject {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeSocket;
|
static const XObject::Type kObjectType = XObject::Type::Socket;
|
||||||
|
|
||||||
enum AddressFamily {
|
enum AddressFamily {
|
||||||
AF_INET = 2,
|
AF_INET = 2,
|
||||||
|
|
|
@ -16,9 +16,9 @@ namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
XSymbolicLink::XSymbolicLink(KernelState* kernel_state)
|
XSymbolicLink::XSymbolicLink(KernelState* kernel_state)
|
||||||
: XObject(kernel_state, kType), path_(), target_() {}
|
: XObject(kernel_state, kObjectType), path_(), target_() {}
|
||||||
|
|
||||||
XSymbolicLink::XSymbolicLink() : XObject(kType), path_(), target_() {}
|
XSymbolicLink::XSymbolicLink() : XObject(kObjectType), path_(), target_() {}
|
||||||
|
|
||||||
XSymbolicLink::~XSymbolicLink() {}
|
XSymbolicLink::~XSymbolicLink() {}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace kernel {
|
||||||
|
|
||||||
class XSymbolicLink : public XObject {
|
class XSymbolicLink : public XObject {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeSymbolicLink;
|
static const XObject::Type kObjectType = XObject::Type::SymbolicLink;
|
||||||
|
|
||||||
explicit XSymbolicLink(KernelState* kernel_state);
|
explicit XSymbolicLink(KernelState* kernel_state);
|
||||||
~XSymbolicLink() override;
|
~XSymbolicLink() override;
|
||||||
|
|
|
@ -48,13 +48,13 @@ using xe::cpu::ppc::PPCOpcode;
|
||||||
uint32_t next_xthread_id_ = 0;
|
uint32_t next_xthread_id_ = 0;
|
||||||
|
|
||||||
XThread::XThread(KernelState* kernel_state)
|
XThread::XThread(KernelState* kernel_state)
|
||||||
: XObject(kernel_state, kType), guest_thread_(true) {}
|
: XObject(kernel_state, kObjectType), guest_thread_(true) {}
|
||||||
|
|
||||||
XThread::XThread(KernelState* kernel_state, uint32_t stack_size,
|
XThread::XThread(KernelState* kernel_state, uint32_t stack_size,
|
||||||
uint32_t xapi_thread_startup, uint32_t start_address,
|
uint32_t xapi_thread_startup, uint32_t start_address,
|
||||||
uint32_t start_context, uint32_t creation_flags,
|
uint32_t start_context, uint32_t creation_flags,
|
||||||
bool guest_thread, bool main_thread)
|
bool guest_thread, bool main_thread)
|
||||||
: XObject(kernel_state, kType),
|
: XObject(kernel_state, kObjectType),
|
||||||
thread_id_(++next_xthread_id_),
|
thread_id_(++next_xthread_id_),
|
||||||
guest_thread_(guest_thread),
|
guest_thread_(guest_thread),
|
||||||
main_thread_(main_thread),
|
main_thread_(main_thread),
|
||||||
|
|
|
@ -106,7 +106,7 @@ static_assert_size(X_KTHREAD, 0xAB0);
|
||||||
|
|
||||||
class XThread : public XObject, public cpu::Thread {
|
class XThread : public XObject, public cpu::Thread {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeThread;
|
static const XObject::Type kObjectType = XObject::Type::Thread;
|
||||||
|
|
||||||
struct CreationParams {
|
struct CreationParams {
|
||||||
uint32_t stack_size;
|
uint32_t stack_size;
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
XTimer::XTimer(KernelState* kernel_state) : XObject(kernel_state, kType) {}
|
XTimer::XTimer(KernelState* kernel_state)
|
||||||
|
: XObject(kernel_state, kObjectType) {}
|
||||||
|
|
||||||
XTimer::~XTimer() = default;
|
XTimer::~XTimer() = default;
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ class XThread;
|
||||||
|
|
||||||
class XTimer : public XObject {
|
class XTimer : public XObject {
|
||||||
public:
|
public:
|
||||||
static const Type kType = kTypeTimer;
|
static const XObject::Type kObjectType = XObject::Type::Timer;
|
||||||
|
|
||||||
explicit XTimer(KernelState* kernel_state);
|
explicit XTimer(KernelState* kernel_state);
|
||||||
~XTimer() override;
|
~XTimer() override;
|
||||||
|
|
|
@ -158,24 +158,26 @@ bool Memory::Initialize() {
|
||||||
physical_membase_ = mapping_base_ + 0x100000000ull;
|
physical_membase_ = mapping_base_ + 0x100000000ull;
|
||||||
|
|
||||||
// Prepare virtual heaps.
|
// Prepare virtual heaps.
|
||||||
heaps_.v00000000.Initialize(this, virtual_membase_, 0x00000000, 0x40000000,
|
heaps_.v00000000.Initialize(this, virtual_membase_, HeapType::kGuestVirtual,
|
||||||
4096);
|
0x00000000, 0x40000000, 4096);
|
||||||
heaps_.v40000000.Initialize(this, virtual_membase_, 0x40000000,
|
heaps_.v40000000.Initialize(this, virtual_membase_, HeapType::kGuestVirtual,
|
||||||
0x40000000 - 0x01000000, 64 * 1024);
|
0x40000000, 0x40000000 - 0x01000000, 64 * 1024);
|
||||||
heaps_.v80000000.Initialize(this, virtual_membase_, 0x80000000, 0x10000000,
|
heaps_.v80000000.Initialize(this, virtual_membase_, HeapType::kGuestXex,
|
||||||
64 * 1024);
|
0x80000000, 0x10000000, 64 * 1024);
|
||||||
heaps_.v90000000.Initialize(this, virtual_membase_, 0x90000000, 0x10000000,
|
heaps_.v90000000.Initialize(this, virtual_membase_, HeapType::kGuestXex,
|
||||||
4096);
|
0x90000000, 0x10000000, 4096);
|
||||||
|
|
||||||
// Prepare physical heaps.
|
// Prepare physical heaps.
|
||||||
heaps_.physical.Initialize(this, physical_membase_, 0x00000000, 0x20000000,
|
heaps_.physical.Initialize(this, physical_membase_, HeapType::kGuestPhysical,
|
||||||
4096);
|
0x00000000, 0x20000000, 4096);
|
||||||
heaps_.vA0000000.Initialize(this, virtual_membase_, 0xA0000000, 0x20000000,
|
heaps_.vA0000000.Initialize(this, virtual_membase_, HeapType::kGuestPhysical,
|
||||||
64 * 1024, &heaps_.physical);
|
0xA0000000, 0x20000000, 64 * 1024,
|
||||||
heaps_.vC0000000.Initialize(this, virtual_membase_, 0xC0000000, 0x20000000,
|
&heaps_.physical);
|
||||||
16 * 1024 * 1024, &heaps_.physical);
|
heaps_.vC0000000.Initialize(this, virtual_membase_, HeapType::kGuestPhysical,
|
||||||
heaps_.vE0000000.Initialize(this, virtual_membase_, 0xE0000000, 0x1FD00000,
|
0xC0000000, 0x20000000, 16 * 1024 * 1024,
|
||||||
4096, &heaps_.physical);
|
&heaps_.physical);
|
||||||
|
heaps_.vE0000000.Initialize(this, virtual_membase_, HeapType::kGuestPhysical,
|
||||||
|
0xE0000000, 0x1FD00000, 4096, &heaps_.physical);
|
||||||
|
|
||||||
// Protect the first and last 64kb of memory.
|
// Protect the first and last 64kb of memory.
|
||||||
heaps_.v00000000.AllocFixed(
|
heaps_.v00000000.AllocFixed(
|
||||||
|
@ -373,7 +375,7 @@ uint32_t Memory::HostToGuestVirtualThunk(const void* context,
|
||||||
|
|
||||||
uint32_t Memory::GetPhysicalAddress(uint32_t address) const {
|
uint32_t Memory::GetPhysicalAddress(uint32_t address) const {
|
||||||
const BaseHeap* heap = LookupHeap(address);
|
const BaseHeap* heap = LookupHeap(address);
|
||||||
if (!heap || !heap->IsGuestPhysicalHeap()) {
|
if (!heap || heap->heap_type() != HeapType::kGuestPhysical) {
|
||||||
return UINT32_MAX;
|
return UINT32_MAX;
|
||||||
}
|
}
|
||||||
return static_cast<const PhysicalHeap*>(heap)->GetPhysicalAddress(address);
|
return static_cast<const PhysicalHeap*>(heap)->GetPhysicalAddress(address);
|
||||||
|
@ -449,7 +451,7 @@ bool Memory::AccessViolationCallback(
|
||||||
}
|
}
|
||||||
uint32_t virtual_address = HostToGuestVirtual(host_address);
|
uint32_t virtual_address = HostToGuestVirtual(host_address);
|
||||||
BaseHeap* heap = LookupHeap(virtual_address);
|
BaseHeap* heap = LookupHeap(virtual_address);
|
||||||
if (!heap->IsGuestPhysicalHeap()) {
|
if (heap->heap_type() != HeapType::kGuestPhysical) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,7 +477,7 @@ bool Memory::TriggerPhysicalMemoryCallbacks(
|
||||||
uint32_t virtual_address, uint32_t length, bool is_write,
|
uint32_t virtual_address, uint32_t length, bool is_write,
|
||||||
bool unwatch_exact_range, bool unprotect) {
|
bool unwatch_exact_range, bool unprotect) {
|
||||||
BaseHeap* heap = LookupHeap(virtual_address);
|
BaseHeap* heap = LookupHeap(virtual_address);
|
||||||
if (heap->IsGuestPhysicalHeap()) {
|
if (heap->heap_type() == HeapType::kGuestPhysical) {
|
||||||
auto physical_heap = static_cast<PhysicalHeap*>(heap);
|
auto physical_heap = static_cast<PhysicalHeap*>(heap);
|
||||||
return physical_heap->TriggerCallbacks(std::move(global_lock_locked_once),
|
return physical_heap->TriggerCallbacks(std::move(global_lock_locked_once),
|
||||||
virtual_address, length, is_write,
|
virtual_address, length, is_write,
|
||||||
|
@ -619,6 +621,10 @@ uint32_t FromPageAccess(xe::memory::PageAccess protect) {
|
||||||
return kMemoryProtectRead;
|
return kMemoryProtectRead;
|
||||||
case memory::PageAccess::kReadWrite:
|
case memory::PageAccess::kReadWrite:
|
||||||
return kMemoryProtectRead | kMemoryProtectWrite;
|
return kMemoryProtectRead | kMemoryProtectWrite;
|
||||||
|
case memory::PageAccess::kExecuteReadOnly:
|
||||||
|
// Guest memory cannot be executable - this should never happen :)
|
||||||
|
assert_always();
|
||||||
|
return kMemoryProtectRead;
|
||||||
case memory::PageAccess::kExecuteReadWrite:
|
case memory::PageAccess::kExecuteReadWrite:
|
||||||
// Guest memory cannot be executable - this should never happen :)
|
// Guest memory cannot be executable - this should never happen :)
|
||||||
assert_always();
|
assert_always();
|
||||||
|
@ -633,11 +639,12 @@ BaseHeap::BaseHeap()
|
||||||
|
|
||||||
BaseHeap::~BaseHeap() = default;
|
BaseHeap::~BaseHeap() = default;
|
||||||
|
|
||||||
void BaseHeap::Initialize(Memory* memory, uint8_t* membase, uint32_t heap_base,
|
void BaseHeap::Initialize(Memory* memory, uint8_t* membase, HeapType heap_type,
|
||||||
uint32_t heap_size, uint32_t page_size,
|
uint32_t heap_base, uint32_t heap_size,
|
||||||
uint32_t host_address_offset) {
|
uint32_t page_size, uint32_t host_address_offset) {
|
||||||
memory_ = memory;
|
memory_ = memory;
|
||||||
membase_ = membase;
|
membase_ = membase;
|
||||||
|
heap_type_ = heap_type;
|
||||||
heap_base_ = heap_base;
|
heap_base_ = heap_base;
|
||||||
heap_size_ = heap_size;
|
heap_size_ = heap_size;
|
||||||
page_size_ = page_size;
|
page_size_ = page_size;
|
||||||
|
@ -1346,9 +1353,10 @@ VirtualHeap::VirtualHeap() = default;
|
||||||
VirtualHeap::~VirtualHeap() = default;
|
VirtualHeap::~VirtualHeap() = default;
|
||||||
|
|
||||||
void VirtualHeap::Initialize(Memory* memory, uint8_t* membase,
|
void VirtualHeap::Initialize(Memory* memory, uint8_t* membase,
|
||||||
uint32_t heap_base, uint32_t heap_size,
|
HeapType heap_type, uint32_t heap_base,
|
||||||
uint32_t page_size) {
|
uint32_t heap_size, uint32_t page_size) {
|
||||||
BaseHeap::Initialize(memory, membase, heap_base, heap_size, page_size);
|
BaseHeap::Initialize(memory, membase, heap_type, heap_base, heap_size,
|
||||||
|
page_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicalHeap::PhysicalHeap() : parent_heap_(nullptr) {}
|
PhysicalHeap::PhysicalHeap() : parent_heap_(nullptr) {}
|
||||||
|
@ -1356,8 +1364,9 @@ PhysicalHeap::PhysicalHeap() : parent_heap_(nullptr) {}
|
||||||
PhysicalHeap::~PhysicalHeap() = default;
|
PhysicalHeap::~PhysicalHeap() = default;
|
||||||
|
|
||||||
void PhysicalHeap::Initialize(Memory* memory, uint8_t* membase,
|
void PhysicalHeap::Initialize(Memory* memory, uint8_t* membase,
|
||||||
uint32_t heap_base, uint32_t heap_size,
|
HeapType heap_type, uint32_t heap_base,
|
||||||
uint32_t page_size, VirtualHeap* parent_heap) {
|
uint32_t heap_size, uint32_t page_size,
|
||||||
|
VirtualHeap* parent_heap) {
|
||||||
uint32_t host_address_offset;
|
uint32_t host_address_offset;
|
||||||
if (heap_base >= 0xE0000000 &&
|
if (heap_base >= 0xE0000000 &&
|
||||||
xe::memory::allocation_granularity() > 0x1000) {
|
xe::memory::allocation_granularity() > 0x1000) {
|
||||||
|
@ -1366,8 +1375,8 @@ void PhysicalHeap::Initialize(Memory* memory, uint8_t* membase,
|
||||||
host_address_offset = 0;
|
host_address_offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseHeap::Initialize(memory, membase, heap_base, heap_size, page_size,
|
BaseHeap::Initialize(memory, membase, heap_type, heap_base, heap_size,
|
||||||
host_address_offset);
|
page_size, host_address_offset);
|
||||||
parent_heap_ = parent_heap;
|
parent_heap_ = parent_heap;
|
||||||
system_page_size_ = uint32_t(xe::memory::page_size());
|
system_page_size_ = uint32_t(xe::memory::page_size());
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,13 @@ enum SystemHeapFlag : uint32_t {
|
||||||
kSystemHeapDefault = kSystemHeapVirtual,
|
kSystemHeapDefault = kSystemHeapVirtual,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class HeapType : uint8_t {
|
||||||
|
kGuestVirtual,
|
||||||
|
kGuestXex,
|
||||||
|
kGuestPhysical,
|
||||||
|
kHostPhysical,
|
||||||
|
};
|
||||||
|
|
||||||
enum MemoryAllocationFlag : uint32_t {
|
enum MemoryAllocationFlag : uint32_t {
|
||||||
kMemoryAllocationReserve = 1 << 0,
|
kMemoryAllocationReserve = 1 << 0,
|
||||||
kMemoryAllocationCommit = 1 << 1,
|
kMemoryAllocationCommit = 1 << 1,
|
||||||
|
@ -106,6 +113,9 @@ class BaseHeap {
|
||||||
// Size of each page within the heap range in bytes.
|
// Size of each page within the heap range in bytes.
|
||||||
uint32_t page_size() const { return page_size_; }
|
uint32_t page_size() const { return page_size_; }
|
||||||
|
|
||||||
|
// Type of specified heap
|
||||||
|
HeapType heap_type() const { return heap_type_; }
|
||||||
|
|
||||||
// Offset added to the virtual addresses to convert them to host addresses
|
// Offset added to the virtual addresses to convert them to host addresses
|
||||||
// (not including membase).
|
// (not including membase).
|
||||||
uint32_t host_address_offset() const { return host_address_offset_; }
|
uint32_t host_address_offset() const { return host_address_offset_; }
|
||||||
|
@ -177,9 +187,6 @@ class BaseHeap {
|
||||||
xe::memory::PageAccess QueryRangeAccess(uint32_t low_address,
|
xe::memory::PageAccess QueryRangeAccess(uint32_t low_address,
|
||||||
uint32_t high_address);
|
uint32_t high_address);
|
||||||
|
|
||||||
// Whether the heap is a guest virtual memory mapping of the physical memory.
|
|
||||||
virtual bool IsGuestPhysicalHeap() const { return false; }
|
|
||||||
|
|
||||||
bool Save(ByteStream* stream);
|
bool Save(ByteStream* stream);
|
||||||
bool Restore(ByteStream* stream);
|
bool Restore(ByteStream* stream);
|
||||||
|
|
||||||
|
@ -188,12 +195,13 @@ class BaseHeap {
|
||||||
protected:
|
protected:
|
||||||
BaseHeap();
|
BaseHeap();
|
||||||
|
|
||||||
void Initialize(Memory* memory, uint8_t* membase, uint32_t heap_base,
|
void Initialize(Memory* memory, uint8_t* membase, HeapType heap_type,
|
||||||
uint32_t heap_size, uint32_t page_size,
|
uint32_t heap_base, uint32_t heap_size, uint32_t page_size,
|
||||||
uint32_t host_address_offset = 0);
|
uint32_t host_address_offset = 0);
|
||||||
|
|
||||||
Memory* memory_;
|
Memory* memory_;
|
||||||
uint8_t* membase_;
|
uint8_t* membase_;
|
||||||
|
HeapType heap_type_;
|
||||||
uint32_t heap_base_;
|
uint32_t heap_base_;
|
||||||
uint32_t heap_size_;
|
uint32_t heap_size_;
|
||||||
uint32_t page_size_;
|
uint32_t page_size_;
|
||||||
|
@ -209,8 +217,8 @@ class VirtualHeap : public BaseHeap {
|
||||||
~VirtualHeap() override;
|
~VirtualHeap() override;
|
||||||
|
|
||||||
// Initializes the heap properties and allocates the page table.
|
// Initializes the heap properties and allocates the page table.
|
||||||
void Initialize(Memory* memory, uint8_t* membase, uint32_t heap_base,
|
void Initialize(Memory* memory, uint8_t* membase, HeapType heap_type,
|
||||||
uint32_t heap_size, uint32_t page_size);
|
uint32_t heap_base, uint32_t heap_size, uint32_t page_size);
|
||||||
};
|
};
|
||||||
|
|
||||||
// A heap for ranges of memory that are mapped to physical ranges.
|
// A heap for ranges of memory that are mapped to physical ranges.
|
||||||
|
@ -226,8 +234,8 @@ class PhysicalHeap : public BaseHeap {
|
||||||
~PhysicalHeap() override;
|
~PhysicalHeap() override;
|
||||||
|
|
||||||
// Initializes the heap properties and allocates the page table.
|
// Initializes the heap properties and allocates the page table.
|
||||||
void Initialize(Memory* memory, uint8_t* membase, uint32_t heap_base,
|
void Initialize(Memory* memory, uint8_t* membase, HeapType heap_type,
|
||||||
uint32_t heap_size, uint32_t page_size,
|
uint32_t heap_base, uint32_t heap_size, uint32_t page_size,
|
||||||
VirtualHeap* parent_heap);
|
VirtualHeap* parent_heap);
|
||||||
|
|
||||||
bool Alloc(uint32_t size, uint32_t alignment, uint32_t allocation_type,
|
bool Alloc(uint32_t size, uint32_t alignment, uint32_t allocation_type,
|
||||||
|
@ -253,7 +261,6 @@ class PhysicalHeap : public BaseHeap {
|
||||||
uint32_t virtual_address, uint32_t length, bool is_write,
|
uint32_t virtual_address, uint32_t length, bool is_write,
|
||||||
bool unwatch_exact_range, bool unprotect = true);
|
bool unwatch_exact_range, bool unprotect = true);
|
||||||
|
|
||||||
bool IsGuestPhysicalHeap() const override { return true; }
|
|
||||||
uint32_t GetPhysicalAddress(uint32_t address) const;
|
uint32_t GetPhysicalAddress(uint32_t address) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -97,13 +97,15 @@ std::unique_ptr<Entry> HostPathEntry::CreateEntryInternal(
|
||||||
|
|
||||||
bool HostPathEntry::DeleteEntryInternal(Entry* entry) {
|
bool HostPathEntry::DeleteEntryInternal(Entry* entry) {
|
||||||
auto full_path = host_path_ / xe::to_path(entry->name());
|
auto full_path = host_path_ / xe::to_path(entry->name());
|
||||||
|
std::error_code ec; // avoid exception on remove/remove_all failure
|
||||||
if (entry->attributes() & kFileAttributeDirectory) {
|
if (entry->attributes() & kFileAttributeDirectory) {
|
||||||
// Delete entire directory and contents.
|
// Delete entire directory and contents.
|
||||||
return std::filesystem::remove_all(full_path);
|
auto removed = std::filesystem::remove_all(full_path, ec);
|
||||||
|
return removed >= 1 && removed != static_cast<std::uintmax_t>(-1);
|
||||||
} else {
|
} else {
|
||||||
// Delete file.
|
// Delete file.
|
||||||
return !std::filesystem::is_directory(full_path) &&
|
return !std::filesystem::is_directory(full_path) &&
|
||||||
std::filesystem::remove(full_path);
|
std::filesystem::remove(full_path, ec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 97246a638a6d8f0269f4555c5e31106a86e3fd94
|
|
@ -1 +1 @@
|
||||||
Subproject commit 11aff7aeacc8315e85a659bc1e803c1064adc6b3
|
Subproject commit df609672110ac07ff7ea6597911575c4365c2928
|
Binary file not shown.
|
@ -26,8 +26,12 @@ local function combined_test_suite(test_suite_name, project_root, base_path, con
|
||||||
}))
|
}))
|
||||||
links(merge_arrays(config["links"], {
|
links(merge_arrays(config["links"], {
|
||||||
}))
|
}))
|
||||||
|
defines({
|
||||||
|
"XE_TEST_SUITE_NAME=\""..test_suite_name.."\"",
|
||||||
|
})
|
||||||
files({
|
files({
|
||||||
project_root.."/"..build_tools_src.."/test_suite_main.cc",
|
project_root.."/"..build_tools_src.."/test_suite_main.cc",
|
||||||
|
project_root.."/src/xenia/base/main_"..platform_suffix..".cc",
|
||||||
base_path.."/**_test.cc",
|
base_path.."/**_test.cc",
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,44 +13,34 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "xenia/base/cvar.h"
|
||||||
|
#include "xenia/base/main.h"
|
||||||
|
|
||||||
#define CATCH_CONFIG_RUNNER
|
#define CATCH_CONFIG_RUNNER
|
||||||
#include "third_party/catch/include/catch.hpp"
|
#include "third_party/catch/include/catch.hpp"
|
||||||
#include "xenia/base/cvar.h"
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
namespace test_suite {
|
||||||
|
|
||||||
bool has_console_attached() { return true; }
|
int test_suite_main(const std::vector<std::string>& args) {
|
||||||
|
// Catch doesn't expose a way to pass a vector of strings, despite building a
|
||||||
// Used in console mode apps; automatically picked based on subsystem.
|
// vector internally.
|
||||||
int Main(int argc, char* argv[]) {
|
int argc = 0;
|
||||||
cvar::ParseLaunchArguments(argc, argv, "", std::vector<std::string>());
|
std::vector<const char*> argv;
|
||||||
|
for (const auto& arg : args) {
|
||||||
|
argv.push_back(arg.c_str());
|
||||||
|
argc++;
|
||||||
|
}
|
||||||
|
|
||||||
// Run Catch.
|
// Run Catch.
|
||||||
int result = Catch::Session().run(argc, argv);
|
return Catch::Session().run(argc, argv.data());
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace test_suite
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
#if _WIN32
|
#ifndef XE_TEST_SUITE_NAME
|
||||||
#include "xenia/base/platform_win.h"
|
#error XE_TEST_SUITE_NAME is undefined!
|
||||||
|
#endif
|
||||||
|
|
||||||
extern "C" int main(int argc, wchar_t* argv[]) {
|
DEFINE_ENTRY_POINT(XE_TEST_SUITE_NAME, xe::test_suite::test_suite_main, "");
|
||||||
// Setup COM on the main thread.
|
|
||||||
// NOTE: this may fail if COM has already been initialized - that's OK.
|
|
||||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
|
||||||
|
|
||||||
// Convert all args to narrow, as gflags doesn't support wchar.
|
|
||||||
int argca = argc;
|
|
||||||
char** argva = (char**)alloca(sizeof(char*) * argca);
|
|
||||||
for (int n = 0; n < argca; n++) {
|
|
||||||
size_t len = wcslen(argv[n]);
|
|
||||||
argva[n] = (char*)alloca(len + 1);
|
|
||||||
std::wcstombs(argva[n], argv[n], len + 1);
|
|
||||||
}
|
|
||||||
return xe::Main(argc, argva);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
extern "C" int main(int argc, char* argv[]) { return xe::Main(argc, argv); }
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
96
xenia-build
96
xenia-build
|
@ -7,7 +7,7 @@
|
||||||
Run with --help or no arguments for possible commands.
|
Run with --help or no arguments for possible commands.
|
||||||
"""
|
"""
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
from datetime import datetime
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
@ -271,6 +271,48 @@ def generate_version_h():
|
||||||
with open('build/version.h', 'w') as f:
|
with open('build/version.h', 'w') as f:
|
||||||
f.write(contents)
|
f.write(contents)
|
||||||
|
|
||||||
|
def generate_source_class(path):
|
||||||
|
header_path = '{}.h'.format(path)
|
||||||
|
source_path = '{}.cc'.format(path)
|
||||||
|
|
||||||
|
if os.path.isfile(header_path) or os.path.isfile(source_path):
|
||||||
|
print('ERROR: Target file already exists')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if generate_source_file(header_path) > 0:
|
||||||
|
return 1
|
||||||
|
if generate_source_file(source_path) > 0:
|
||||||
|
# remove header if source file generation failed
|
||||||
|
os.remove(os.path.join(source_root, header_path))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def generate_source_file(path):
|
||||||
|
"""Generates a source file at the specified path containing copyright notice
|
||||||
|
"""
|
||||||
|
copyright = '''/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright {} Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/'''.format(datetime.now().year)
|
||||||
|
|
||||||
|
if os.path.isfile(path):
|
||||||
|
print('ERROR: Target file already exists')
|
||||||
|
return 1
|
||||||
|
try:
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write(copyright)
|
||||||
|
except Exception as e:
|
||||||
|
print('ERROR: Could not write to file [path {}]'.format(path))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def git_get_head_info():
|
def git_get_head_info():
|
||||||
"""Queries the current branch and commit checksum from git.
|
"""Queries the current branch and commit checksum from git.
|
||||||
|
@ -513,6 +555,7 @@ def discover_commands(subparsers):
|
||||||
'format': FormatCommand(subparsers),
|
'format': FormatCommand(subparsers),
|
||||||
'style': StyleCommand(subparsers),
|
'style': StyleCommand(subparsers),
|
||||||
'tidy': TidyCommand(subparsers),
|
'tidy': TidyCommand(subparsers),
|
||||||
|
'stub': StubCommand(subparsers),
|
||||||
}
|
}
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
commands['gendxbc'] = GenDxbcCommand(subparsers)
|
commands['gendxbc'] = GenDxbcCommand(subparsers)
|
||||||
|
@ -1538,6 +1581,57 @@ class TidyCommand(Command):
|
||||||
print('Tidy completed successfully.')
|
print('Tidy completed successfully.')
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
class StubCommand(Command):
|
||||||
|
"""'stub' command."""
|
||||||
|
|
||||||
|
def __init__(self, subparsers, *args, **kwargs):
|
||||||
|
super(StubCommand, self).__init__(
|
||||||
|
subparsers,
|
||||||
|
name='stub',
|
||||||
|
help_short='Create new file(s) in the xenia source tree and run premake',
|
||||||
|
*args, **kwargs)
|
||||||
|
self.parser.add_argument(
|
||||||
|
'--file', default=None,
|
||||||
|
help='Generate a source file at the provided location in the source tree')
|
||||||
|
self.parser.add_argument(
|
||||||
|
'--class', default=None,
|
||||||
|
help='Generate a class pair (.cc/.h) at the provided location in the source tree')
|
||||||
|
self.parser.add_argument(
|
||||||
|
'--target_os', default=None,
|
||||||
|
help='Target OS passed to premake, for cross-compilation')
|
||||||
|
|
||||||
|
def execute(self, args, pass_args, cwd):
|
||||||
|
root = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
source_root = os.path.join(root, os.path.normpath('src/xenia'))
|
||||||
|
|
||||||
|
if args['class']:
|
||||||
|
path = os.path.normpath(os.path.join(source_root, args['class']))
|
||||||
|
target_dir = os.path.dirname(path)
|
||||||
|
class_name = os.path.basename(path)
|
||||||
|
|
||||||
|
status = generate_source_class(path)
|
||||||
|
if status > 0:
|
||||||
|
return status
|
||||||
|
|
||||||
|
print('Created class \'{0}\' at {1}'.format(class_name, target_dir))
|
||||||
|
|
||||||
|
elif args['file']:
|
||||||
|
path = os.path.normpath(os.path.join(source_root, args['file']))
|
||||||
|
target_dir = os.path.dirname(path)
|
||||||
|
file_name = os.path.basename(path)
|
||||||
|
|
||||||
|
status = generate_source_file(path)
|
||||||
|
if status > 0:
|
||||||
|
return status
|
||||||
|
|
||||||
|
print('Created file \'{0}\' at {1}'.format(file_name, target_dir))
|
||||||
|
|
||||||
|
else:
|
||||||
|
print('ERROR: Please specify a file/class to generate')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
run_platform_premake(target_os_override=args['target_os'])
|
||||||
|
return 0
|
||||||
|
|
||||||
class DevenvCommand(Command):
|
class DevenvCommand(Command):
|
||||||
"""'devenv' command."""
|
"""'devenv' command."""
|
||||||
|
|
Loading…
Reference in New Issue