early-access version 1766
This commit is contained in:
parent
9612b12e5c
commit
10b3fd6931
3 changed files with 110 additions and 40 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 1763.
|
This is the source code for early-access 1766.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -6,20 +6,11 @@
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#elif defined(_WIN32) // ^^^ Linux ^^^ vvv Windows vvv
|
#elif defined(_WIN32) // ^^^ Linux ^^^ vvv Windows vvv
|
||||||
#ifdef _WIN32_WINNT
|
|
||||||
#undef _WIN32_WINNT
|
|
||||||
#endif
|
|
||||||
#define _WIN32_WINNT 0x0A00 // Windows 10
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#include <boost/icl/separate_interval_set.hpp>
|
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <boost/icl/separate_interval_set.hpp>
|
||||||
#pragma comment(lib, "mincore.lib")
|
#include <windows.h>
|
||||||
|
#include "common/dynamic_library.h"
|
||||||
#endif // ^^^ Windows ^^^
|
#endif // ^^^ Windows ^^^
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -31,43 +22,99 @@
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
static constexpr size_t PageAlignment = 0x1000;
|
constexpr size_t PageAlignment = 0x1000;
|
||||||
static constexpr size_t HugePageSize = 0x200000;
|
constexpr size_t HugePageSize = 0x200000;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
// Manually imported for MinGW compatibility
|
||||||
|
#ifndef MEM_RESERVE_PLACEHOLDER
|
||||||
|
#define MEM_RESERVE_PLACEHOLDER 0x0004000
|
||||||
|
#endif
|
||||||
|
#ifndef MEM_REPLACE_PLACEHOLDER
|
||||||
|
#define MEM_REPLACE_PLACEHOLDER 0x00004000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using PFN_CreateFileMapping2 = _Ret_maybenull_ HANDLE(WINAPI*)(
|
||||||
|
_In_ HANDLE File, _In_opt_ SECURITY_ATTRIBUTES* SecurityAttributes, _In_ ULONG DesiredAccess,
|
||||||
|
_In_ ULONG PageProtection, _In_ ULONG AllocationAttributes, _In_ ULONG64 MaximumSize,
|
||||||
|
_In_opt_ PCWSTR Name,
|
||||||
|
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
|
||||||
|
_In_ ULONG ParameterCount);
|
||||||
|
|
||||||
|
using PFN_VirtualAlloc2 = _Ret_maybenull_ PVOID(WINAPI*)(
|
||||||
|
_In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, _In_ SIZE_T Size,
|
||||||
|
_In_ ULONG AllocationType, _In_ ULONG PageProtection,
|
||||||
|
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
|
||||||
|
_In_ ULONG ParameterCount);
|
||||||
|
|
||||||
|
using PFN_MapViewOfFile3 = _Ret_maybenull_ __out_data_source(FILE)
|
||||||
|
PVOID(WINAPI*)(_In_ HANDLE FileMapping, _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress,
|
||||||
|
_In_ ULONG64 Offset, _In_ SIZE_T ViewSize, _In_ ULONG AllocationType,
|
||||||
|
_In_ ULONG PageProtection,
|
||||||
|
_Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters,
|
||||||
|
_In_ ULONG ParameterCount);
|
||||||
|
|
||||||
|
using PFN_UnmapViewOfFile2 = BOOL(WINAPI*)(_In_ HANDLE Process, _In_ PVOID BaseAddress,
|
||||||
|
_In_ ULONG UnmapFlags);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
|
||||||
|
if (!dll.GetSymbol(name, &pfn)) {
|
||||||
|
LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
|
||||||
|
throw std::bad_alloc{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class HostMemory::Impl {
|
class HostMemory::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||||
: backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()} {
|
: backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()},
|
||||||
|
kernelbase_dll("Kernelbase") {
|
||||||
|
if (!kernelbase_dll.IsOpen()) {
|
||||||
|
LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll");
|
||||||
|
throw std::bad_alloc{};
|
||||||
|
}
|
||||||
|
GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2);
|
||||||
|
GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2);
|
||||||
|
GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3);
|
||||||
|
GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2);
|
||||||
|
|
||||||
// Allocate backing file map
|
// Allocate backing file map
|
||||||
backing_handle =
|
backing_handle =
|
||||||
CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
|
pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
|
||||||
PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
|
PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
|
||||||
if (!backing_handle) {
|
if (!backing_handle) {
|
||||||
|
LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory",
|
||||||
|
backing_size >> 20);
|
||||||
throw std::bad_alloc{};
|
throw std::bad_alloc{};
|
||||||
}
|
}
|
||||||
// Allocate a virtual memory for the backing file map as placeholder
|
// Allocate a virtual memory for the backing file map as placeholder
|
||||||
backing_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, backing_size,
|
backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size,
|
||||||
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||||
PAGE_NOACCESS, nullptr, 0));
|
PAGE_NOACCESS, nullptr, 0));
|
||||||
if (!backing_base) {
|
if (!backing_base) {
|
||||||
Release();
|
Release();
|
||||||
|
LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory",
|
||||||
|
backing_size >> 20);
|
||||||
throw std::bad_alloc{};
|
throw std::bad_alloc{};
|
||||||
}
|
}
|
||||||
// Map backing placeholder
|
// Map backing placeholder
|
||||||
void* const ret = MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size,
|
void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size,
|
||||||
MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
|
MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
|
||||||
if (ret != backing_base) {
|
if (ret != backing_base) {
|
||||||
Release();
|
Release();
|
||||||
|
LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20);
|
||||||
throw std::bad_alloc{};
|
throw std::bad_alloc{};
|
||||||
}
|
}
|
||||||
// Allocate virtual address placeholder
|
// Allocate virtual address placeholder
|
||||||
virtual_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, virtual_size,
|
virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size,
|
||||||
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||||
PAGE_NOACCESS, nullptr, 0));
|
PAGE_NOACCESS, nullptr, 0));
|
||||||
if (!virtual_base) {
|
if (!virtual_base) {
|
||||||
Release();
|
Release();
|
||||||
|
LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory",
|
||||||
|
virtual_size >> 30);
|
||||||
throw std::bad_alloc{};
|
throw std::bad_alloc{};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,8 +179,8 @@ private:
|
||||||
void Release() {
|
void Release() {
|
||||||
if (!placeholders.empty()) {
|
if (!placeholders.empty()) {
|
||||||
for (const auto& placeholder : placeholders) {
|
for (const auto& placeholder : placeholders) {
|
||||||
if (!UnmapViewOfFile2(process, virtual_base + placeholder.lower(),
|
if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(),
|
||||||
MEM_PRESERVE_PLACEHOLDER)) {
|
MEM_PRESERVE_PLACEHOLDER)) {
|
||||||
LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder");
|
LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +192,7 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (backing_base) {
|
if (backing_base) {
|
||||||
if (!UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
|
if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) {
|
||||||
LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder");
|
LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder");
|
||||||
}
|
}
|
||||||
if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) {
|
if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) {
|
||||||
|
@ -180,8 +227,8 @@ private:
|
||||||
const bool split_left = unmap_begin > placeholder_begin;
|
const bool split_left = unmap_begin > placeholder_begin;
|
||||||
const bool split_right = unmap_end < placeholder_end;
|
const bool split_right = unmap_end < placeholder_end;
|
||||||
|
|
||||||
if (!UnmapViewOfFile2(process, virtual_base + placeholder_begin,
|
if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin,
|
||||||
MEM_PRESERVE_PLACEHOLDER)) {
|
MEM_PRESERVE_PLACEHOLDER)) {
|
||||||
LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder");
|
LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder");
|
||||||
}
|
}
|
||||||
// If we have to remap memory regions due to partial unmaps, we are in a data race as
|
// If we have to remap memory regions due to partial unmaps, we are in a data race as
|
||||||
|
@ -231,8 +278,8 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapView(size_t virtual_offset, size_t host_offset, size_t length) {
|
void MapView(size_t virtual_offset, size_t host_offset, size_t length) {
|
||||||
if (!MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset,
|
if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset,
|
||||||
length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) {
|
length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) {
|
||||||
LOG_CRITICAL(HW_Memory, "Failed to map placeholder");
|
LOG_CRITICAL(HW_Memory, "Failed to map placeholder");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,6 +322,12 @@ private:
|
||||||
HANDLE process{}; ///< Current process handle
|
HANDLE process{}; ///< Current process handle
|
||||||
HANDLE backing_handle{}; ///< File based backing memory
|
HANDLE backing_handle{}; ///< File based backing memory
|
||||||
|
|
||||||
|
DynamicLibrary kernelbase_dll;
|
||||||
|
PFN_CreateFileMapping2 pfn_CreateFileMapping2{};
|
||||||
|
PFN_VirtualAlloc2 pfn_VirtualAlloc2{};
|
||||||
|
PFN_MapViewOfFile3 pfn_MapViewOfFile3{};
|
||||||
|
PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{};
|
||||||
|
|
||||||
std::mutex placeholder_mutex; ///< Mutex for placeholders
|
std::mutex placeholder_mutex; ///< Mutex for placeholders
|
||||||
boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
|
boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
|
||||||
std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/scope_exit.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
|
@ -119,11 +120,20 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
|
||||||
|
|
||||||
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||||
|
|
||||||
|
// In the event that something fails here, stub a result to prevent the game from crashing.
|
||||||
|
// This is a work-around in the event that somehow we process a service request after the
|
||||||
|
// session has been closed by the game. This has been observed to happen rarely in Pokemon
|
||||||
|
// Sword/Shield and is likely a result of us using host threads/scheduling for services.
|
||||||
|
// TODO(bunnei): Find a better solution here.
|
||||||
|
auto error_guard = SCOPE_GUARD({ CompleteSyncRequest(*context); });
|
||||||
|
|
||||||
// Ensure we have a session request handler
|
// Ensure we have a session request handler
|
||||||
if (manager->HasSessionRequestHandler(*context)) {
|
if (manager->HasSessionRequestHandler(*context)) {
|
||||||
if (auto strong_ptr = manager->GetServiceThread().lock()) {
|
if (auto strong_ptr = manager->GetServiceThread().lock()) {
|
||||||
strong_ptr->QueueSyncRequest(*parent, std::move(context));
|
strong_ptr->QueueSyncRequest(*parent, std::move(context));
|
||||||
return ResultSuccess;
|
|
||||||
|
// We succeeded.
|
||||||
|
error_guard.Cancel();
|
||||||
} else {
|
} else {
|
||||||
ASSERT_MSG(false, "strong_ptr is nullptr!");
|
ASSERT_MSG(false, "strong_ptr is nullptr!");
|
||||||
}
|
}
|
||||||
|
@ -136,13 +146,20 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
|
||||||
|
|
||||||
ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||||
ResultCode result = ResultSuccess;
|
ResultCode result = ResultSuccess;
|
||||||
|
|
||||||
// If the session has been converted to a domain, handle the domain request
|
// If the session has been converted to a domain, handle the domain request
|
||||||
if (IsDomain() && context.HasDomainMessageHeader()) {
|
if (manager->HasSessionRequestHandler(context)) {
|
||||||
result = HandleDomainSyncRequest(context);
|
if (IsDomain() && context.HasDomainMessageHeader()) {
|
||||||
// If there is no domain header, the regular session handler is used
|
result = HandleDomainSyncRequest(context);
|
||||||
} else if (manager->HasSessionHandler()) {
|
// If there is no domain header, the regular session handler is used
|
||||||
// If this ServerSession has an associated HLE handler, forward the request to it.
|
} else if (manager->HasSessionHandler()) {
|
||||||
result = manager->SessionHandler().HandleSyncRequest(*this, context);
|
// If this ServerSession has an associated HLE handler, forward the request to it.
|
||||||
|
result = manager->SessionHandler().HandleSyncRequest(*this, context);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(false, "Session handler is invalid, stubbing response!");
|
||||||
|
IPC::ResponseBuilder rb(context, 2);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (convert_to_domain) {
|
if (convert_to_domain) {
|
||||||
|
|
Loading…
Reference in a new issue