diff --git a/src/common/AddressRanges.h b/src/common/AddressRanges.h index 297b9f65d..a608bba3b 100644 --- a/src/common/AddressRanges.h +++ b/src/common/AddressRanges.h @@ -20,6 +20,7 @@ // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * // * (c) 2017-2019 Patrick van Logchem +// * (c) 2019 ergo720 // * // * All rights reserved // * @@ -71,7 +72,6 @@ const struct { #define SYSTEM_CHIHIRO (1 << 3) // Short-hand for sets of system configurations #define SYSTEM_ALL (SYSTEM_XBOX | SYSTEM_DEVKIT | SYSTEM_CHIHIRO) - #define SYSTEM_RETAIL (SYSTEM_XBOX | SYSTEM_DEVKIT ) #define SYSTEM_128MB ( SYSTEM_DEVKIT | SYSTEM_CHIHIRO) #ifdef DEBUG const char *Comment; @@ -84,31 +84,26 @@ const struct { #endif // See http://xboxdevwiki.net/Memory // and http://xboxdevwiki.net/Boot_Process#Paging - // Entry : Start , End , Size , Protect , RangeFlags , Comment - RANGE_ENTRY(0x00000000, 0x03FFFFFF, MB( 64), PROT_XRW, SYSTEM_XBOX | MAY_FAIL, "MemLowVirtual (Retail Xbox) Optional (already reserved via virtual_memory_placeholder)"), - RANGE_ENTRY(0x00000000, 0x07FFFFFF, MB(128), PROT_XRW, SYSTEM_128MB | MAY_FAIL, "MemLowVirtual (Chihiro / DevKit)"), - RANGE_ENTRY(0x80000000, 0x83FFFFFF, MB( 64), PROT_XRW, SYSTEM_XBOX , "MemPhysical (Retail)"), - RANGE_ENTRY(0x80000000, 0x87FFFFFF, MB(128), PROT_XRW, SYSTEM_128MB , "MemPhysical (Chihiro / DevKit)"), - RANGE_ENTRY(0xB0000000, 0xB7FFFFFF, MB(128), PROT_NAC, SYSTEM_DEVKIT , "DevKitMemory"), // TODO : Check reserved range (might behave like MemTiled) - RANGE_ENTRY(0xC0000000, 0xC03FFFFF, MB( 4), PROT_RW, SYSTEM_ALL , "MemPageTable"), // See PAGE_TABLES_SIZE, which contains one 4 byte entry per PAGE_SIZE - RANGE_ENTRY(0xD0000000, 0xEFFFFFFF, MB(512), PROT_UNH, SYSTEM_ALL | MAY_FAIL, "SystemMemory Optional"), // TODO : Check reserved range (might behave like MemTiled) - RANGE_ENTRY(0xF0000000, 0xF3FFFFFF, MB( 64), PROT_UNH, SYSTEM_ALL | MAY_FAIL, "MemTiled Optional (even though it can't be reserved, MapViewOfFileEx to this range still works!?)"), - RANGE_ENTRY(0xFD000000, 0xFD6FFFFF, MB( 7), PROT_NAC, SYSTEM_ALL , "DeviceNV2A_a (GPU)"), - RANGE_ENTRY(0xFD700000, 0xFD7FFFFF, MB( 1), PROT_RW, SYSTEM_ALL , "MemNV2APRAMIN"), - RANGE_ENTRY(0xFD800000, 0xFDFFFFFF, MB( 8), PROT_NAC, SYSTEM_ALL , "DeviceNV2A_b (GPU)"), - RANGE_ENTRY(0xFE800000, 0xFE87FFFF, KB(512), PROT_NAC, SYSTEM_ALL , "DeviceAPU"), - RANGE_ENTRY(0xFEC00000, 0xFEC00FFF, KB( 4), PROT_NAC, SYSTEM_ALL , "DeviceAC97 (ACI)"), - RANGE_ENTRY(0xFED00000, 0xFED00FFF, KB( 4), PROT_NAC, SYSTEM_ALL , "DeviceUSB"), - RANGE_ENTRY(0xFEF00000, 0xFEF003FF, KB( 1), PROT_NAC, SYSTEM_ALL , "DeviceNVNet"), - RANGE_ENTRY(0xFF000000, 0xFF3FFFFF, MB( 4), PROT_NAC, SYSTEM_ALL , "DeviceFlash_a (Flash mirror 1)"), - RANGE_ENTRY(0xFF400000, 0xFF7FFFFF, MB( 4), PROT_NAC, SYSTEM_ALL , "DeviceFlash_b (Flash mirror 2)"), - RANGE_ENTRY(0xFF800000, 0xFFBFFFFF, MB( 4), PROT_NAC, SYSTEM_ALL , "DeviceFlash_c (Flash mirror 3)"), - RANGE_ENTRY(0xFFC00000, 0xFFFFFFFF, MB( 4), PROT_NAC, SYSTEM_ALL | MAY_FAIL, "DeviceFlash_d (Flash mirror 4) Optional (will probably fail reservation, which is acceptable - the 3 other mirrors work just fine"), - /* This region is only relevant if we were running the original Xbox boot sequence (including MCPX), - so it's completely redundant to use it: By the time the Kernel has started execution, this region is disabled - and cannot be re-enabled. Xbox software (and the kernel) have no access to this region whatsoever once 2BL has completed. - RANGE_ENTRY(0xFFFFFE00, 0xFFFFFFFF, 512 , PROT_NAC, SYSTEM_RETAIL | MAY_FAIL, "DeviceMCPX (not Chihiro, Xbox - if enabled) Optional (can safely be ignored)"), - */ + // Entry : Start , End , Size , Protect , RangeFlags , Comment + RANGE_ENTRY(0x00010000, 0x03FFFFFF, MB( 64) - KB(64), PROT_XRW, SYSTEM_XBOX | MAY_FAIL, "MemLowVirtual (Retail Xbox) Optional (already reserved via virtual_memory_placeholder)"), + RANGE_ENTRY(0x00010000, 0x07FFFFFF, MB(128) - KB(64), PROT_XRW, SYSTEM_128MB | MAY_FAIL, "MemLowVirtual (Chihiro / DevKit)"), + RANGE_ENTRY(0x80000000, 0x83FFFFFF, MB( 64) , PROT_UNH, SYSTEM_XBOX , "MemPhysical (Retail)"), + RANGE_ENTRY(0x80000000, 0x87FFFFFF, MB(128) , PROT_UNH, SYSTEM_128MB , "MemPhysical (Chihiro / DevKit)"), + RANGE_ENTRY(0xB0000000, 0xBFFFFFFF, MB(256) , PROT_NAC, SYSTEM_DEVKIT , "DevKitMemory"), // TODO : Check reserved range (might behave like MemTiled) + RANGE_ENTRY(0xC0000000, 0xC03FFFFF, MB( 4) , PROT_RW, SYSTEM_ALL , "MemPageTable"), // See PAGE_TABLES_SIZE, which contains one 4 byte entry per PAGE_SIZE + RANGE_ENTRY(0xD0000000, 0xEFFFFFFF, MB(512) , PROT_RW, SYSTEM_ALL | MAY_FAIL, "SystemMemory Optional"), // TODO : Check reserved range (might behave like MemTiled) + RANGE_ENTRY(0xF0000000, 0xF3FFFFFF, MB( 64) , PROT_UNH, SYSTEM_ALL , "MemTiled Optional (even though it can't be reserved, MapViewOfFileEx to this range still works!?)"), + RANGE_ENTRY(0xFD000000, 0xFD6FFFFF, MB( 7) , PROT_NAC, SYSTEM_ALL , "DeviceNV2A_a (GPU)"), + RANGE_ENTRY(0xFD700000, 0xFD7FFFFF, MB( 1) , PROT_RW, SYSTEM_ALL , "MemNV2APRAMIN"), + RANGE_ENTRY(0xFD800000, 0xFDFFFFFF, MB( 8) , PROT_NAC, SYSTEM_ALL , "DeviceNV2A_b (GPU)"), + RANGE_ENTRY(0xFE800000, 0xFE87FFFF, KB(512) , PROT_NAC, SYSTEM_ALL , "DeviceAPU"), + RANGE_ENTRY(0xFEC00000, 0xFEC00FFF, KB( 4) , PROT_NAC, SYSTEM_ALL , "DeviceAC97 (ACI)"), + RANGE_ENTRY(0xFED00000, 0xFED00FFF, KB( 4) , PROT_NAC, SYSTEM_ALL , "DeviceUSB"), + RANGE_ENTRY(0xFEF00000, 0xFEF003FF, KB( 1) , PROT_NAC, SYSTEM_ALL , "DeviceNVNet"), + RANGE_ENTRY(0xFF000000, 0xFF3FFFFF, MB( 4) , PROT_NAC, SYSTEM_ALL , "DeviceFlash_a (Flash mirror 1)"), + RANGE_ENTRY(0xFF400000, 0xFF7FFFFF, MB( 4) , PROT_NAC, SYSTEM_ALL , "DeviceFlash_b (Flash mirror 2)"), + RANGE_ENTRY(0xFF800000, 0xFFBFFFFF, MB( 4) , PROT_NAC, SYSTEM_ALL , "DeviceFlash_c (Flash mirror 3)"), + RANGE_ENTRY(0xFFC00000, 0xFFFFFFFF, MB( 4) , PROT_NAC, SYSTEM_ALL | MAY_FAIL, "DeviceFlash_d (Flash mirror 4) Optional (will probably fail reservation, which is acceptable - the 3 other mirrors work just fine"), #undef RANGE_ENTRY }; @@ -119,3 +114,4 @@ extern bool VerifyWow64(); extern LPTSTR GetLastErrorString(); extern void FreeLastErrorString(LPTSTR Error); +extern void OutputMessage(const char* msg); diff --git a/src/common/IPCHybrid.hpp b/src/common/IPCHybrid.hpp index 5a249f228..ff605733f 100644 --- a/src/common/IPCHybrid.hpp +++ b/src/common/IPCHybrid.hpp @@ -38,6 +38,7 @@ typedef enum class _IPC_UPDATE_GUI { , XBOX_LED_COLOUR , LOG_ENABLED , KRNL_IS_READY + , VM_PERSIST_MEM } IPC_UPDATE_GUI; void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value); diff --git a/src/common/ReserveAddressRanges.cpp b/src/common/ReserveAddressRanges.cpp index 2f3a8a38a..7bc5587a7 100644 --- a/src/common/ReserveAddressRanges.cpp +++ b/src/common/ReserveAddressRanges.cpp @@ -20,6 +20,7 @@ // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * // * (c) 2017-2019 Patrick van Logchem +// * (c) 2019 ergo720 // * // * All rights reserved // * @@ -30,35 +31,75 @@ #include "AddressRanges.h" // Reserve an address range up to the extend of what the host allows. -bool ReserveMemoryRange(int index) +bool ReserveMemoryRange(int index, uint32_t blocks_reserved[384]) { uint32_t Start = XboxAddressRanges[index].Start; int Size = XboxAddressRanges[index].Size; bool HadAnyFailure = false; - if (Start == 0) { - // The zero page (the entire first 64 KB block) can't be reserved (if we would - // try to reserve VirtualAlloc at address zero, it would hand us another address) - Start += BLOCK_SIZE; - Size -= BLOCK_SIZE; - HadAnyFailure = true; - } - // Reserve this range in 64 Kb block increments, so that during emulation // our memory-management code can VirtualFree() each block individually : - bool HadFailure = HadAnyFailure; - const DWORD Protect = XboxAddressRanges[index].InitialMemoryProtection; - while (Size > 0) { - SIZE_T BlockSize = (SIZE_T)(Size > BLOCK_SIZE) ? BLOCK_SIZE : Size; - LPVOID Result = VirtualAlloc((LPVOID)Start, BlockSize, MEM_RESERVE, Protect); - if (Result == NULL) { - HadFailure = true; - HadAnyFailure = true; - } - // Handle the next block - Start += BLOCK_SIZE; - Size -= BLOCK_SIZE; + const DWORD Protect = XboxAddressRanges[index].InitialMemoryProtection; + bool NeedsReservationTracking = false; + switch (Start) { + case 0x80000000: + case 0xF0000000: { + static bool NeedsInitialization = true; + static HANDLE hFileMapping; + if (NeedsInitialization) { + hFileMapping = CreateFileMapping( + INVALID_HANDLE_VALUE, + nullptr, + PAGE_EXECUTE_READWRITE, + 0, + Size, + nullptr); + if (hFileMapping == nullptr) { + HadAnyFailure = true; + break; + } + NeedsInitialization = false; + } + LPVOID Result = MapViewOfFileEx( + hFileMapping, + Start == 0x80000000 ? + (FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_EXECUTE) : (FILE_MAP_READ | FILE_MAP_WRITE), + 0, + 0, + Size, + (LPVOID)Start); + if (Result == nullptr) { + HadAnyFailure = true; + } + } + break; + + case 0xB0000000: + case 0xD0000000: { + NeedsReservationTracking = true; + } + [[fallthrough]]; + + default: { + while (Size > 0) { + static int arr_index = 0; + SIZE_T BlockSize = (SIZE_T)(Size > BLOCK_SIZE) ? BLOCK_SIZE : Size; + LPVOID Result = VirtualAlloc((LPVOID)Start, BlockSize, MEM_RESERVE, Protect); + if (Result == nullptr) { + HadAnyFailure = true; + } + // Handle the next block + Start += BLOCK_SIZE; + Size -= BLOCK_SIZE; + if (NeedsReservationTracking) { + if (Result != nullptr) { + blocks_reserved[arr_index / 32] |= (1 << (arr_index % 32)); + } + arr_index++; + } + } + } } // Only a complete success when the entire request was reserved in a single range @@ -66,7 +107,7 @@ bool ReserveMemoryRange(int index) return !HadAnyFailure; } -bool ReserveAddressRanges(const int system) { +bool ReserveAddressRanges(const int system, uint32_t blocks_reserved[384]) { // Loop over all Xbox address ranges for (int i = 0; i < ARRAY_SIZE(XboxAddressRanges); i++) { // Skip address ranges that don't match the given flags @@ -74,7 +115,7 @@ bool ReserveAddressRanges(const int system) { continue; // Try to reserve each address range - if (ReserveMemoryRange(i)) + if (ReserveMemoryRange(i, blocks_reserved)) continue; // Some ranges are allowed to fail reserving diff --git a/src/common/ReserveAddressRanges.h b/src/common/ReserveAddressRanges.h index 42e20e82b..45e2cf36d 100644 --- a/src/common/ReserveAddressRanges.h +++ b/src/common/ReserveAddressRanges.h @@ -26,4 +26,4 @@ // ****************************************************************** #pragma once -extern bool ReserveAddressRanges(const int system); +extern bool ReserveAddressRanges(const int system, uint32_t blocks_reserved[384]); diff --git a/src/common/VerifyAddressRanges.cpp b/src/common/VerifyAddressRanges.cpp index ebbe5fc1a..99077ec00 100644 --- a/src/common/VerifyAddressRanges.cpp +++ b/src/common/VerifyAddressRanges.cpp @@ -20,6 +20,7 @@ // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * // * (c) 2017-2019 Patrick van Logchem +// * (c) 2019 ego720 // * // * All rights reserved // * @@ -51,13 +52,6 @@ bool VerifyAddressRange(int index) int Size = XboxAddressRanges[index].Size; bool HadAnyFailure = false; - if (BaseAddress == 0) { - // The zero page (the entire first 64 KB block) can't be verified - // so to avoid verification failures, we just skip it, knowing it'll be alright - BaseAddress += BLOCK_SIZE; - Size -= BLOCK_SIZE; - } - // Safeguard against bounds overflow if (ReservedRangeCount < ARRAY_SIZE(ReservedRanges)) { // Initialize the reservation of a new range @@ -69,8 +63,9 @@ bool VerifyAddressRange(int index) // Verify this range in 64 Kb block increments, as they are supposed // to have been reserved like that too: bool HadFailure = HadAnyFailure; - const DWORD AllocationProtect = (XboxAddressRanges[index].Start == 0) ? PAGE_EXECUTE_WRITECOPY : XboxAddressRanges[index].InitialMemoryProtection; + const DWORD AllocationProtect = (XboxAddressRanges[index].Start == 0x10000) ? PAGE_EXECUTE_WRITECOPY : XboxAddressRanges[index].InitialMemoryProtection; MEMORY_BASIC_INFORMATION mbi; + bool Okay; while (Size > 0) { // Expected values PVOID AllocationBase = (PVOID)BaseAddress; @@ -78,9 +73,9 @@ bool VerifyAddressRange(int index) DWORD State = MEM_RESERVE; DWORD Protect = 0; DWORD Type = MEM_PRIVATE; - +#if 0 // Allowed deviations - if (XboxAddressRanges[index].Start == 0) { + if (XboxAddressRanges[index].Start == 0x10000) { AllocationBase = (PVOID)0x10000; State = MEM_COMMIT; Type = MEM_IMAGE; @@ -110,23 +105,58 @@ bool VerifyAddressRange(int index) break; } } - - // Verify each block - bool Okay = (VirtualQuery((LPVOID)BaseAddress, &mbi, sizeof(mbi)) != 0); - if (Okay) - Okay = (mbi.BaseAddress == (LPVOID)BaseAddress); - if (Okay) - Okay = (mbi.AllocationBase == AllocationBase); - if (Okay) - Okay = (mbi.AllocationProtect == AllocationProtect); - if (Okay) - Okay = (mbi.RegionSize == RegionSize); - if (Okay) - Okay = (mbi.State == State); - if (Okay) - Okay = (mbi.Protect == Protect); - if (Okay) - Okay = (mbi.Type == Type); +#endif + if (BaseAddress == 0x80000000 || BaseAddress == 0xF0000000) { + RegionSize = Size; + Okay = (VirtualQuery((LPVOID)BaseAddress, &mbi, sizeof(mbi)) != 0); + if (Okay) + Okay = (mbi.BaseAddress == (LPVOID)BaseAddress); + if (Okay) + Okay = (mbi.AllocationBase == AllocationBase); + if (Okay) + Okay = (mbi.AllocationProtect == (BaseAddress == 0x80000000 ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE)); + if (Okay) + Okay = (mbi.RegionSize == Size); + if (Okay) + Okay = (mbi.State == MEM_COMMIT); + if (Okay) + Okay = (mbi.Protect == mbi.AllocationProtect); + if (Okay) + Okay = (mbi.Type == MEM_MAPPED); + } + else if (BaseAddress == 0x10000) { + RegionSize = 0; + while (BaseAddress <= 0x07FFFFFF) { + Okay = (VirtualQuery((LPVOID)BaseAddress, &mbi, sizeof(mbi)) != 0); + if (!Okay || mbi.State != MEM_COMMIT || mbi.Type != MEM_IMAGE) { + Okay = false; + break; + } + BaseAddress += mbi.RegionSize; + RegionSize += mbi.RegionSize; + } + if (Okay) { + RegionSize = Size; + } + } + else { + // Verify each block + Okay = (VirtualQuery((LPVOID)BaseAddress, &mbi, sizeof(mbi)) != 0); + if (Okay) + Okay = (mbi.BaseAddress == (LPVOID)BaseAddress); + if (Okay) + Okay = (mbi.AllocationBase == AllocationBase); + if (Okay) + Okay = (mbi.AllocationProtect == AllocationProtect); + if (Okay) + Okay = (mbi.RegionSize == RegionSize); + if (Okay) + Okay = (mbi.State == State); + if (Okay) + Okay = (mbi.Protect == Protect); + if (Okay) + Okay = (mbi.Type == Type); + } if (!Okay) { HadFailure = true; @@ -154,9 +184,14 @@ bool VerifyAddressRange(int index) } } - // Handle the next region - BaseAddress += RegionSize; - Size -= RegionSize; + if (BaseAddress == 0x80000000 || BaseAddress == 0xF0000000 || XboxAddressRanges[index].Start == 0x10000) { + Size = 0; + } + else { + // Handle the next region + BaseAddress += RegionSize; + Size -= RegionSize; + } } // Safeguard against bounds overflow diff --git a/src/common/win32/EmuShared.h b/src/common/win32/EmuShared.h index 508617110..897e9abef 100644 --- a/src/common/win32/EmuShared.h +++ b/src/common/win32/EmuShared.h @@ -80,7 +80,7 @@ class EmuShared : public Mutex // * Check if parent process is emulating title // ****************************************************************** void GetIsEmulating(bool *isEmulating) { Lock(); *isEmulating = m_bEmulating_status; Unlock(); } - void SetIsEmulating(const bool isEmulating) { Lock(); m_bEmulating_status = isEmulating; Unlock(); } + void SetIsEmulating(const bool isEmulating) { Lock(); m_bEmulating_status = isEmulating; Unlock(); } // ****************************************************************** // * Each child process need to wait until parent process is ready @@ -199,8 +199,7 @@ class EmuShared : public Mutex void GetLogModules(unsigned int *value) { Lock(); - for (int i = 0; i < NUM_INTEGERS_LOG; ++i) - { + for (int i = 0; i < NUM_INTEGERS_LOG; ++i) { value[i] = m_core.LoggedModules[i]; } Unlock(); @@ -208,12 +207,11 @@ class EmuShared : public Mutex void SetLogModules(unsigned int *value) { Lock(); - for (int i = 0; i < NUM_INTEGERS_LOG; ++i) - { + for (int i = 0; i < NUM_INTEGERS_LOG; ++i) { m_core.LoggedModules[i] = value[i]; } Unlock(); - } + } // ****************************************************************** // * File storage location diff --git a/src/common/win32/IPCWindows.cpp b/src/common/win32/IPCWindows.cpp index 19eb2b681..0c8388630 100644 --- a/src/common/win32/IPCWindows.cpp +++ b/src/common/win32/IPCWindows.cpp @@ -65,6 +65,10 @@ void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value) cmdParam = ID_GUI_STATUS_KRNL_IS_READY; break; + case IPC_UPDATE_GUI::VM_PERSIST_MEM: + cmdParam = ID_GUI_VM_PERSIST_MEM; + break; + default: cmdParam = 0; break; diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 442908776..1b205ec02 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -291,6 +291,7 @@ g_EmuCDPD = {0}; /*XB_MACRO(VOID, WINAPI, D3DDevice_LoadVertexShaderProgram, (CONST DWORD*, DWORD) );*/\ /*XB_MACRO(VOID, __stdcall, D3DDevice_LoadVertexShader_0, () );*/\ /*XB_MACRO(VOID, WINAPI, D3DDevice_LoadVertexShader_4, (DWORD) );*/\ + XB_MACRO(HRESULT, WINAPI, D3DDevice_PersistDisplay, (VOID) ); \ XB_MACRO(HRESULT, WINAPI, D3DDevice_Reset, (XTL::X_D3DPRESENT_PARAMETERS*) ); \ /*XB_MACRO(VOID, WINAPI, D3DDevice_SelectVertexShader, (DWORD, DWORD) );*/\ /*XB_MACRO(VOID, __stdcall, D3DDevice_SelectVertexShader_0, () );*/\ @@ -8697,9 +8698,7 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_PersistDisplay)() { LOG_FUNC(); - HRESULT hRet = D3D_OK; - - LOG_UNIMPLEMENTED(); + LOG_INCOMPLETE(); // TODO: This function simply saves a copy of the display to a surface and persists it in contiguous memory // This function, if ever required, can be implemented as the following @@ -8711,8 +8710,11 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_PersistDisplay)() // 5. Use MmPersistContigousMemory to persist the surface data across reboot // 6. Call AvSetSavedDataAddress, passing the xbox surface data pointer - - return hRet; + // Call the native Xbox function so that AvSetSavedDataAddress is called and the VMManager can know its correct address + if (XB_TRMP(D3DDevice_PersistDisplay)) { + return XB_TRMP(D3DDevice_PersistDisplay)(); + } + return 0; } // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlHal.cpp b/src/core/kernel/exports/EmuKrnlHal.cpp index 2236052bf..090da6b0f 100644 --- a/src/core/kernel/exports/EmuKrnlHal.cpp +++ b/src/core/kernel/exports/EmuKrnlHal.cpp @@ -48,6 +48,7 @@ namespace xboxkrnl #include "devices\Xbox.h" // For g_SMBus, SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER #include "devices\SMCDevice.h" // For SMC_COMMAND_SCRATCH #include "common/util/strConverter.hpp" // for utf16_to_ascii +#include "core\kernel\memory-manager\VMManager.h" #include // for std::replace #include @@ -567,6 +568,8 @@ XBSYSAPI EXPORTNUM(49) xboxkrnl::VOID DECLSPEC_NORETURN NTAPI xboxkrnl::HalRetur QuickReboot |= BOOT_QUICK_REBOOT; g_EmuShared->SetBootFlags(&QuickReboot); + g_VMManager.SavePersistentMemory(); + // Some titles (Xbox Dashboard and retail/demo discs) use ";" as a current directory path seperator // This process is handled during initialization. No speical handling here required. @@ -587,11 +590,10 @@ XBSYSAPI EXPORTNUM(49) xboxkrnl::VOID DECLSPEC_NORETURN NTAPI xboxkrnl::HalRetur case ReturnFirmwareFatal: { - // NOTE: the error code is displayed by ExDisplayFatalError by other code paths so we need to change our corresponding - // paths if we want to emulate all the possible fatal errors - xboxkrnl::HalWriteSMBusValue(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, SMC_COMMAND_SCRATCH, 0, SMC_SCRATCH_DISPLAY_FATAL_ERROR); + g_VMManager.SavePersistentMemory(); + std::string szProcArgsBuffer; CxbxConvertArgToString(szProcArgsBuffer, szFilePath_CxbxReloaded_Exe, szFilePath_Xbe, CxbxKrnl_hEmuParent, CxbxKrnl_DebugMode, CxbxKrnl_DebugFileName.c_str()); @@ -610,7 +612,7 @@ XBSYSAPI EXPORTNUM(49) xboxkrnl::VOID DECLSPEC_NORETURN NTAPI xboxkrnl::HalRetur } EmuShared::Cleanup(); - ExitProcess(EXIT_SUCCESS); + TerminateProcess(GetCurrentProcess(), EXIT_SUCCESS); } // ****************************************************************** diff --git a/src/core/kernel/exports/EmuKrnlMm.cpp b/src/core/kernel/exports/EmuKrnlMm.cpp index 2aa5fbe39..f1104d2c3 100644 --- a/src/core/kernel/exports/EmuKrnlMm.cpp +++ b/src/core/kernel/exports/EmuKrnlMm.cpp @@ -53,8 +53,6 @@ namespace NtDll // ****************************************************************** // * 0x0066 - MmGlobalData // ****************************************************************** -// ergo720: a couple of these could be implemented, but most cannot. However, I wouldn't bother with these variables -// since they are just exported but never used by the kernel XBSYSAPI EXPORTNUM(102) xboxkrnl::PVOID xboxkrnl::MmGlobalData[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; // ****************************************************************** @@ -104,7 +102,7 @@ XBSYSAPI EXPORTNUM(166) xboxkrnl::PVOID NTAPI xboxkrnl::MmAllocateContiguousMemo LOG_FUNC_ARG_TYPE(PROTECTION_TYPE, ProtectionType) LOG_FUNC_END; - PVOID pRet = (PVOID)g_VMManager.AllocateContiguous(NumberOfBytes, LowestAcceptableAddress, HighestAcceptableAddress, Alignment, ProtectionType); + PVOID pRet = (PVOID)g_VMManager.AllocateContiguousMemory(NumberOfBytes, LowestAcceptableAddress, HighestAcceptableAddress, Alignment, ProtectionType); RETURN(pRet); } @@ -206,7 +204,7 @@ XBSYSAPI EXPORTNUM(171) xboxkrnl::VOID NTAPI xboxkrnl::MmFreeContiguousMemory { LOG_FUNC_ONE_ARG(BaseAddress); - g_VMManager.DeallocateContiguous((VAddr)BaseAddress); + g_VMManager.DeallocateContiguousMemory((VAddr)BaseAddress); // TODO -oDxbx: Sokoban crashes after this, at reset time (press Black + White to hit this). // Tracing in assembly shows the crash takes place quite a while further, so it's probably @@ -486,9 +484,6 @@ XBSYSAPI EXPORTNUM(374) xboxkrnl::PVOID NTAPI xboxkrnl::MmDbgAllocateMemory LOG_FUNC_ARG_TYPE(PROTECTION_TYPE, Protect) LOG_FUNC_END; - // This should only be called by debug xbe's - assert(g_bIsDebug); - PVOID addr = (PVOID)g_VMManager.AllocateSystemMemory(DebuggerType, Protect, NumberOfBytes, false); if (addr) { RtlFillMemoryUlong((void*)addr, ROUND_UP_4K(NumberOfBytes), 0); } // debugger pages are zeroed @@ -509,9 +504,6 @@ XBSYSAPI EXPORTNUM(375) xboxkrnl::ULONG NTAPI xboxkrnl::MmDbgFreeMemory LOG_FUNC_ARG(NumberOfBytes) LOG_FUNC_END; - // This should only be called by debug xbe's - assert(g_bIsDebug); - ULONG FreedPagesNumber = g_VMManager.DeallocateSystemMemory(DebuggerType, (VAddr)BaseAddress, NumberOfBytes); RETURN(FreedPagesNumber); @@ -524,9 +516,6 @@ XBSYSAPI EXPORTNUM(376) xboxkrnl::ULONG NTAPI xboxkrnl::MmDbgQueryAvailablePages { LOG_FUNC(); - // This should only be called by debug xbe's - assert(g_bIsDebug); - ULONG FreeDebuggerPageNumber = g_VMManager.QueryNumberOfFreeDebuggerPages(); RETURN(FreeDebuggerPageNumber); @@ -546,9 +535,6 @@ XBSYSAPI EXPORTNUM(377) xboxkrnl::VOID NTAPI xboxkrnl::MmDbgReleaseAddress LOG_FUNC_ARG(Opaque) LOG_FUNC_END; - // This should only be called by debug xbe's - assert(g_bIsDebug); - g_VMManager.DbgTestPte((VAddr)VirtualAddress, (PMMPTE)Opaque, false); } @@ -566,9 +552,6 @@ XBSYSAPI EXPORTNUM(378) xboxkrnl::PVOID NTAPI xboxkrnl::MmDbgWriteCheck LOG_FUNC_ARG(Opaque) LOG_FUNC_END; - // This should only be called by debug xbe's - assert(g_bIsDebug); - PVOID addr = (PVOID)g_VMManager.DbgTestPte((VAddr)VirtualAddress, (PMMPTE)Opaque, true); RETURN(addr); diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 18f4e4392..0a2a37268 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -88,8 +88,6 @@ static std::vector g_hThreads; char szFilePath_CxbxReloaded_Exe[MAX_PATH] = { 0 }; char szFolder_CxbxReloadedData[MAX_PATH] = { 0 }; char szFilePath_EEPROM_bin[MAX_PATH] = { 0 }; -char szFilePath_memory_bin[MAX_PATH] = { 0 }; -char szFilePath_page_tables[MAX_PATH] = { 0 }; char szFilePath_Xbe[MAX_PATH*2] = { 0 }; // NOTE: LAUNCH_DATA_HEADER's szLaunchPath is MAX_PATH*2 = 520 std::string CxbxBasePath; @@ -290,209 +288,6 @@ std::string CxbxGetLastErrorString(char * lpszFunction) return result; } -HANDLE CxbxRestoreContiguousMemory(char *szFilePath_memory_bin) -{ - // First, try to open an existing memory.bin file : - HANDLE hFile = CreateFile(szFilePath_memory_bin, - GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - /* lpSecurityAttributes */nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, // FILE_FLAG_WRITE_THROUGH - /* hTemplateFile */nullptr); - - bool NeedsInitialization = (hFile == INVALID_HANDLE_VALUE); - if (NeedsInitialization) - { - // If the memory.bin file doesn't exist yet, create it : - hFile = CreateFile(szFilePath_memory_bin, - GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - /* lpSecurityAttributes */nullptr, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, // FILE_FLAG_WRITE_THROUGH - /* hTemplateFile */nullptr); - if (hFile == INVALID_HANDLE_VALUE) - { - CxbxKrnlCleanup("%s : Couldn't create memory.bin file!\n", __func__); - return nullptr; - } - } - - // Make sure memory.bin is at least 128 MB in size - SetFilePointer(hFile, CHIHIRO_MEMORY_SIZE, nullptr, FILE_BEGIN); - SetEndOfFile(hFile); - - HANDLE hFileMapping = CreateFileMapping( - hFile, - /* lpFileMappingAttributes */nullptr, - PAGE_EXECUTE_READWRITE, - /* dwMaximumSizeHigh */0, - /* dwMaximumSizeLow */CHIHIRO_MEMORY_SIZE, - /**/nullptr); - if (hFileMapping == NULL) - { - CxbxKrnlCleanup("%s : Couldn't create contiguous memory.bin file mapping!\n", __func__); - return nullptr; - } - - LARGE_INTEGER len_li; - GetFileSizeEx(hFile, &len_li); - unsigned int FileSize = len_li.u.LowPart; - if (FileSize != CHIHIRO_MEMORY_SIZE) - { - CxbxKrnlCleanup("%s : memory.bin file is not 128 MiB large!\n", __func__); - return nullptr; - } - - -#ifdef CXBX_LOADER - // TODO : Use ReserveMemoryRange / UnreserveMemoryRange(Mem??) where appropriate -#endif - - // Map memory.bin contents into memory : - void *memory = (void *)MapViewOfFileEx( - hFileMapping, - FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_EXECUTE, - /* dwFileOffsetHigh */0, - /* dwFileOffsetLow */0, - CHIHIRO_CONTIGUOUS_MEMORY_SIZE, - (void *)CONTIGUOUS_MEMORY_BASE); - if (memory != (void *)CONTIGUOUS_MEMORY_BASE) - { - if (memory) - UnmapViewOfFile(memory); - - CxbxKrnlCleanup("%s: Couldn't map contiguous memory.bin to 0x80000000!", __func__); - return nullptr; - } - - EmuLogInit(LOG_LEVEL::INFO, "Mapped %d MiB of Xbox contiguous memory at 0x%.8X to 0x%.8X", - CHIHIRO_CONTIGUOUS_MEMORY_SIZE / ONE_MB, CONTIGUOUS_MEMORY_BASE, CONTIGUOUS_MEMORY_BASE + CHIHIRO_CONTIGUOUS_MEMORY_SIZE - 1); - - if (NeedsInitialization) - { - memset(memory, 0, CHIHIRO_CONTIGUOUS_MEMORY_SIZE); - EmuLogInit(LOG_LEVEL::INFO, "Initialized contiguous memory"); - } - else - EmuLogInit(LOG_LEVEL::INFO, "Loaded contiguous memory.bin"); - - size_t tiledMemorySize = XBOX_WRITE_COMBINED_SIZE; - if (g_bIsWine) { - EmuLogInit(LOG_LEVEL::INFO, "Wine detected: Using 64MB Tiled Memory Size"); - // TODO: Figure out why Wine needs this and Windows doesn't. - // Perhaps it's a Wine bug, or perhaps Wine reserves this memory for it's own usage? - tiledMemorySize = XBOX_WRITE_COMBINED_SIZE / 2; - } - - // Map memory.bin contents into tiled memory too : - void *tiled_memory = (void *)MapViewOfFileEx( - hFileMapping, - FILE_MAP_READ | FILE_MAP_WRITE, - /* dwFileOffsetHigh */0, - /* dwFileOffsetLow */0, - tiledMemorySize, - (void *)XBOX_WRITE_COMBINED_BASE); - - if (tiled_memory != (void *)XBOX_WRITE_COMBINED_BASE) - { - if (tiled_memory) - UnmapViewOfFile(tiled_memory); - - CxbxKrnlCleanup("%s: Couldn't map contiguous memory.bin into tiled memory at 0xF0000000!", __func__); - return nullptr; - } - - EmuLogInit(LOG_LEVEL::INFO, "Mapped contiguous memory to Xbox tiled memory at 0x%.8X to 0x%.8X", - XBOX_WRITE_COMBINED_BASE, XBOX_WRITE_COMBINED_BASE + tiledMemorySize - 1); - - - return hFileMapping; -} - -HANDLE CxbxRestorePageTablesMemory(char* szFilePath_page_tables) -{ - // First, try to open an existing PageTables.bin file : - HANDLE hFile = CreateFile(szFilePath_page_tables, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - /* lpSecurityAttributes */nullptr, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, // FILE_FLAG_WRITE_THROUGH - /* hTemplateFile */nullptr); - - bool NeedsInitialization = (hFile == INVALID_HANDLE_VALUE); - if (NeedsInitialization) - { - // If the PageTables.bin file doesn't exist yet, create it : - hFile = CreateFile(szFilePath_page_tables, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - /* lpSecurityAttributes */nullptr, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, // FILE_FLAG_WRITE_THROUGH - /* hTemplateFile */nullptr); - if (hFile == INVALID_HANDLE_VALUE) - { - CxbxKrnlCleanup("%s : Couldn't create PageTables.bin file!\n", __func__); - } - } - - // Make sure PageTables.bin is at least 4 MB in size - SetFilePointer(hFile, PAGE_TABLES_SIZE, nullptr, FILE_BEGIN); - SetEndOfFile(hFile); - - HANDLE hFileMapping = CreateFileMapping( - hFile, - /* lpFileMappingAttributes */nullptr, - PAGE_READWRITE, - /* dwMaximumSizeHigh */0, - /* dwMaximumSizeLow */PAGE_TABLES_SIZE, - /**/nullptr); - if (hFileMapping == NULL) - { - CxbxKrnlCleanup("%s : Couldn't create PageTables.bin file mapping!\n", __func__); - } - - LARGE_INTEGER len_li; - GetFileSizeEx(hFile, &len_li); - unsigned int FileSize = len_li.u.LowPart; - if (FileSize != PAGE_TABLES_SIZE) - { - CxbxKrnlCleanup("%s : PageTables.bin file is not 4 MiB large!\n", __func__); - } - - // Map PageTables.bin contents into memory : - void *memory = (void *)MapViewOfFileEx( - hFileMapping, - FILE_MAP_READ | FILE_MAP_WRITE, - /* dwFileOffsetHigh */0, - /* dwFileOffsetLow */0, - 4 * ONE_MB, - (void *)PAGE_TABLES_BASE); - if (memory != (void *)PAGE_TABLES_BASE) - { - if (memory) - UnmapViewOfFile(memory); - - CxbxKrnlCleanup("%s: Couldn't map PageTables.bin to 0xC0000000!", __func__); - } - - EmuLogInit(LOG_LEVEL::INFO, "Mapped %d MiB of Xbox page tables memory at 0x%.8X to 0x%.8X", - 4, PAGE_TABLES_BASE, PAGE_TABLES_END); - - if (NeedsInitialization) - { - memset(memory, 0, 4 * ONE_MB); - EmuLogInit(LOG_LEVEL::INFO, "Initialized page tables memory"); - } - else - EmuLogInit(LOG_LEVEL::INFO, "Loaded PageTables.bin"); - - return hFileMapping; -} - #pragma optimize("", off) int CxbxMessageBox(const char* msg, UINT uType, HWND hWnd) @@ -954,7 +749,7 @@ bool HandleFirstLaunch() return true; } -void CxbxKrnlMain(int argc, char* argv[]) +void CxbxKrnlMain(int argc, char* argv[], uint32_t blocks_reserved[384]) { // NOTE: This is designated for standalone kernel mode launch without GUI if (g_Settings != nullptr) { @@ -1168,13 +963,6 @@ void CxbxKrnlMain(int argc, char* argv[]) } #endif -#ifdef CXBX_LOADER - if (!VerifyAddressRanges(SYSTEM_XBOX)) { - CxbxPopupMessage("Cxbx-Reloaded hasn't got access to all required address ranges"); - return; // TODO : Halt(0); - } -#endif - // Create a safe copy of the complete EXE header: DWORD ExeHeaderSize = ExeOptionalHeader->SizeOfHeaders; // Should end up as 0x400 NewDosHeader = (PIMAGE_DOS_HEADER)VirtualAlloc(nullptr, ExeHeaderSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); @@ -1223,9 +1011,6 @@ void CxbxKrnlMain(int argc, char* argv[]) RestoreExeImageHeader(); } - HANDLE hMemoryBin = CxbxRestoreContiguousMemory(szFilePath_memory_bin); - HANDLE hPageTables = CxbxRestorePageTablesMemory(szFilePath_page_tables); - // Load Per-Xbe Keys from the Cxbx-Reloaded AppData directory LoadXboxKeys(szFolder_CxbxReloadedData); @@ -1326,7 +1111,7 @@ void CxbxKrnlMain(int argc, char* argv[]) } #endif // Initialize the virtual manager - g_VMManager.Initialize(hMemoryBin, hPageTables, BootFlags); + g_VMManager.Initialize(SYSTEM_XBOX, BootFlags, blocks_reserved); // Commit the memory used by the xbe header size_t HeaderSize = CxbxKrnl_Xbe->m_Header.dwSizeofHeaders; @@ -1788,8 +1573,6 @@ void CxbxInitFilePaths() } snprintf(szFilePath_EEPROM_bin, MAX_PATH, "%s\\EEPROM.bin", szFolder_CxbxReloadedData); - snprintf(szFilePath_memory_bin, MAX_PATH, "%s\\memory.bin", szFolder_CxbxReloadedData); - snprintf(szFilePath_page_tables, MAX_PATH, "%s\\PageTables.bin", szFolder_CxbxReloadedData); GetModuleFileName(GetModuleHandle(nullptr), szFilePath_CxbxReloaded_Exe, MAX_PATH); } diff --git a/src/core/kernel/init/CxbxKrnl.h b/src/core/kernel/init/CxbxKrnl.h index 1f5f3af53..ea3c92446 100644 --- a/src/core/kernel/init/CxbxKrnl.h +++ b/src/core/kernel/init/CxbxKrnl.h @@ -150,7 +150,7 @@ extern "C" { // fit in 51 MB. If we ever encounter an even larger XBE, this // value will have to be increased likewise (maybe up to 64 MB // for XBOX_MEMORY_SIZE or even 128 MB for CHIHIRO_MEMORY_SIZE). -#define XBE_MAX_VA (64 * ONE_MB) +#define XBE_MAX_VA (128 * ONE_MB) /*! base address of Cxbx host executable, see Cxbx project options, Linker, Advanced, Base Address */ #define CXBX_BASE_ADDR XBE_IMAGE_BASE @@ -235,7 +235,7 @@ bool CreateSettings(); bool HandleFirstLaunch(); /*! Cxbx Kernel Entry Point */ -void CxbxKrnlMain(int argc, char* argv[]); +void CxbxKrnlMain(int argc, char* argv[], uint32_t blocks_reserved[384]); /*! initialize emulation */ __declspec(noreturn) void CxbxKrnlInit(void *pTLSData, Xbe::TLS *pTLS, Xbe::LibraryVersion *LibraryVersion, DebugMode DbgMode, const char *szDebugFilename, Xbe::Header *XbeHeader, uint32_t XbeHeaderSize, void (*Entry)(), int BootFlags); diff --git a/src/core/kernel/memory-manager/PhysicalMemory.cpp b/src/core/kernel/memory-manager/PhysicalMemory.cpp index f7c009981..8608ef4fe 100644 --- a/src/core/kernel/memory-manager/PhysicalMemory.cpp +++ b/src/core/kernel/memory-manager/PhysicalMemory.cpp @@ -73,13 +73,7 @@ void PhysicalMemory::InitializePageDirectory() TempPte.Default += LARGE_PAGE_SIZE; // increase PFN } - - // NOTE: we don't need to unmap the rest of the system physical region because that mapping is done by the 2BL - // on the Xbox, which is not present here on Cxbx-Reloaded - - // Here we should also reserve some system pte's for the file system cache. However, the implementation of the kernel - // file cache functions is basically non-existent at the moment and relies on ExAllocatePoolWithTag, which is not - // correctly implemented. So, for now, we keep on ignoring this allocation + // TODO: map memory for the file system cache? } void PhysicalMemory::WritePfn(PFN pfn_start, PFN pfn_end, PMMPTE pPte, PageType BusyType, bool bZero) @@ -380,7 +374,7 @@ void PhysicalMemory::InsertFree(PFN start, PFN end) } } -bool PhysicalMemory::ConvertXboxToSystemPteProtection(DWORD perms, PMMPTE pPte) +bool PhysicalMemory::ConvertXboxToSystemPtePermissions(DWORD perms, PMMPTE pPte) { ULONG Mask = 0; @@ -437,7 +431,7 @@ bool PhysicalMemory::ConvertXboxToSystemPteProtection(DWORD perms, PMMPTE pPte) return false; } -bool PhysicalMemory::ConvertXboxToPteProtection(DWORD perms, PMMPTE pPte) +bool PhysicalMemory::ConvertXboxToPtePermissions(DWORD perms, PMMPTE pPte) { ULONG Mask = 0; ULONG LowNibble; @@ -504,7 +498,7 @@ bool PhysicalMemory::ConvertXboxToPteProtection(DWORD perms, PMMPTE pPte) return false; } -DWORD PhysicalMemory::ConvertPteToXboxProtection(ULONG PteMask) +DWORD PhysicalMemory::ConvertPteToXboxPermissions(ULONG PteMask) { // This routine assumes that the pte has valid protection bits. If it doesn't, it can produce invalid // access permissions @@ -528,7 +522,7 @@ DWORD PhysicalMemory::ConvertPteToXboxProtection(ULONG PteMask) DWORD PhysicalMemory::PatchXboxPermissions(DWORD Perms) { - // Usage notes: this routine expects the permissions to be already sanitized by ConvertXboxToSystemPteProtection or + // Usage notes: this routine expects the permissions to be already sanitized by ConvertXboxToSystemPtePermissions or // similar. If not, it can produce incorrect results // ergo720: this checks if the specified Xbox permission mask has the execute flag enabled. If not, it adds it. This is @@ -581,7 +575,7 @@ DWORD PhysicalMemory::PatchXboxPermissions(DWORD Perms) } } -DWORD PhysicalMemory::ConvertXboxToWinProtection(DWORD Perms) +DWORD PhysicalMemory::ConvertXboxToWinPermissions(DWORD Perms) { // This function assumes that the supplied permissions have been sanitized already diff --git a/src/core/kernel/memory-manager/PhysicalMemory.h b/src/core/kernel/memory-manager/PhysicalMemory.h index 1a25605a4..d4a1f4d17 100644 --- a/src/core/kernel/memory-manager/PhysicalMemory.h +++ b/src/core/kernel/memory-manager/PhysicalMemory.h @@ -19,7 +19,7 @@ // * If not, write to the Free Software Foundation, Inc., // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * -// * (c) 2017-2018 ergo720 +// * (c) 2017-2018-2019 ergo720 // * // * All rights reserved // * @@ -52,13 +52,11 @@ typedef unsigned int PFN_COUNT; typedef struct _FreeBlock { PFN start; // starting page of the block - PFN_COUNT size; // number of pages in the block (edges included) + PFN_COUNT size; // number of pages in the block xboxkrnl::LIST_ENTRY ListEntry; }FreeBlock, *PFreeBlock; -// NOTE: all the bit fields below can have endianess issues... - /* The Xbox PTE, modelled around the Intel 386 PTE specification */ typedef struct _XBOX_PTE { @@ -79,36 +77,31 @@ typedef struct _XBOX_PTE /* PTE as used by the memory manager */ -typedef struct _MMPTE +typedef union _MMPTE { - union - { - ULONG Default; - XBOX_PTE Hardware; - }; -} MMPTE, *PMMPTE; + ULONG Default; + XBOX_PTE Hardware; +}MMPTE, *PMMPTE; /* PFN entry used by the memory manager */ -typedef struct _XBOX_PFN { - union - { - ULONG Default; - struct { - ULONG LockCount : 16; // Set to prevent page relocation. Used by MmLockUnlockPhysicalPage and others - ULONG Busy : 1; // If set, PFN is in use - ULONG Unused : 1; - ULONG PteIndex : 10; // Offset in the PT that maps the pte (it seems to be needed only for page relocations) - ULONG BusyType : 4; // What the page is used for - } Busy; - struct { - ULONG LockCount : 16; // Set to prevent page relocation. Used by MmLockUnlockPhysicalPage and others - ULONG Busy : 1; // If set, PFN is in use - ULONG PtesUsed : 11; // Number of used pte's in the PT pointed by the pde - ULONG BusyType : 4; // What the page is used for (must be VirtualPageTableType or SystemPageTableType) - } PTPageFrame; - }; -} XBOX_PFN, *PXBOX_PFN; +typedef union _XBOX_PFN +{ + ULONG Default; + struct { + ULONG LockCount : 16; // Set to prevent page relocation. Used by MmLockUnlockPhysicalPage and others + ULONG Busy : 1; // If set, PFN is in use + ULONG Unused : 1; + ULONG PteIndex : 10; // Offset in the PT that maps the pte (it seems to be needed only for page relocations) + ULONG BusyType : 4; // What the page is used for + } Busy; + struct { + ULONG LockCount : 16; // Set to prevent page relocation. Used by MmLockUnlockPhysicalPage and others + ULONG Busy : 1; // If set, PFN is in use + ULONG PtesUsed : 11; // Number of used pte's in the PT pointed by the pde + ULONG BusyType : 4; // What the page is used for (must be VirtualPageTableType or SystemPageTableType) + } PTPageFrame; +}XBOX_PFN, *PXBOX_PFN; /* enum describing the usage type of the memory pages */ @@ -129,7 +122,7 @@ typedef enum _PageType }PageType; -/* enum describing the memory layouts available on the Xbox */ +/* enum describing the memory layouts the memory manager can use */ typedef enum _MmLayout { MmChihiro = 1, @@ -173,17 +166,12 @@ typedef enum _MmLayout #define IsPteOnPdeBoundary(Pte) (((ULONG_PTR)(Pte) & (PAGE_SIZE - 1)) == 0) #define WRITE_ZERO_PTE(pPte) ((pPte)->Default = 0) #define WRITE_PTE(pPte, Pte) (*(pPte) = Pte) -// On real hardware, enabling only the cache disable bit would result in an effective caching type of USWC -// (uncacheable speculative write combining), so we set both to achieve it #define DISABLE_CACHING(Pte) ((Pte).Hardware.CacheDisable = 1); ((Pte).Hardware.WriteThrough = 1) #define SET_WRITE_COMBINE(Pte) ((Pte).Hardware.CacheDisable = 0); ((Pte).Hardware.WriteThrough = 1) #define ValidKernelPteBits (PTE_VALID_MASK | PTE_WRITE_MASK | PTE_DIRTY_MASK | PTE_ACCESS_MASK) // 0x63 #define ValidKernelPdeBits (PTE_VALID_MASK | PTE_WRITE_MASK | PTE_OWNER_MASK | PTE_DIRTY_MASK | PTE_ACCESS_MASK) // 0x67 -// This returns the VAddr in the contiguous region #define CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(Pfn) ((PCHAR)PHYSICAL_MAP_BASE + ((Pfn) << PAGE_SHIFT)) -// This works with both PAddr and VAddr in the contiguous region #define CONVERT_CONTIGUOUS_PHYSICAL_TO_PFN(Va) (((Va) & (BYTES_IN_PHYSICAL_MAP - 1)) >> PAGE_SHIFT) -// This returns the address of the PFN entry for Xbox/Chihiro #define XBOX_PFN_ELEMENT(pfn) (&((PXBOX_PFN)XBOX_PFN_ADDRESS)[pfn]) #define CHIHIRO_PFN_ELEMENT(pfn) (&((PXBOX_PFN)CHIHIRO_PFN_ADDRESS)[pfn]) @@ -253,13 +241,13 @@ class PhysicalMemory // release a contiguous number of pages void InsertFree(PFN start, PFN end); // convert from Xbox to the desired system pte protection (if possible) and return it - bool ConvertXboxToSystemPteProtection(DWORD perms, PMMPTE pPte); + bool ConvertXboxToSystemPtePermissions(DWORD perms, PMMPTE pPte); // convert from Xbox to non-system pte protection (if possible) and return it - bool ConvertXboxToPteProtection(DWORD perms, PMMPTE pPte); + bool ConvertXboxToPtePermissions(DWORD perms, PMMPTE pPte); // convert from pte permissions to the corresponding Xbox protection code - DWORD ConvertPteToXboxProtection(ULONG PteMask); + DWORD ConvertPteToXboxPermissions(ULONG PteMask); // convert from Xbox to Windows permissions - DWORD ConvertXboxToWinProtection(DWORD Perms); + DWORD ConvertXboxToWinPermissions(DWORD Perms); // add execute rights if the permission mask doesn't include it DWORD PatchXboxPermissions(DWORD Perms); // commit page tables (if necessary) diff --git a/src/core/kernel/memory-manager/VMManager.cpp b/src/core/kernel/memory-manager/VMManager.cpp index b5e24bafb..60fdfe70e 100644 --- a/src/core/kernel/memory-manager/VMManager.cpp +++ b/src/core/kernel/memory-manager/VMManager.cpp @@ -19,16 +19,14 @@ // * If not, write to the Free Software Foundation, Inc., // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * -// * (c) 2017-2018 ergo720 +// * (c) 2017-2018-2019 ergo720 // * // * All rights reserved // * // ****************************************************************** // Acknowledgment: -// The core logic of the VMManager class is based on the virtual page management code of the citra emulator (GPLv2 license), -// with heavy changes and the addition of real page tables to better suit Cxbx-Reloaded and Xbox emulation. -// https://github.com/citra-emu/citra +// Some of the functions with the suffix VMA are from the vm_manager.cpp file of the citra emulator // Copyright 2015 Citra Emulator Project // Licensed under GPLv2 or any later version @@ -56,17 +54,13 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const return false; } -void VMManager::Initialize(HANDLE memory_view, HANDLE pagetables_view, int BootFlags) +void VMManager::Initialize(int SystemType, int BootFlags, uint32_t blocks_reserved[384]) { // Set up the critical section to synchronize accesses InitializeCriticalSectionAndSpinCount(&m_CriticalSection, 0x400); - SYSTEM_INFO si; - GetSystemInfo(&si); - m_AllocationGranularity = si.dwAllocationGranularity; + m_AllocationGranularity = 64 * ONE_KB; g_SystemMaxMemory = XBOX_MEMORY_SIZE; - m_hContiguousFile = memory_view; - m_hPTFile = pagetables_view; // Set up the structs tracking the memory regions ConstructMemoryRegion(LOWEST_USER_ADDRESS, USER_MEMORY_SIZE, UserRegion); @@ -74,27 +68,41 @@ void VMManager::Initialize(HANDLE memory_view, HANDLE pagetables_view, int BootF ConstructMemoryRegion(SYSTEM_MEMORY_BASE, SYSTEM_MEMORY_SIZE, SystemRegion); ConstructMemoryRegion(DEVKIT_MEMORY_BASE, DEVKIT_MEMORY_SIZE, DevkitRegion); - unsigned char PreviousLayout; - if ((BootFlags & BOOT_QUICK_REBOOT) != 0) - { - // Restore the memory layout we were emulating in the previous session + // Commit all the memory reserved by the loader for the PTs + for (int i = 0; i < 64; i++) { + LPVOID ret = VirtualAlloc((LPVOID)(PAGE_TABLES_BASE + i * (64 * ONE_KB)), 64 * ONE_KB, MEM_COMMIT, PAGE_READWRITE); + if (ret != (LPVOID)(PAGE_TABLES_BASE + i * (64 * ONE_KB))) { + CxbxKrnlCleanup("The error was 0x%08X\n", GetLastError()); + } + } - PreviousLayout = *(unsigned char*)(CONTIGUOUS_MEMORY_BASE + PAGE_SIZE - 9); - m_MmLayoutChihiro = (PreviousLayout == MmChihiro); - m_MmLayoutDebug = (PreviousLayout == MmDebug); - m_MmLayoutRetail = (PreviousLayout == MmRetail); + if (SystemType == SYSTEM_DEVKIT) { + for (int i = 0; i < 4096; i++) { + if ((blocks_reserved[i / 32] & (1 << (i % 32))) == 0) { + // The loader was unable to reserve this block, so discard it from the memory region + ConstructVMA(DEVKIT_MEMORY_BASE + i * (64 * ONE_KB), (64 * ONE_KB), DevkitRegion, ReservedVma, false); + } + } + for (int i = 4096; i < 12288; i++) { + if ((blocks_reserved[i / 32] & (1 << (i % 32))) == 0) { + // The loader was unable to reserve this block, so discard it from the memory region + ConstructVMA(SYSTEM_MEMORY_BASE + i * (64 * ONE_KB), (64 * ONE_KB), SystemRegion, ReservedVma, false); + } + } } - else - { - // Save the type of xbe we are emulating in this emulation session. This information will be needed if the current xbe performs - // a quick reboot + else { + for (int i = 0; i < 8192; i++) { + if ((blocks_reserved[i / 32] & (1 << (i % 32))) == 0) { + // The loader was unable to reserve this block, so discard it from the memory region + ConstructVMA(SYSTEM_MEMORY_BASE + i * (64 * ONE_KB), (64 * ONE_KB), SystemRegion, ReservedVma, false); + } + } + } - m_MmLayoutChihiro = (g_XbeType == xtChihiro); - m_MmLayoutDebug = (g_XbeType == xtDebug); - m_MmLayoutRetail = (g_XbeType == xtRetail); - - PreviousLayout = m_MmLayoutChihiro ? MmChihiro : (m_MmLayoutDebug ? MmDebug : MmRetail); - } + // Ensure that SystemType doesn't change between quick reboots, for example, by ignoring/forbidding changes to it while a title is being emulated + m_MmLayoutChihiro = (SystemType == SYSTEM_CHIHIRO); + m_MmLayoutDebug = (SystemType == SYSTEM_DEVKIT); + m_MmLayoutRetail = (SystemType == SYSTEM_XBOX); // Set up general memory variables according to the xbe type if (m_MmLayoutChihiro) @@ -127,21 +135,11 @@ void VMManager::Initialize(HANDLE memory_view, HANDLE pagetables_view, int BootF block->ListEntry.Flink = block->ListEntry.Blink = nullptr; // Was LIST_ENTRY_INITIALIZE() InsertHeadList(ListEntry, &block->ListEntry); - // Set up the pfn database if ((BootFlags & BOOT_QUICK_REBOOT) == 0) { - - // Quote from LukeUsher "Yeah, known issue. Contiguous memory persists more than it should so the framebuffer doesn't get cleared. - // The memory manager needs updating to only persist areas of memory marked with MmPersistContiguousMemory and discard the rest. - // But right now it persists the whole block". So we also clear the entire mapped memory.bin since we are not quick rebooting - xboxkrnl::RtlFillMemoryUlong((void*)CONTIGUOUS_MEMORY_BASE, g_SystemMaxMemory, 0); - xboxkrnl::RtlFillMemoryUlong((void*)PAGE_TABLES_BASE, PAGE_TABLES_SIZE, 0); - *(unsigned char*)(CONTIGUOUS_MEMORY_BASE + PAGE_SIZE - 9) = PreviousLayout; - InitializePfnDatabase(); + InitializeSystemAllocations(); } else { - // Restore persistent memory allocations, if there are any RestorePersistentMemory(); - ReinitializePfnDatabase(); } // Initialize the pool manager @@ -151,7 +149,7 @@ void VMManager::Initialize(HANDLE memory_view, HANDLE pagetables_view, int BootF InitializePageDirectory(); // Reserve the xbe image memory. Doing this now allows us to avoid calling XbAllocateVirtualMemory later - ConstructVMA(XBE_IMAGE_BASE, ROUND_UP_4K(CxbxKrnl_Xbe->m_Header.dwSizeofImage), UserRegion, ReservedVma, false, XBOX_PAGE_READWRITE); + ConstructVMA(XBE_IMAGE_BASE, ROUND_UP_4K(CxbxKrnl_Xbe->m_Header.dwSizeofImage), UserRegion, ReservedVma, XBOX_PAGE_READWRITE); m_VirtualMemoryBytesReserved += ROUND_UP_4K(CxbxKrnl_Xbe->m_Header.dwSizeofImage); if (m_MmLayoutChihiro) { @@ -173,27 +171,20 @@ void VMManager::ConstructMemoryRegion(VAddr Start, size_t Size, MemoryRegionType void VMManager::DestroyMemoryRegions() { - // VirtualAlloc and MapViewOfFileEx cannot be used in the contiguous region so skip it + // VirtualAlloc cannot be used in the contiguous region so skip it for (int i = 0; i < COUNTRegion - 1; ++i) { for (auto& it : m_MemoryRegionArray[i].RegionMap) { if (it.second.type != FreeVma && it.first >= XBE_MAX_VA) { - if (it.second.bFragmented) - { - VirtualFree((void*)it.first, 0, MEM_RELEASE); - } - else - { - UnmapViewOfFile((void*)(ROUND_DOWN(it.first, m_AllocationGranularity))); - } + VirtualFree((void*)it.first, 0, MEM_RELEASE); } } } } -void VMManager::InitializePfnDatabase() +void VMManager::InitializeSystemAllocations() { PFN pfn; PFN pfn_end; @@ -205,23 +196,20 @@ void VMManager::InitializePfnDatabase() // Construct the pfn of the page used by D3D - AllocateContiguous(PAGE_SIZE, D3D_PHYSICAL_PAGE, D3D_PHYSICAL_PAGE + PAGE_SIZE - 1, 0, XBOX_PAGE_READWRITE); + AllocateContiguousMemory(PAGE_SIZE, D3D_PHYSICAL_PAGE, D3D_PHYSICAL_PAGE + PAGE_SIZE - 1, 0, XBOX_PAGE_READWRITE); PersistMemory(CONTIGUOUS_MEMORY_BASE, PAGE_SIZE, true); // Construct the pfn of the page directory - AllocateContiguous(PAGE_SIZE, PAGE_DIRECTORY_PHYSICAL_ADDRESS, PAGE_DIRECTORY_PHYSICAL_ADDRESS + PAGE_SIZE - 1, 0, XBOX_PAGE_READWRITE); + AllocateContiguousMemory(PAGE_SIZE, PAGE_DIRECTORY_PHYSICAL_ADDRESS, PAGE_DIRECTORY_PHYSICAL_ADDRESS + PAGE_SIZE - 1, 0, XBOX_PAGE_READWRITE); PersistMemory(CONTIGUOUS_MEMORY_BASE + PAGE_DIRECTORY_PHYSICAL_ADDRESS, PAGE_SIZE, true); // Construct the pfn's of the kernel pages - AllocateContiguous(KERNEL_SIZE, XBE_IMAGE_BASE, XBE_IMAGE_BASE + ROUND_UP_4K(KERNEL_SIZE) - 1, 0, XBOX_PAGE_READWRITE); + AllocateContiguousMemory(KERNEL_SIZE, XBE_IMAGE_BASE, XBE_IMAGE_BASE + ROUND_UP_4K(KERNEL_SIZE) - 1, 0, XBOX_PAGE_READWRITE); PersistMemory(CONTIGUOUS_MEMORY_BASE + XBE_IMAGE_BASE, KERNEL_SIZE, true); - // NOTE: we cannot just use AllocateContiguous for the pfn and the instance memory since they both need to do their - // allocations above the contiguous limit, which is forbidden by the function. We also don't need to call - // UpdateMemoryPermissions since the contiguous region has already R/W/E rights // ergo720: on devkits, the pfn allocation spans across the retail-debug region boundary (it's 16 pages before and // 16 pages after). I decided to split this 32 pages equally between the retail and debug regions, however, this is // just a guess of mine, I could be wrong on this... @@ -240,18 +228,12 @@ void VMManager::InitializePfnDatabase() pfn_end = CHIHIRO_PFN_DATABASE_PHYSICAL_PAGE + 32 - 1; } addr = (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn); - PointerPte = GetPteAddress(addr); - EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); - TempPte.Default = ValidKernelPteBits | PTE_PERSIST_MASK; - - RemoveFree(pfn_end - pfn + 1, &result, 0, pfn, pfn_end); - AllocatePT((EndingPte - PointerPte + 1) << PAGE_SHIFT, addr); - WritePfn(pfn, pfn_end, &TempPte, UnknownType); - WritePte(PointerPte, EndingPte, TempPte, pfn); - ConstructVMA(addr, (pfn_end - pfn + 1) << PAGE_SHIFT, ContiguousRegion, AllocatedVma, false); + AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); + PersistMemory(addr, (pfn_end - pfn + 1) << PAGE_SHIFT, true); if (m_MmLayoutDebug) { m_PhysicalPagesAvailable += 16; m_DebuggerPagesAvailable -= 16; } + // Construct the pfn's of the pages holding the nv2a instance memory if (m_MmLayoutRetail || m_MmLayoutDebug) { pfn = XBOX_INSTANCE_PHYSICAL_PAGE; @@ -264,15 +246,12 @@ void VMManager::InitializePfnDatabase() addr = (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn); PointerPte = GetPteAddress(addr); EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); - TempPte.Default = ValidKernelPteBits; - DISABLE_CACHING(TempPte); - - RemoveFree(pfn_end - pfn + 1, &result, 0, pfn, pfn_end); - AllocatePT((EndingPte - PointerPte + 1) << PAGE_SHIFT, addr); - WritePfn(pfn, pfn_end, &TempPte, ContiguousType); - WritePte(PointerPte, EndingPte, TempPte, pfn); - ConstructVMA(addr, NV2A_INSTANCE_PAGE_COUNT << PAGE_SHIFT, ContiguousRegion, AllocatedVma, false); + AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); + while (PointerPte <= EndingPte) { + DISABLE_CACHING(*PointerPte); + PointerPte++; + } if (m_MmLayoutDebug) { @@ -284,160 +263,184 @@ void VMManager::InitializePfnDatabase() PointerPte = GetPteAddress(addr); EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); - RemoveFree(pfn_end - pfn + 1, &result, 0, pfn, pfn_end); - AllocatePT((EndingPte - PointerPte + 1) << PAGE_SHIFT, addr); - WritePfn(pfn, pfn_end, &TempPte, ContiguousType); - WritePte(PointerPte, EndingPte, TempPte, pfn); - ConstructVMA(addr, NV2A_INSTANCE_PAGE_COUNT << PAGE_SHIFT, ContiguousRegion, AllocatedVma, false); + AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); + while (PointerPte <= EndingPte) { + DISABLE_CACHING(*PointerPte); + PointerPte++; + } } } -void VMManager::ReinitializePfnDatabase() +void VMManager::RestorePersistentMemory() { - // With a quick reboot the initialization is simplified since the previous pte's of the persistent allocations are carried over and - // can be reused + HANDLE handle = OpenFileMapping(FILE_MAP_READ, FALSE, "PersistentMemory"); + if (handle == NULL) { + CxbxKrnlCleanup("Couldn't restore persistent memory! OpenFileMapping failed"); + return; + } - PFN pfn; - PFN pfn_end; + PersistedMemory* persisted_mem = (PersistedMemory*)MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0); + if (persisted_mem == nullptr) { + CxbxKrnlCleanup("Couldn't restore persistent memory! MapViewOfFile failed with error %08X", GetLastError()); + return; + } + if (persisted_mem->LaunchFrameAddresses[0] != 0 && IS_PHYSICAL_ADDRESS(persisted_mem->LaunchFrameAddresses[0])) { + xboxkrnl::LaunchDataPage = (xboxkrnl::PLAUNCH_DATA_PAGE)persisted_mem->LaunchFrameAddresses[0]; + DBG_PRINTF("Restored LaunchDataPage\n"); + } - // Update the allocation of the page used by D3D - RestorePersistentAllocation(CONTIGUOUS_MEMORY_BASE, D3D_PHYSICAL_PAGE, D3D_PHYSICAL_PAGE, ContiguousType); + if (persisted_mem->LaunchFrameAddresses[1] != 0 && IS_PHYSICAL_ADDRESS(persisted_mem->LaunchFrameAddresses[1])) { + xboxkrnl::AvSavedDataAddress = (xboxkrnl::PVOID)persisted_mem->LaunchFrameAddresses[1]; + DBG_PRINTF("Restored Framebuffer\n"); + } + MMPTE pte; + PFN pfn; + for (unsigned int i = 0; i < persisted_mem->NumOfPtes; i++) { + pte.Default = persisted_mem->Data[persisted_mem->NumOfPtes + i]; + assert(pte.Hardware.Valid != 0 && pte.Hardware.Persist != 0); + memcpy(GetPteAddress(persisted_mem->Data[i]), &pte.Default, sizeof(MMPTE)); + RemoveFree(1, &pfn, 0, pte.Hardware.PFN, pte.Hardware.PFN); + if (m_MmLayoutChihiro) { + memcpy(CHIHIRO_PFN_ELEMENT(pte.Hardware.PFN), + &((PXBOX_PFN)&persisted_mem->Data[(persisted_mem->NumOfPtes * 2) + (persisted_mem->NumOfPtes - 32) * ONE_KB])[pte.Hardware.PFN], + sizeof(XBOX_PFN)); + if ((uint32_t*)persisted_mem->Data[i] < (uint32_t*)CHIHIRO_PFN_ADDRESS) { + memcpy((void*)(persisted_mem->Data[i]), &persisted_mem->Data[persisted_mem->NumOfPtes * 2 + i * ONE_KB], PAGE_SIZE); + } + } + else { + memcpy(XBOX_PFN_ELEMENT(pte.Hardware.PFN), + &((PXBOX_PFN)&persisted_mem->Data[(persisted_mem->NumOfPtes * 2) + (persisted_mem->NumOfPtes - 16) * ONE_KB])[pte.Hardware.PFN], + sizeof(XBOX_PFN)); + if ((uint32_t*)persisted_mem->Data[i] < (uint32_t*)XBOX_PFN_ADDRESS) { + memcpy((void*)(persisted_mem->Data[i]), &persisted_mem->Data[persisted_mem->NumOfPtes * 2 + i * ONE_KB], PAGE_SIZE); + } + } + } - // Update the allocation of the page directory - RestorePersistentAllocation(CONTIGUOUS_MEMORY_BASE + PAGE_DIRECTORY_PHYSICAL_ADDRESS, PAGE_DIRECTORY_PHYSICAL_ADDRESS >> PAGE_SHIFT, - PAGE_DIRECTORY_PHYSICAL_ADDRESS >> PAGE_SHIFT, ContiguousType); - - - // Update the allocation of the kernel - RestorePersistentAllocation(CONTIGUOUS_MEMORY_BASE + XBE_IMAGE_BASE, XBE_IMAGE_BASE >> PAGE_SHIFT, - ((XBE_IMAGE_BASE + ROUND_UP_4K(KERNEL_SIZE)) >> PAGE_SHIFT) - 1, ContiguousType); - - - // Update the allocation of the pfn database - if (m_MmLayoutRetail) { - pfn = XBOX_PFN_DATABASE_PHYSICAL_PAGE; - pfn_end = XBOX_PFN_DATABASE_PHYSICAL_PAGE + 16 - 1; + PFN_COUNT pages_num = 1; + for (unsigned int i = 0; i < persisted_mem->NumOfPtes; i++) { + pte.Default = persisted_mem->Data[persisted_mem->NumOfPtes + i]; + if (pte.Hardware.GuardOrEnd == 0) { + pages_num++; + continue; + } + size_t size = pages_num << PAGE_SHIFT; + VAddr addr = persisted_mem->Data[i] - (size - PAGE_SIZE); + AllocatePT(size, addr); + ConstructVMA(addr, size, ContiguousRegion, AllocatedVma, false); + GetPfnOfPT(GetPteAddress(addr))->PTPageFrame.PtesUsed += pages_num; + pages_num = 1; } - else if (m_MmLayoutDebug) { - pfn = XBOX_PFN_DATABASE_PHYSICAL_PAGE; - pfn_end = XBOX_PFN_DATABASE_PHYSICAL_PAGE + 32 - 1; + + if (m_MmLayoutDebug) { m_PhysicalPagesAvailable += 16; m_DebuggerPagesAvailable -= 16; } + + PFN pfn_end; + PFN result; + if (m_MmLayoutRetail || m_MmLayoutDebug) { + pfn = XBOX_INSTANCE_PHYSICAL_PAGE; + pfn_end = XBOX_INSTANCE_PHYSICAL_PAGE + NV2A_INSTANCE_PAGE_COUNT - 1; } else { - pfn = CHIHIRO_PFN_DATABASE_PHYSICAL_PAGE; - pfn_end = CHIHIRO_PFN_DATABASE_PHYSICAL_PAGE + 32 - 1; + pfn = CHIHIRO_INSTANCE_PHYSICAL_PAGE; + pfn_end = CHIHIRO_INSTANCE_PHYSICAL_PAGE + NV2A_INSTANCE_PAGE_COUNT - 1; } - RestorePersistentAllocation((VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn), pfn, pfn_end, UnknownType); - if (m_MmLayoutDebug) { m_PhysicalPagesAvailable += 16; m_DebuggerPagesAvailable -= 16; } + VAddr addr = (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn); + PMMPTE PointerPte = GetPteAddress(addr); + PMMPTE EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); + AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); + while (PointerPte <= EndingPte) { + DISABLE_CACHING(*PointerPte); + PointerPte++; + } + if (m_MmLayoutDebug) { - PFN result; - MMPTE TempPte; - VAddr addr; + // Debug kits have two nv2a instance memory, another at the top of the 128 MiB - // Re-construct the allocation of the nv2a instance memory - // NOTE: the entire instance memory is persisted during a quick reboot, however, it doesn't change anything to re-construct it - // here since we would just have to move this block of code to a specific vmmanager reboot routine called in HalReturnToFirmware - if (m_MmLayoutRetail || m_MmLayoutDebug) { - pfn = XBOX_INSTANCE_PHYSICAL_PAGE; - pfn_end = XBOX_INSTANCE_PHYSICAL_PAGE + NV2A_INSTANCE_PAGE_COUNT - 1; - } - else { - pfn = CHIHIRO_INSTANCE_PHYSICAL_PAGE; - pfn_end = CHIHIRO_INSTANCE_PHYSICAL_PAGE + NV2A_INSTANCE_PAGE_COUNT - 1; - } + pfn += DEBUGKIT_FIRST_UPPER_HALF_PAGE; + pfn_end += DEBUGKIT_FIRST_UPPER_HALF_PAGE; addr = (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn); - TempPte.Default = ValidKernelPteBits; - DISABLE_CACHING(TempPte); + PointerPte = GetPteAddress(addr); + EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); - RemoveFree(pfn_end - pfn + 1, &result, 0, pfn, pfn_end); - RestorePersistentAllocation(addr, pfn, pfn_end, ContiguousType); - WritePte(GetPteAddress(addr), GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)), TempPte, pfn); - - - if (m_MmLayoutDebug) - { - // Debug kits have two nv2a instance memory, another at the top of the 128 MiB - - pfn += DEBUGKIT_FIRST_UPPER_HALF_PAGE; - pfn_end += DEBUGKIT_FIRST_UPPER_HALF_PAGE; - addr = (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn); - - RemoveFree(pfn_end - pfn + 1, &result, 0, pfn, pfn_end); - RestorePersistentAllocation(addr, pfn, pfn_end, ContiguousType); - WritePte(GetPteAddress(addr), GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)), TempPte, pfn); + AllocateContiguousMemoryInternal(pfn_end - pfn + 1, pfn, pfn_end, 1, XBOX_PAGE_READWRITE); + while (PointerPte <= EndingPte) { + DISABLE_CACHING(*PointerPte); + PointerPte++; } } + + UnmapViewOfFile(persisted_mem); + CloseHandle(handle); + + ipc_send_gui_update(IPC_UPDATE_GUI::VM_PERSIST_MEM, 0); } -void VMManager::RestorePersistentAllocation(VAddr addr, PFN StartingPfn, PFN EndingPfn, PageType Type) +void VMManager::SavePersistentMemory() { - AllocatePT((EndingPfn - StartingPfn + 1) << PAGE_SHIFT, addr); - ConstructVMA(addr, (EndingPfn - StartingPfn + 1) << PAGE_SHIFT, ContiguousRegion, AllocatedVma, false); - WritePfn(StartingPfn, EndingPfn, GetPteAddress(addr), Type); - GetPfnOfPT(GetPteAddress(addr))->PTPageFrame.PtesUsed += (EndingPfn - StartingPfn + 1); -} + PersistedMemory* persisted_mem; + HANDLE handle; + LPVOID addr; + PMMPTE PointerPte; + PMMPTE EndingPte; + int i; -void VMManager::ConstructVMA(VAddr Start, size_t Size, MemoryRegionType Type, VMAType VmaType, bool bFragFlag, DWORD Perms) -{ - VMAIter it_begin = m_MemoryRegionArray[Type].RegionMap.begin(); - VMAIter it_end = m_MemoryRegionArray[Type].RegionMap.end(); - VMAIter it; + Lock(); - VMAIter vma_handle = CarveVMA(Start, Size, Type); - VirtualMemoryArea& vma = vma_handle->second; - vma.type = VmaType; - vma.permissions = Perms; - vma.bFragmented = bFragFlag; + handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, m_NumPersistentPtes * PAGE_SIZE + m_NumPersistentPtes * 4 * 2 + sizeof(PersistedMemory), "PersistentMemory"); + if (handle == NULL) { + CxbxKrnlCleanup("Couldn't persist memory! CreateFileMapping failed"); + return; + } + addr = MapViewOfFile(handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); + if (addr == nullptr) { + CxbxKrnlCleanup("Couldn't persist memory! MapViewOfFile failed with error %08X", GetLastError()); + return; + } - // Depending on the splitting done by CarveVMA and the type of the adiacent vma's, there is no guarantee that the next - // or previous vma's are free. We are just going to iterate forward and backward until we find one. + persisted_mem = (PersistedMemory*)addr; + persisted_mem->NumOfPtes = m_NumPersistentPtes; - it = std::next(vma_handle); + if (xboxkrnl::LaunchDataPage != xbnullptr) { + persisted_mem->LaunchFrameAddresses[0] = (VAddr)xboxkrnl::LaunchDataPage; + DBG_PRINTF("Persisted LaunchDataPage\n"); + } - while (it != it_end) + if (xboxkrnl::AvSavedDataAddress != xbnullptr) { + persisted_mem->LaunchFrameAddresses[1] = (VAddr)xboxkrnl::AvSavedDataAddress; + DBG_PRINTF("Persisted Framebuffer\n"); + } + + i = 0; + PointerPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE); + + if (m_MmLayoutRetail) { + EndingPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE + XBOX_CONTIGUOUS_MEMORY_SIZE - 1); + } + else { + EndingPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE + CHIHIRO_CONTIGUOUS_MEMORY_SIZE - 1); + } + + while (PointerPte <= EndingPte) { - if (it->second.type == FreeVma) - { - m_MemoryRegionArray[Type].LastFree = it; - return; + if (PointerPte->Hardware.Valid != 0 && PointerPte->Hardware.Persist != 0) { + persisted_mem->Data[i] = GetVAddrMappedByPte(PointerPte); + persisted_mem->Data[m_NumPersistentPtes + i] = PointerPte->Default; + memcpy(&persisted_mem->Data[m_NumPersistentPtes * 2 + i * ONE_KB], (void*)(persisted_mem->Data[i]), PAGE_SIZE); + i++; } - ++it; + PointerPte++; } - if (vma_handle == it_begin) - { - // Already at the beginning of the map, bail out immediately + assert(i == m_NumPersistentPtes); - EmuLog(LOG_LEVEL::WARNING, "Can't find any more free space in the memory region %d! Virtual memory exhausted?", Type); - m_MemoryRegionArray[Type].LastFree = m_MemoryRegionArray[Type].RegionMap.end(); - return; - } + ipc_send_gui_update(IPC_UPDATE_GUI::VM_PERSIST_MEM, 1); - it = std::prev(vma_handle); - - while (true) - { - if (it->second.type == FreeVma) - { - m_MemoryRegionArray[Type].LastFree = it; - return; - } - - if (it == it_begin) { break; } - --it; - } - - // ergo720: I don't expect this to happen since it would mean we have exhausted the virtual space in the memory region, - // but it's just in case it does - - EmuLog(LOG_LEVEL::WARNING, "Can't find any more free space in the memory region %d! Virtual memory exhausted?", Type); - - m_MemoryRegionArray[Type].LastFree = m_MemoryRegionArray[Type].RegionMap.end(); - - return; + Unlock(); } VAddr VMManager::DbgTestPte(VAddr addr, PMMPTE Pte, bool bWriteCheck) @@ -596,128 +599,24 @@ void VMManager::PersistMemory(VAddr addr, size_t Size, bool bPersist) PointerPte = GetPteAddress(addr); EndingPte = GetPteAddress(addr + Size - 1); - while (PointerPte <= EndingPte) - { - PointerPte->Hardware.Persist = bPersist ? 1 : 0; - PointerPte++; - } - - // Now, if the supplied address is that of the launch data page or the frame buffer, then we store it inside the free - // space of the allocation of the d3d page, since we know it's persisted and it's always located at 0x80000000 - - if (addr != CONTIGUOUS_MEMORY_BASE && // D3D - addr != CONTIGUOUS_MEMORY_BASE + PAGE_DIRECTORY_PHYSICAL_ADDRESS && // page directory - addr != CONTIGUOUS_MEMORY_BASE + XBE_IMAGE_BASE && // dummy kernel - addr != CONTIGUOUS_MEMORY_BASE + (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(XBOX_PFN_DATABASE_PHYSICAL_PAGE) && // pfn - addr != CONTIGUOUS_MEMORY_BASE + (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(CHIHIRO_PFN_DATABASE_PHYSICAL_PAGE) // pfn - ) - { - if ((xboxkrnl::PVOID)addr == xboxkrnl::LaunchDataPage) + if (bPersist) { + while (PointerPte <= EndingPte) { - if (bPersist) - { - *(VAddr*)(CONTIGUOUS_MEMORY_BASE + PAGE_SIZE - 4) = addr; - EmuLog(LOG_LEVEL::DEBUG, "Persisting LaunchDataPage"); - } - else - { - *(VAddr*)(CONTIGUOUS_MEMORY_BASE + PAGE_SIZE - 4) = NULL; - EmuLog(LOG_LEVEL::DEBUG, "Forgetting LaunchDataPage"); - } - } - else - { - if (bPersist) - { - *(VAddr*)(CONTIGUOUS_MEMORY_BASE + PAGE_SIZE - 8) = addr; - EmuLog(LOG_LEVEL::DEBUG, "Persisting FrameBuffer"); - } - else - { - *(VAddr*)(CONTIGUOUS_MEMORY_BASE + PAGE_SIZE - 8) = NULL; - EmuLog(LOG_LEVEL::DEBUG, "Forgetting FrameBuffer"); - } - } + PointerPte->Hardware.Persist = 1; + m_NumPersistentPtes++; + PointerPte++; } + } + else { + while (PointerPte <= EndingPte) + { + PointerPte->Hardware.Persist = 0; + m_NumPersistentPtes--; + PointerPte++; } } - Unlock(); } -void VMManager::RestorePersistentMemory() -{ - // We are going to loop on the contiguous pte region looking for persistent memory marked by the Persist bit in the pte. - // If we find them we keep the pte in that state and remove the page from the free list, otherwise we zero the pte. - - PMMPTE PointerPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE); - PMMPTE EndingPte; - - if (m_MmLayoutRetail) { - EndingPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE + XBOX_CONTIGUOUS_MEMORY_SIZE - 1); - } - else { - EndingPte = GetPteAddress(CONTIGUOUS_MEMORY_BASE + CHIHIRO_CONTIGUOUS_MEMORY_SIZE - 1); - } - - while (PointerPte <= EndingPte) - { - PFN pfn; - if (PointerPte->Hardware.Valid != 0 && PointerPte->Hardware.Persist != 0) { - RemoveFree(1, &pfn, 0, PointerPte->Hardware.PFN, PointerPte->Hardware.PFN); - } - else { - // Clear also the page. We could use RtlFillMemoryULong, but that will fill up the kernel log quite quickly, so we just - // use memset instead - memset((void*)(GetVAddrMappedByPte(PointerPte)), 0, PAGE_SIZE); - WRITE_ZERO_PTE(PointerPte); - } - PointerPte++; - } - - // Zero all the remaining pte's - EndingPte += 1; - xboxkrnl::RtlFillMemoryUlong((void*)PAGE_TABLES_BASE, (VAddr)GetPteAddress(CONTIGUOUS_MEMORY_BASE) - PAGE_TABLES_BASE, 0); - xboxkrnl::RtlFillMemoryUlong((void*)EndingPte, PAGE_TABLES_END + 1 - (VAddr)EndingPte, 0); - - // Zero all the entries of the PFN database - if (m_MmLayoutRetail) { - xboxkrnl::RtlFillMemoryUlong((void*)XBOX_PFN_ADDRESS, X64KB, 0); // Xbox: 64 KiB - } - else if (m_MmLayoutChihiro) { - xboxkrnl::RtlFillMemoryUlong((void*)CHIHIRO_PFN_ADDRESS, X64KB * 2, 0); // Chihiro: 128 KiB - } - else { - xboxkrnl::RtlFillMemoryUlong((void*)XBOX_PFN_ADDRESS, X64KB * 2, 0); // Debug: 128 KiB - } - - // Now we need to restore the launch data page and the frame buffer pointers to their correct values - - { - VAddr LauchDataAddress = *(VAddr*)(CONTIGUOUS_MEMORY_BASE + PAGE_SIZE - 4); - VAddr FrameBufferAddress = *(VAddr*)(CONTIGUOUS_MEMORY_BASE + PAGE_SIZE - 8); - - if (LauchDataAddress != 0 && IS_PHYSICAL_ADDRESS(LauchDataAddress)) { - xboxkrnl::LaunchDataPage = (xboxkrnl::PLAUNCH_DATA_PAGE)LauchDataAddress; - *(VAddr*)(CONTIGUOUS_MEMORY_BASE + PAGE_SIZE - 4) = NULL; - - RestorePersistentAllocation(LauchDataAddress, GetPteAddress(LauchDataAddress)->Hardware.PFN, - GetPteAddress(LauchDataAddress)->Hardware.PFN, ContiguousType); - - EmuLog(LOG_LEVEL::DEBUG, "Restored LaunchDataPage"); - } - - if (FrameBufferAddress != 0 && IS_PHYSICAL_ADDRESS(FrameBufferAddress)) { - xboxkrnl::AvSavedDataAddress = (xboxkrnl::PVOID)FrameBufferAddress; - *(VAddr*)(CONTIGUOUS_MEMORY_BASE + PAGE_SIZE - 8) = NULL; - - RestorePersistentAllocation(FrameBufferAddress, GetPteAddress(FrameBufferAddress)->Hardware.PFN, - GetPteAddress(FrameBufferAddress)->Hardware.PFN + (QuerySize(FrameBufferAddress, false) >> PAGE_SHIFT) - 1, ContiguousType); - - EmuLog(LOG_LEVEL::DEBUG, "Restored FrameBuffer"); - } - } -} - VAddr VMManager::Allocate(size_t Size) { LOG_FUNC_ONE_ARG(Size); @@ -726,15 +625,8 @@ VAddr VMManager::Allocate(size_t Size) PMMPTE PointerPte; PMMPTE EndingPte; PFN pfn; - PFN EndingPfn; PFN_COUNT PteNumber; VAddr addr; - MappingFn MappingRoutine; - bool bVAlloc = false; - - // ergo720: I'm not sure why this routine is needed at all, but its usage (together with AllocateZeroed) is quite - // widespread in the D3D patches. I think that most of those functions should use the Nt or the heap functions instead, - // but, until those are properly implemented, this routine is here to stay if (!Size) { RETURN(NULL); } @@ -744,46 +636,15 @@ VAddr VMManager::Allocate(size_t Size) if (!IsMappable(PteNumber, true, m_MmLayoutDebug && m_bAllowNonDebuggerOnTop64MiB ? true : false)) { goto Fail; } - ConvertXboxToPteProtection(XBOX_PAGE_EXECUTE_READWRITE, &TempPte); + ConvertXboxToPtePermissions(XBOX_PAGE_EXECUTE_READWRITE, &TempPte); - if (RemoveFree(PteNumber, &pfn, 0, 0, m_MmLayoutDebug && !m_bAllowNonDebuggerOnTop64MiB ? XBOX_HIGHEST_PHYSICAL_PAGE - : m_HighestPage)) // MapViewOfFileEx path - { - MappingRoutine = &VMManager::MapBlockWithMapViewOfFileEx; - addr = MapMemoryBlock(MappingRoutine, UserRegion, PteNumber, pfn); + addr = MapMemoryBlock(UserRegion, PteNumber, MEM_RESERVE | MEM_COMMIT, false); + if (!addr) { goto Fail; } - if (!addr) - { - InsertFree(pfn, pfn + PteNumber - 1); - goto Fail; - } - } - else // VirtualAlloc path - { - // We couldn't find a contiguous block to map with MapViewOfFileEx, so we try to salvage this allocation with - // VirtualAlloc. Note that we don't try to map contiguous blocks from non-contiguous ones because we could run into - // a race condition in which (for example) we can map the 1st block but in the midtime somebody else allocates in - // our intended region before we could do. - - bVAlloc = true; - MappingRoutine = &VMManager::MapBlockWithVirtualAlloc; - addr = MapMemoryBlock(MappingRoutine, UserRegion, PteNumber, 0); - - if (!addr) { goto Fail; } - } - - // check if we have to construct the PT's for this allocation + // Check if we have to construct the PT's for this allocation if (!AllocatePT(PteNumber << PAGE_SHIFT, addr)) { - if (bVAlloc) - { - VirtualFree((void*)addr, 0, MEM_RELEASE); - } - else - { - UnmapViewOfFile((void*)ROUND_DOWN(addr, m_AllocationGranularity)); - InsertFree(pfn, pfn + PteNumber - 1); - } + VirtualFree((void*)addr, 0, MEM_RELEASE); goto Fail; } @@ -791,28 +652,17 @@ VAddr VMManager::Allocate(size_t Size) PointerPte = GetPteAddress(addr); EndingPte = PointerPte + PteNumber - 1; - if (bVAlloc) + while (PointerPte <= EndingPte) { - PFN TempPfn; - // With VirtualAlloc we grab one page at a time to avoid fragmentation issues - while (PointerPte <= EndingPte) - { - RemoveFree(1, &TempPfn, 0, 0, m_MmLayoutDebug && !m_bAllowNonDebuggerOnTop64MiB ? XBOX_HIGHEST_PHYSICAL_PAGE - : m_HighestPage); - WritePfn(TempPfn, TempPfn, PointerPte, ImageType); - WritePte(PointerPte, PointerPte, TempPte, TempPfn); + RemoveFree(1, &pfn, 0, 0, m_MmLayoutDebug && !m_bAllowNonDebuggerOnTop64MiB ? XBOX_HIGHEST_PHYSICAL_PAGE + : m_HighestPage); + WritePfn(pfn, pfn, PointerPte, ImageType); + WritePte(PointerPte, PointerPte, TempPte, pfn); - PointerPte++; - } - } - else - { - EndingPfn = pfn + PteNumber - 1; - WritePfn(pfn, EndingPfn, PointerPte, ImageType); - WritePte(PointerPte, EndingPte, TempPte, pfn); + PointerPte++; } - ConstructVMA(addr, PteNumber << PAGE_SHIFT, UserRegion, AllocatedVma, bVAlloc); + ConstructVMA(addr, PteNumber << PAGE_SHIFT, UserRegion, AllocatedVma); UpdateMemoryPermissions(addr, PteNumber << PAGE_SHIFT, XBOX_PAGE_EXECUTE_READWRITE); Unlock(); @@ -846,24 +696,20 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz PMMPTE PointerPte; PMMPTE EndingPte; PFN pfn; - PFN EndingPfn; PFN LowestAcceptablePfn; PFN HighestAcceptablePfn; PFN_COUNT PteNumber; PFN_COUNT PagesNumber; VAddr addr; - MappingFn MappingRoutine; - bool bVAlloc; MemoryRegionType MemoryType; // NOTE: AllocateSystemMemory won't allocate a physical page for the guard page (if requested) and just adds an extra // unallocated virtual page in front of the mapped allocation. For this reason we will decommmit later the extra guard page allocated - if (!Size || !ConvertXboxToSystemPteProtection(Perms, &TempPte)) { RETURN(NULL); } + if (!Size || !ConvertXboxToSystemPtePermissions(Perms, &TempPte)) { RETURN(NULL); } LowestAcceptablePfn = 0; HighestAcceptablePfn = m_MmLayoutDebug ? XBOX_HIGHEST_PHYSICAL_PAGE : m_HighestPage; - bVAlloc = false; MemoryType = SystemRegion; Lock(); @@ -884,47 +730,14 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz } else { if (!IsMappable(PteNumber, true, false)) { goto Fail; } } - if ((BusyType != DebuggerType) && (RemoveFree(PteNumber, &pfn, 0, LowestAcceptablePfn, HighestAcceptablePfn))) // MapViewOfFileEx path - { - MappingRoutine = &VMManager::MapBlockWithMapViewOfFileEx; - addr = MapMemoryBlock(MappingRoutine, MemoryType, PteNumber, pfn); + addr = MapMemoryBlock(MemoryType, PteNumber, MEM_COMMIT, true); - if (!addr) - { - InsertFree(pfn, pfn + PteNumber - 1); - goto Fail; - } - } - else // VirtualAlloc path - { - // We couldn't find a contiguous block to map with MapViewOfFileEx, so we try to salvage this allocation with - // VirtualAlloc. Note that we don't try to map contiguous blocks from non-contiguous ones because we could run into - // a race condition in which (for example) we can map the 1st block but in the midtime somebody else allocates in - // our intended region before we could do. - - bVAlloc = true; - MappingRoutine = &VMManager::MapBlockWithVirtualAlloc; - addr = MapMemoryBlock(MappingRoutine, MemoryType, PteNumber, 0); - - if (!addr) { goto Fail; } - } + if (!addr) { goto Fail; } // check if we have to construct the PT's for this allocation if (!AllocatePT(PteNumber << PAGE_SHIFT, addr)) { - // If we reach here it means we had enough memory for the allocation but not for PT's mapping it, so this - // allocation must fail instead. - - if (bVAlloc) - { - VirtualFree((void*)addr, 0, MEM_RELEASE); - } - else - { - // Recalculate the granularity aligned addr - UnmapViewOfFile((void*)ROUND_DOWN(addr, m_AllocationGranularity)); - InsertFree(pfn, pfn + PteNumber - 1); - } + VirtualFree((void*)addr, 0, MEM_DECOMMIT); goto Fail; } @@ -942,31 +755,18 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz } EndingPte = PointerPte + PagesNumber - 1; - if (bVAlloc) + while (PointerPte <= EndingPte) { - PFN TempPfn; - // With VirtualAlloc we grab one page at a time to avoid fragmentation issues - while (PointerPte <= EndingPte) - { - RemoveFree(1, &TempPfn, 0, LowestAcceptablePfn, HighestAcceptablePfn); - WritePfn(TempPfn, TempPfn, PointerPte, BusyType); - WritePte(PointerPte, PointerPte, TempPte, TempPfn); + RemoveFree(1, &pfn, 0, LowestAcceptablePfn, HighestAcceptablePfn); + WritePfn(pfn, pfn, PointerPte, BusyType); + WritePte(PointerPte, PointerPte, TempPte, pfn); - PointerPte++; - } - } - else - { - if (bAddGuardPage) { InsertFree(pfn, pfn); pfn++; } // Free the commited guard page - EndingPfn = pfn + PagesNumber - 1; - WritePte(PointerPte, EndingPte, TempPte, pfn); - WritePfn(pfn, EndingPfn, PointerPte, BusyType); + PointerPte++; } EndingPte->Hardware.GuardOrEnd = 1; UpdateMemoryPermissions(bAddGuardPage ? addr + PAGE_SIZE : addr, PagesNumber << PAGE_SHIFT, Perms); - - ConstructVMA(addr, PteNumber << PAGE_SHIFT, MemoryType, AllocatedVma, bVAlloc); + ConstructVMA(addr, PteNumber << PAGE_SHIFT, MemoryType, AllocatedVma); Unlock(); RETURN(addr); @@ -976,7 +776,7 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz RETURN(NULL); } -VAddr VMManager::AllocateContiguous(size_t Size, PAddr LowestAddress, PAddr HighestAddress, ULONG Alignment, DWORD Perms) +VAddr VMManager::AllocateContiguousMemory(size_t Size, PAddr LowestAddress, PAddr HighestAddress, ULONG Alignment, DWORD Perms) { LOG_FUNC_BEGIN LOG_FUNC_ARG(Size) @@ -986,40 +786,56 @@ VAddr VMManager::AllocateContiguous(size_t Size, PAddr LowestAddress, PAddr High LOG_FUNC_ARG(Perms) LOG_FUNC_END; - MMPTE TempPte; - PMMPTE PointerPte; - PMMPTE EndingPte; PFN PfnAlignment; - PFN LowerPfn; - PFN HigherPfn; - PFN pfn; - PFN EndingPfn; + PFN LowestPfn; + PFN HighestPfn; PFN_COUNT PteNumber; - VAddr addr; + VAddr Addr; - if (!Size || !ConvertXboxToSystemPteProtection(Perms, &TempPte)) { RETURN(NULL); } + if (!Size) { RETURN(NULL); } PteNumber = ROUND_UP_4K(Size) >> PAGE_SHIFT; - LowerPfn = LowestAddress >> PAGE_SHIFT; - HigherPfn = HighestAddress >> PAGE_SHIFT; + LowestPfn = LowestAddress >> PAGE_SHIFT; + HighestPfn = HighestAddress >> PAGE_SHIFT; PfnAlignment = Alignment >> PAGE_SHIFT; - if (HigherPfn > m_MaxContiguousPfn) { HigherPfn = m_MaxContiguousPfn; } - if (LowerPfn > HigherPfn) { LowerPfn = HigherPfn; } + if (HighestPfn > m_MaxContiguousPfn) { HighestPfn = m_MaxContiguousPfn; } + if (LowestPfn > HighestPfn) { LowestPfn = HighestPfn; } if (!PfnAlignment) { PfnAlignment = 1; } Lock(); - if (!IsMappable(PteNumber, true, false)) { goto Fail; } - if (!RemoveFree(PteNumber, &pfn, PfnAlignment, LowerPfn, HigherPfn)) { goto Fail; } + if (!IsMappable(PteNumber, true, false)) { + Unlock(); + RETURN(NULL); + } + + Addr = AllocateContiguousMemoryInternal(PteNumber, LowestPfn, HighestPfn, PfnAlignment, Perms); + + Unlock(); + + RETURN(Addr); +} + +VAddr VMManager::AllocateContiguousMemoryInternal(PFN_COUNT NumberOfPages, PFN LowestPfn, PFN HighestPfn, PFN PfnAlignment, DWORD Perms) +{ + MMPTE TempPte; + PMMPTE PointerPte; + PMMPTE EndingPte; + PFN pfn; + PFN EndingPfn; + VAddr addr; + + if (!ConvertXboxToSystemPtePermissions(Perms, &TempPte)) { goto Fail; } + if (!RemoveFree(NumberOfPages, &pfn, PfnAlignment, LowestPfn, HighestPfn)) { goto Fail; } addr = CONTIGUOUS_MEMORY_BASE + (pfn << PAGE_SHIFT); assert(CHECK_ALIGNMENT(pfn, PfnAlignment)); // check if the page alignment is correct - EndingPfn = pfn + PteNumber - 1; + EndingPfn = pfn + NumberOfPages - 1; // check if we have to construct the PT's for this allocation - if (!AllocatePT(PteNumber << PAGE_SHIFT, addr)) + if (!AllocatePT(NumberOfPages << PAGE_SHIFT, addr)) { InsertFree(pfn, EndingPfn); goto Fail; @@ -1027,21 +843,19 @@ VAddr VMManager::AllocateContiguous(size_t Size, PAddr LowestAddress, PAddr High // Finally, write the pte's and the pfn's PointerPte = GetPteAddress(addr); - EndingPte = PointerPte + PteNumber - 1; + EndingPte = PointerPte + NumberOfPages - 1; WritePte(PointerPte, EndingPte, TempPte, pfn); WritePfn(pfn, EndingPfn, PointerPte, ContiguousType); EndingPte->Hardware.GuardOrEnd = 1; - ConstructVMA(addr, PteNumber << PAGE_SHIFT, ContiguousRegion, AllocatedVma, false); - UpdateMemoryPermissions(addr, PteNumber << PAGE_SHIFT, Perms); + ConstructVMA(addr, NumberOfPages << PAGE_SHIFT, ContiguousRegion, AllocatedVma, false); + UpdateMemoryPermissions(addr, NumberOfPages << PAGE_SHIFT, Perms); - Unlock(); - RETURN(addr); + return addr; Fail: - Unlock(); - RETURN(NULL); + return NULL; } VAddr VMManager::MapDeviceMemory(PAddr Paddr, size_t Size, DWORD Perms) @@ -1058,9 +872,8 @@ VAddr VMManager::MapDeviceMemory(PAddr Paddr, size_t Size, DWORD Perms) PFN pfn; PFN_COUNT PteNumber; VAddr addr; - MappingFn MappingRoutine; - if (!Size || !ConvertXboxToSystemPteProtection(Perms, &TempPte)) { RETURN(NULL); } + if (!Size || !ConvertXboxToSystemPtePermissions(Perms, &TempPte)) { RETURN(NULL); } // Is it a physical address for hardware devices (flash, NV2A, etc) ? if (Paddr >= XBOX_WRITE_COMBINED_BASE /*&& Paddr + Size <= XBOX_UNCACHED_END*/) @@ -1074,21 +887,18 @@ VAddr VMManager::MapDeviceMemory(PAddr Paddr, size_t Size, DWORD Perms) // allocate any physical memory for it, we still need to reserve the memory with VirtualAlloc. If we don't, then // we are likely to collide with other system allocations in the region at some point, which will overwrite and corrupt // the affected allocation. We only reserve the memory so that the access will trigger an access violation and it will - // be handled by EmuException. However, RegisterBAR doesn't register any known device in the system region and so - // EmuX86_Read/Write are going to fail with the error "Unknown address" and resume execution as it is... + // be handled by EmuException. PteNumber = PAGES_SPANNED(Paddr, Size); Lock(); - MappingRoutine = &VMManager::ReserveBlockWithVirtualAlloc; - addr = MapMemoryBlock(MappingRoutine, SystemRegion, PteNumber, 0); + addr = MapMemoryBlock(SystemRegion, PteNumber, 0xFFFFFFFF, true); if (!addr) { goto Fail; } - // check if we have to construct the PT's for this allocation + // Check if we have to construct the PT's for this allocation if (!AllocatePT(PteNumber << PAGE_SHIFT, addr)) { - VirtualFree((void*)addr, 0, MEM_RELEASE); goto Fail; } @@ -1098,7 +908,6 @@ VAddr VMManager::MapDeviceMemory(PAddr Paddr, size_t Size, DWORD Perms) pfn = Paddr >> PAGE_SHIFT; WritePte(PointerPte, EndingPte, TempPte, pfn); - ConstructVMA(addr, PteNumber << PAGE_SHIFT, SystemRegion, ReservedVma, true); Unlock(); @@ -1115,10 +924,10 @@ void VMManager::Deallocate(VAddr addr) LOG_FUNC_ARG(addr) LOG_FUNC_END; + PMMPTE PointerPte; PMMPTE StartingPte; PMMPTE EndingPte; PFN pfn; - PFN EndingPfn; PFN_COUNT PteNumber; VMAIter it; bool bOverflow; @@ -1136,30 +945,18 @@ void VMManager::Deallocate(VAddr addr) return; } - StartingPte = GetPteAddress(addr); - EndingPte = StartingPte + (it->second.size >> PAGE_SHIFT) - 1; - + PointerPte = GetPteAddress(addr); + EndingPte = PointerPte + (it->second.size >> PAGE_SHIFT) - 1; + StartingPte = PointerPte; PteNumber = EndingPte - StartingPte + 1; - if (it->second.bFragmented) + while (PointerPte <= EndingPte) { - PFN TempPfn; - // With VirtualAlloc we free one page at a time since the allocated pfn's are not contiguous - while (StartingPte <= EndingPte) - { - TempPfn = StartingPte->Hardware.PFN; - InsertFree(TempPfn, TempPfn); - WritePfn(TempPfn, TempPfn, StartingPte, ImageType, true); + pfn = PointerPte->Hardware.PFN; + InsertFree(pfn, pfn); + WritePfn(pfn, pfn, PointerPte, ImageType, true); - StartingPte++; - } - } - else - { - pfn = StartingPte->Hardware.PFN; - EndingPfn = pfn + (EndingPte - StartingPte); - InsertFree(pfn, EndingPfn); - WritePfn(pfn, EndingPfn, StartingPte, ImageType, true); + PointerPte++; } WritePte(StartingPte, EndingPte, *StartingPte, 0, true); @@ -1169,7 +966,7 @@ void VMManager::Deallocate(VAddr addr) Unlock(); } -void VMManager::DeallocateContiguous(VAddr addr) +void VMManager::DeallocateContiguousMemory(VAddr addr) { LOG_FUNC_BEGIN LOG_FUNC_ARG(addr) @@ -1217,10 +1014,10 @@ PFN_COUNT VMManager::DeallocateSystemMemory(PageType BusyType, VAddr addr, size_ LOG_FUNC_ARG(Size) LOG_FUNC_END; + PMMPTE PointerPte; PMMPTE StartingPte; PMMPTE EndingPte; PFN pfn; - PFN EndingPfn; PFN_COUNT PteNumber; VMAIter it; MemoryRegionType MemoryType = SystemRegion; @@ -1250,36 +1047,25 @@ PFN_COUNT VMManager::DeallocateSystemMemory(PageType BusyType, VAddr addr, size_ if (Size) { Size = ROUND_UP_4K(Size); } else { Size = it->second.size; } - StartingPte = GetPteAddress(addr); - if (StartingPte->Hardware.Valid == 0) { - WritePte(StartingPte, StartingPte, *StartingPte, 0, true); // this is the guard page of the stack - StartingPte++; + PointerPte = GetPteAddress(addr); + if (PointerPte->Hardware.Valid == 0) { + WritePte(PointerPte, PointerPte, *PointerPte, 0, true); // this is the guard page of the stack + PointerPte++; Size -= PAGE_SIZE; bGuardPageAdded = true; } - EndingPte = StartingPte + (Size >> PAGE_SHIFT) - 1; - PteNumber = EndingPte - StartingPte + 1; + EndingPte = PointerPte + (Size >> PAGE_SHIFT) - 1; + StartingPte = PointerPte; + PteNumber = EndingPte - PointerPte + 1; - if (it->second.bFragmented) + while (PointerPte <= EndingPte) { - PFN TempPfn; - // With VirtualAlloc we free one page at a time since the allocated pfn's are not contiguous - while (StartingPte <= EndingPte) - { - TempPfn = StartingPte->Hardware.PFN; - InsertFree(TempPfn, TempPfn); - WritePfn(TempPfn, TempPfn, StartingPte, BusyType, true); + pfn = PointerPte->Hardware.PFN; + InsertFree(pfn, pfn); + WritePfn(pfn, pfn, PointerPte, BusyType, true); - StartingPte++; - } - } - else - { - pfn = StartingPte->Hardware.PFN; - EndingPfn = pfn + (EndingPte - StartingPte); - InsertFree(pfn, EndingPfn); - WritePfn(pfn, EndingPfn, StartingPte, BusyType, true); + PointerPte++; } WritePte(StartingPte, EndingPte, *StartingPte, 0, true); @@ -1350,7 +1136,7 @@ void VMManager::Protect(VAddr addr, size_t Size, DWORD NewPerms) assert(IS_PHYSICAL_ADDRESS(addr) || IS_SYSTEM_ADDRESS(addr)); - if (!Size || !ConvertXboxToSystemPteProtection(NewPerms, &NewPermsPte)) { return; } + if (!Size || !ConvertXboxToSystemPtePermissions(NewPerms, &NewPermsPte)) { return; } Lock(); @@ -1400,7 +1186,7 @@ DWORD VMManager::QueryProtection(VAddr addr) if ((TempPte.Hardware.Valid != 0) || ((TempPte.Default != 0) && (addr <= HIGHEST_USER_ADDRESS))) { - Protect = ConvertPteToXboxProtection(TempPte.Default); + Protect = ConvertPteToXboxPermissions(TempPte.Default); } else { @@ -1409,7 +1195,7 @@ DWORD VMManager::QueryProtection(VAddr addr) } else { - Protect = ConvertPteToXboxProtection(TempPte.Default); // large page, query it immediately + Protect = ConvertPteToXboxPermissions(TempPte.Default); // large page, query it immediately } } else @@ -1569,7 +1355,6 @@ xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG ZeroBit VAddr CapturedBase = *addr; size_t CapturedSize = *Size; VAddr MaxAllowedAddress; - MappingFn MappingRoutine; VAddr AlignedCapturedBase; size_t AlignedCapturedSize; VMAIter it; @@ -1599,7 +1384,7 @@ xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG ZeroBit // At least MEM_RESET, MEM_COMMIT or MEM_RESERVE must be set if ((AllocationType & (XBOX_MEM_COMMIT | XBOX_MEM_RESERVE | XBOX_MEM_RESET)) == 0) { RETURN(STATUS_INVALID_PARAMETER); } - if (!ConvertXboxToPteProtection(Protect, &TempPte)) { RETURN(STATUS_INVALID_PAGE_PROTECTION); } + if (!ConvertXboxToPtePermissions(Protect, &TempPte)) { RETURN(STATUS_INVALID_PAGE_PROTECTION); } EmuLog(LOG_LEVEL::DEBUG, "%s requested range : 0x%.8X - 0x%.8X", __func__, CapturedBase, CapturedBase + CapturedSize); @@ -1623,17 +1408,11 @@ xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG ZeroBit MaxAllowedAddress = HIGHEST_USER_ADDRESS; } - // With XBOX_MEM_TOP_DOWN we will always set the LastFree iterator to the vma containing MaxAllowedAddress in order to map the - // block at the highest possible address. This can become quite slow if a title repeatedly allocates virtual memory with this flag. - // See here: https://randomascii.wordpress.com/2011/08/05/making-virtualalloc-arbitrarily-slower/ - if (AllocationType & XBOX_MEM_TOP_DOWN) { m_MemoryRegionArray[UserRegion].LastFree = GetVMAIterator(MaxAllowedAddress, UserRegion); } // On the Xbox, blocks reserved by NtAllocateVirtualMemory are 64K aligned and the size is rounded up on a 4K boundary. - // This can lead to unaligned blocks if the host granularity is not 64K. Fortunately, Windows uses that - MappingRoutine = &VMManager::ReserveBlockWithVirtualAlloc; - AlignedCapturedBase = MapMemoryBlock(MappingRoutine, UserRegion, AlignedCapturedSize >> PAGE_SHIFT, 0, MaxAllowedAddress); + AlignedCapturedBase = MapMemoryBlock(UserRegion, AlignedCapturedSize >> PAGE_SHIFT, MEM_RESERVE, false, MaxAllowedAddress); if (!AlignedCapturedBase) { status = STATUS_NO_MEMORY; goto Exit; } } @@ -1665,7 +1444,7 @@ xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG ZeroBit } else { if ((VAddr)VirtualAlloc((void*)AlignedCapturedBase, AlignedCapturedSize, MEM_RESERVE, - ConvertXboxToWinProtection(PatchXboxPermissions(Protect)) & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE)) != AlignedCapturedBase) + ConvertXboxToWinPermissions(PatchXboxPermissions(Protect)) & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE)) != AlignedCapturedBase) { // An host allocation is already mapped there, report an error @@ -1676,7 +1455,7 @@ xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG ZeroBit } m_VirtualMemoryBytesReserved += AlignedCapturedSize; - ConstructVMA(AlignedCapturedBase, AlignedCapturedSize, UserRegion, ReservedVma, true, Protect); + ConstructVMA(AlignedCapturedBase, AlignedCapturedSize, UserRegion, ReservedVma, Protect); if ((AllocationType & XBOX_MEM_COMMIT) == 0) { @@ -1750,7 +1529,7 @@ xboxkrnl::NTSTATUS VMManager::XbAllocateVirtualMemory(VAddr* addr, ULONG ZeroBit if (AlignedCapturedBase >= XBE_MAX_VA) { if (!VirtualAlloc((void*)AlignedCapturedBase, AlignedCapturedSize, MEM_COMMIT, - (ConvertXboxToWinProtection(PatchXboxPermissions(Protect))) & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE))) + (ConvertXboxToWinPermissions(PatchXboxPermissions(Protect))) & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE))) { EmuLog(LOG_LEVEL::DEBUG, "%s: VirtualAlloc failed to commit the memory! The error was %d", __func__, GetLastError()); status = STATUS_NO_MEMORY; @@ -2006,7 +1785,7 @@ xboxkrnl::NTSTATUS VMManager::XbVirtualProtect(VAddr* addr, size_t* Size, DWORD* // Size cannot be zero if (CapturedSize == 0) { RETURN(STATUS_INVALID_PARAMETER); } - if (!ConvertXboxToPteProtection(NewPerms, &NewPermsPte)) { RETURN(STATUS_INVALID_PAGE_PROTECTION); } + if (!ConvertXboxToPtePermissions(NewPerms, &NewPermsPte)) { RETURN(STATUS_INVALID_PAGE_PROTECTION); } AlignedCapturedBase = ROUND_DOWN_4K(CapturedBase); AlignedCapturedSize = (PAGES_SPANNED(CapturedBase, CapturedSize)) << PAGE_SHIFT; @@ -2082,7 +1861,7 @@ xboxkrnl::NTSTATUS VMManager::XbVirtualProtect(VAddr* addr, size_t* Size, DWORD* *addr = AlignedCapturedBase; *Size = AlignedCapturedSize; - *Protect = ConvertPteToXboxProtection(OldPermsPte.Default); + *Protect = ConvertPteToXboxPermissions(OldPermsPte.Default); status = STATUS_SUCCESS; Exit: @@ -2173,7 +1952,7 @@ xboxkrnl::NTSTATUS VMManager::XbVirtualMemoryStatistics(VAddr addr, xboxkrnl::PM if (PointerPde->Hardware.Valid != 0 && PointerPte->Default != 0) { CurrentState = XBOX_MEM_COMMIT; PermissionsOfFirstPte = (PointerPte->Default & PTE_VALID_PROTECTION_MASK); - CurrentProtect = ConvertPteToXboxProtection(PointerPte->Default); + CurrentProtect = ConvertPteToXboxPermissions(PointerPte->Default); } else { CurrentState = XBOX_MEM_RESERVE; } @@ -2230,18 +2009,12 @@ xboxkrnl::NTSTATUS VMManager::XbVirtualMemoryStatistics(VAddr addr, xboxkrnl::PM return STATUS_SUCCESS; } -VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, PFN_COUNT PteNumber, PFN pfn, VAddr HighestAddress) +VAddr VMManager::MapMemoryBlock(MemoryRegionType Type, PFN_COUNT PteNumber, DWORD Permissions, bool b64Blocks, VAddr HighestAddress) { VAddr addr; VMAIter it = m_MemoryRegionArray[Type].LastFree; size_t Size = PteNumber << PAGE_SHIFT; - DWORD FileOffsetLow = 0; - if (pfn) // MapViewOfFileEx specific - { - FileOffsetLow = ROUND_DOWN(pfn << PAGE_SHIFT, m_AllocationGranularity); - Size = (pfn << PAGE_SHIFT) + Size - FileOffsetLow; - } VMAIter end_it = m_MemoryRegionArray[Type].RegionMap.end(); @@ -2265,6 +2038,14 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, addr = ROUND_UP(addr, m_AllocationGranularity); } + if (Permissions == 0xFFFFFFFF) { + if (addr + Size - 1 < it->first + it->second.size) { + return addr; + } + ++it; + continue; + } + // Note that, even in free regions, somebody outside the manager could have allocated the memory so we just // keep on trying until we succeed or fail entirely. @@ -2272,9 +2053,27 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, if (HighestAddress && (it->first + it->second.size > HighestAddress + 1)) { vma_end = HighestAddress + 1; } else { vma_end = it->first + it->second.size; } - addr = (this->*MappingRoutine)(addr, Size, vma_end, FileOffsetLow, pfn); - - if (addr) { return addr; } + if (b64Blocks) { + if (addr + Size - 1 < vma_end) { + // The memory was reserved by the loader, commit it in 64kb blocks + VAddr start_addr = addr; + size_t start_size = 0; + while (addr < vma_end) { + addr = MapHostMemory(addr, m_AllocationGranularity, vma_end, Permissions); + assert(addr); + start_size += m_AllocationGranularity; + if (start_size >= Size) { + return start_addr; + } + addr += m_AllocationGranularity; + } + assert(0); + } + } + else { + addr = MapHostMemory(addr, Size, vma_end, Permissions); + if (addr) { return addr; } + } ++it; } @@ -2307,9 +2106,35 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, size_t vma_end = it->first + it->second.size; - addr = (this->*MappingRoutine)(addr, Size, vma_end, FileOffsetLow, pfn); + if (Permissions == 0xFFFFFFFF) { + if (addr + Size - 1 < vma_end) { + return addr; + } + --it; + continue; + } - if (addr) { return addr; } + if (b64Blocks) { + if (addr + Size - 1 < vma_end) { + // The memory was reserved by the loader, commit it in 64kb blocks + VAddr start_addr = addr; + size_t start_size = 0; + while (addr < vma_end) { + addr = MapHostMemory(addr, m_AllocationGranularity, vma_end, Permissions); + assert(addr); + start_size += m_AllocationGranularity; + if (start_size >= Size) { + return start_addr; + } + addr += m_AllocationGranularity; + } + assert(0); + } + } + else { + addr = MapHostMemory(addr, Size, vma_end, Permissions); + if (addr) { return addr; } + } } if (it == begin_it) { break; } @@ -2324,36 +2149,11 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, return NULL; } -VAddr VMManager::MapBlockWithMapViewOfFileEx(VAddr StartingAddr, size_t ViewSize, size_t VmaEnd, DWORD OffsetLow, PFN pfn) -{ - for (; StartingAddr + ViewSize - 1 < VmaEnd; StartingAddr += m_AllocationGranularity) - { - if ((VAddr)MapViewOfFileEx(m_hContiguousFile, FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE, 0, OffsetLow, - ViewSize, (void*)StartingAddr) == StartingAddr) - { - return StartingAddr + (pfn << PAGE_SHIFT) - OffsetLow; // sum the offset into the file view - } - } - return NULL; -} - -VAddr VMManager::MapBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused, PFN Unused2) +VAddr VMManager::MapHostMemory(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Permissions) { for (; StartingAddr + Size - 1 < VmaEnd; StartingAddr += m_AllocationGranularity) { - if ((VAddr)VirtualAlloc((void*)StartingAddr, Size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE) == StartingAddr) - { - return StartingAddr; - } - } - return NULL; -} - -VAddr VMManager::ReserveBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused, PFN Unused2) -{ - for (; StartingAddr + Size - 1 < VmaEnd; StartingAddr += m_AllocationGranularity) - { - if ((VAddr)VirtualAlloc((void*)StartingAddr, Size, MEM_RESERVE, PAGE_NOACCESS) == StartingAddr) + if ((VAddr)VirtualAlloc((void*)StartingAddr, Size, Permissions, PAGE_EXECUTE_READWRITE) == StartingAddr) { return StartingAddr; } @@ -2380,12 +2180,6 @@ bool VMManager::IsValidVirtualAddress(const VAddr addr) if (PointerPte->Hardware.Valid == 0) // invalid pte -> addr is invalid goto InvalidAddress; - // The following check is needed to handle the special case where the address being queried falls inside the PTs region. - // The first-level pte is also a second-level pte for the pages in the 0xC0000000 region, that is, the pte's of the PTs - // are the pde's themselves. The check makes sure that we are not fooled into thinking that the PT to which addr falls - // into is valid because the corresponding pde is valid. Addr could still be invalid but the pde is marked valid simply - // because it's mapping a large page instead of the queried PT. - if (PointerPte->Hardware.LargePage != 0) // pte is actually a pde and it's mapping a large page -> addr is invalid goto InvalidAddress; @@ -2406,56 +2200,21 @@ PAddr VMManager::TranslateVAddrToPAddr(const VAddr addr) PAddr PAddr; PMMPTE PointerPte; - //MemoryRegionType Type; - - Lock(); // ergo720: horrendous hack, this identity maps all allocations done by the VMManager to keep the LLE USB working. // The problem is that if the user buffer pointed to by the TD is allocated by the VMManager with VirtualAlloc, then - // the physical allocation will not reside in memory.bin and if we tried to access the physical address of it, we - // would access a random page with undefined contents. + // the physical allocation will not reside in the contiguous memory and if we tried to access the physical address of it, + // we would access a random page with undefined contents. // NOTE: Once LLE CPU and MMU are implemented, this can be removed. - //if (IS_USER_ADDRESS(addr)) { Type = UserRegion; } - //else if (IS_PHYSICAL_ADDRESS(addr)) { Type = ContiguousRegion; } - //else if (IS_SYSTEM_ADDRESS(addr)) { Type = SystemRegion; } - //else if (IS_DEVKIT_ADDRESS(addr)) { Type = DevkitRegion; } - //else { Type = COUNTRegion; } - - if (true/*(addr >= PAGE_TABLES_BASE && addr <= PAGE_TABLES_END) || (Type != COUNTRegion && Type != ContiguousRegion)*/) { - if (IsValidVirtualAddress(addr)) { - EmuLog(LOG_LEVEL::WARNING, "Applying identity mapping hack to allocation at address 0x%X", addr); - Unlock(); - RETURN(addr); - /* - if (Type == UserRegion) { - EmuLog(LOG_LEVEL::WARNING, "Applying identity mapping hack to allocation at address 0x%X", addr); - Unlock(); - RETURN(addr); // committed pages in the user region always use VirtualAlloc - } - else if (Type != COUNTRegion) { - VMAIter it = GetVMAIterator(addr, Type); - if (it != m_MemoryRegionArray[Type].RegionMap.end() && it->second.type != FreeVma && it->second.bFragmented) { - EmuLog(LOG_LEVEL::WARNING, "Applying identity mapping hack to allocation at address 0x%X", addr); - Unlock(); - RETURN(addr); // committed pages in the system-devkit regions can use VirtualAlloc because of fragmentation - } - } - else { - // This is the special case of the page tables region at 0xC0000000. This area doesn't have a memory region assigned to, - // and never uses VirtualAlloc, but it's still affected by the above problem since its physical pages don't come from - // our memory.bin at 0x80000000, so it needs the hack as well. - - EmuLog(LOG_LEVEL::WARNING, "Applying identity mapping hack to allocation at address 0x%X", addr); - Unlock(); - RETURN(addr); - } - */ - } - else { - goto InvalidAddress; - } + if (IsValidVirtualAddress(addr)) { + RETURN(addr); } + else { + RETURN(NULL); + } + + Lock(); PointerPte = GetPdeAddress(addr); if (PointerPte->Hardware.Valid == 0) { // invalid pde -> addr is invalid @@ -2500,7 +2259,6 @@ VMAIter VMManager::UnmapVMA(VMAIter vma_handle, MemoryRegionType Type) VirtualMemoryArea& vma = vma_handle->second; vma.type = FreeVma; vma.permissions = XBOX_PAGE_NOACCESS; - vma.bFragmented = false; return MergeAdjacentVMA(vma_handle, Type); } @@ -2616,7 +2374,7 @@ void VMManager::UpdateMemoryPermissions(VAddr addr, size_t Size, DWORD Perms) // was specified when calling the CreateFileMapping function. Considering that Cxbx doesn't emulate the caches, // it's probably safe to ignore these flags - DWORD WindowsPerms = ConvertXboxToWinProtection(PatchXboxPermissions(Perms)); + DWORD WindowsPerms = ConvertXboxToWinPermissions(PatchXboxPermissions(Perms)); DWORD dummy; if (!VirtualProtect((void*)addr, Size, WindowsPerms & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE), &dummy)) @@ -2649,6 +2407,62 @@ VMAIter VMManager::CheckConflictingVMA(VAddr addr, size_t Size, MemoryRegionType return m_MemoryRegionArray[Type].RegionMap.end(); // no conflict } +void VMManager::ConstructVMA(VAddr Start, size_t Size, MemoryRegionType Type, VMAType VmaType, DWORD Perms) +{ + VMAIter it_begin = m_MemoryRegionArray[Type].RegionMap.begin(); + VMAIter it_end = m_MemoryRegionArray[Type].RegionMap.end(); + VMAIter it; + + VMAIter vma_handle = CarveVMA(Start, Size, Type); + VirtualMemoryArea& vma = vma_handle->second; + vma.type = VmaType; + vma.permissions = Perms; + + // Depending on the splitting done by CarveVMA and the type of the adiacent vma's, there is no guarantee that the next + // or previous vma's are free. We are just going to iterate forward and backward until we find one. + + it = std::next(vma_handle); + + while (it != it_end) + { + if (it->second.type == FreeVma) + { + m_MemoryRegionArray[Type].LastFree = it; + return; + } + ++it; + } + + if (vma_handle == it_begin) + { + // Already at the beginning of the map, bail out immediately + + EmuLog(LOG_LEVEL::WARNING, "Can't find any more free space in the memory region %d! Virtual memory exhausted?", Type); + m_MemoryRegionArray[Type].LastFree = m_MemoryRegionArray[Type].RegionMap.end(); + return; + } + + it = std::prev(vma_handle); + + while (true) + { + if (it->second.type == FreeVma) + { + m_MemoryRegionArray[Type].LastFree = it; + return; + } + + if (it == it_begin) { break; } + --it; + } + + EmuLog(LOG_LEVEL::WARNING, "Can't find any more free space in the memory region %d! Virtual memory exhausted?", Type); + + m_MemoryRegionArray[Type].LastFree = m_MemoryRegionArray[Type].RegionMap.end(); + + return; +} + void VMManager::DestructVMA(VAddr addr, MemoryRegionType Type, size_t Size) { BOOL ret; @@ -2658,25 +2472,27 @@ void VMManager::DestructVMA(VAddr addr, MemoryRegionType Type, size_t Size) if ((addr >= XBE_MAX_VA) && (Type != ContiguousRegion)) { - if (it->second.bFragmented) - { - // NOTE: unlike NtFreeVirtualMemory, VirtualFree cannot release a committed region only partially, the size must always be 0 - // with MEM_RELEASE. This is a problem because some games can call us from XbFreeVirtualMemory with XBOX_MEM_RELEASE and a - // size != 0, so, in this case, we can only decommit the region to avoid memory leaks. This essentially means that we will - // leave behind reserved areas, which will decrease the total amount of host virtual space available... + if (Type == SystemRegion || Type == DevkitRegion) + { + // This memory was reserved by the loader, do not release it, only decommit - if (addr == it->first && Size == it->second.size) { ret = VirtualFree((void*)addr, 0, MEM_RELEASE); } - else { ret = VirtualFree((void*)addr, Size, MEM_DECOMMIT); } - } - else - { - ret = UnmapViewOfFile((void*)(ROUND_DOWN(it->first, m_AllocationGranularity))); - } + ret = VirtualFree((void*)addr, Size, MEM_DECOMMIT); + } + else + { + // NOTE: unlike NtFreeVirtualMemory, VirtualFree cannot release a committed region only partially, the size must always be 0 + // with MEM_RELEASE. This is a problem because some games can call us from XbFreeVirtualMemory with XBOX_MEM_RELEASE and a + // size != 0, so, in this case, we can only decommit the region to avoid memory leaks. This essentially means that we will + // leave behind reserved areas, which will decrease the total amount of host virtual space available... - if (!ret) - { - EmuLog(LOG_LEVEL::DEBUG, "Deallocation routine failed with error %d", GetLastError()); - } + if (addr == it->first && Size == it->second.size) { ret = VirtualFree((void*)addr, 0, MEM_RELEASE); } + else { ret = VirtualFree((void*)addr, Size, MEM_DECOMMIT); } + } + + if (!ret) + { + DBG_PRINTF("Deallocation routine failed with error %d\n", GetLastError()); + } } VMAIter CarvedVmaIt = CarveVMARange(addr, Size, Type); diff --git a/src/core/kernel/memory-manager/VMManager.h b/src/core/kernel/memory-manager/VMManager.h index ca03ae0ba..402a1a6d7 100644 --- a/src/core/kernel/memory-manager/VMManager.h +++ b/src/core/kernel/memory-manager/VMManager.h @@ -19,7 +19,7 @@ // * If not, write to the Free Software Foundation, Inc., // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * -// * (c) 2017-2018 ergo720 +// * (c) 2017-2018-2019 ergo720 // * // * All rights reserved // * @@ -28,6 +28,9 @@ #ifndef VMMANAGER_H #define VMMANAGER_H +#define SYSTEM_XBOX (1 << 1) +#define SYSTEM_DEVKIT (1 << 2) +#define SYSTEM_CHIHIRO (1 << 3) #include "PhysicalMemory.h" @@ -55,8 +58,6 @@ struct VirtualMemoryArea VMAType type = FreeVma; // initial vma permissions of the allocation, only used by XbVirtualMemoryStatistics DWORD permissions = XBOX_PAGE_NOACCESS; - // this allocation was served by VirtualAlloc - bool bFragmented = false; // tests if this area can be merged to the right with 'next' bool CanBeMergedWith(const VirtualMemoryArea& next) const; }; @@ -84,6 +85,15 @@ typedef enum _MemoryRegionType }MemoryRegionType; +/* struct used to save the persistent memory between reboots */ +typedef struct _PersistedMemory +{ + size_t NumOfPtes; + VAddr LaunchFrameAddresses[2]; + uint32_t Data[]; +}PersistedMemory; + + /* VMManager class */ class VMManager : public PhysicalMemory { @@ -97,19 +107,9 @@ class VMManager : public PhysicalMemory // process is killed with TerminateProcess and so it doesn't have a chance to perform a cleanup... //DestroyMemoryRegions(); DeleteCriticalSection(&m_CriticalSection); - FlushViewOfFile((void*)CONTIGUOUS_MEMORY_BASE, CHIHIRO_MEMORY_SIZE); - FlushViewOfFile((void*)PAGE_TABLES_BASE, PAGE_TABLES_SIZE); - FlushFileBuffers(m_hContiguousFile); - FlushFileBuffers(m_hPTFile); - UnmapViewOfFile((void *)CONTIGUOUS_MEMORY_BASE); - UnmapViewOfFile((void *)PAGE_TABLES_BASE); - UnmapViewOfFile((void*)XBOX_WRITE_COMBINED_BASE); - VirtualFree((void*)PAGE_TABLES_BASE, 0, MEM_RELEASE); - CloseHandle(m_hContiguousFile); - CloseHandle(m_hPTFile); } // initializes the memory manager to the default configuration - void Initialize(HANDLE memory_view, HANDLE pagetables_view, int BootFlags); + void Initialize(int SystemType, int BootFlags, uint32_t blocks_reserved[384]); // retrieves memory statistics void MemoryStatistics(xboxkrnl::PMM_STATISTICS memory_statistics); // allocates memory in the user region @@ -119,13 +119,13 @@ class VMManager : public PhysicalMemory // allocates memory in the system region VAddr AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Size, bool bAddGuardPage); // allocates memory in the contiguous region - VAddr AllocateContiguous(size_t Size, PAddr LowestAddress, PAddr HighestAddress, ULONG Alignment, DWORD Perms); + VAddr AllocateContiguousMemory(size_t Size, PAddr LowestAddress, PAddr HighestAddress, ULONG Alignment, DWORD Perms); // maps device memory in the system region VAddr MapDeviceMemory(PAddr Paddr, size_t Size, DWORD Perms); // deallocates memory in the system region PFN_COUNT DeallocateSystemMemory(PageType BusyType, VAddr addr, size_t Size); // deallocates memory in the contiguous region - void DeallocateContiguous(VAddr addr); + void DeallocateContiguousMemory(VAddr addr); // unmaps device memory in the system region void UnmapDeviceMemory(VAddr addr, size_t Size); // deallocates memory in the user region @@ -158,43 +158,36 @@ class VMManager : public PhysicalMemory xboxkrnl::NTSTATUS XbVirtualProtect(VAddr* addr, size_t* Size, DWORD* Protect); // xbox implementation of NtQueryVirtualMemory xboxkrnl::NTSTATUS XbVirtualMemoryStatistics(VAddr addr, xboxkrnl::PMEMORY_BASIC_INFORMATION memory_statistics); + // saves all persisted memory just before a quick reboot + void SavePersistentMemory(); private: - // typedef of pointer to a member function mapping a memory block - typedef VAddr (VMManager::*MappingFn) (VAddr, size_t, size_t, DWORD, PFN); // an array of structs used to track the free/allocated vma's in the various memory regions MemoryRegion m_MemoryRegionArray[COUNTRegion]; - // handle of the contiguous file mapping - HANDLE m_hContiguousFile = NULL; - // handle of the PT file mapping - HANDLE m_hPTFile = NULL; // critical section lock to synchronize accesses CRITICAL_SECTION m_CriticalSection; - // the allocation granularity of the host. Needed by MapViewOfFileEx and VirtualAlloc + // the allocation granularity of the host DWORD m_AllocationGranularity = 0; // number of bytes reserved with XBOX_MEM_RESERVE by XbAllocateVirtualMemory size_t m_VirtualMemoryBytesReserved = 0; + // number of persisted ptes between quick reboots + size_t m_NumPersistentPtes = 0; - - // set up the pfn database - void InitializePfnDatabase(); - // set up the pfn database after a quick reboot - void ReinitializePfnDatabase(); + // same as AllocateContiguousMemory, but it allows to allocate beyond m_MaxContiguousPfn + VAddr AllocateContiguousMemoryInternal(PFN_COUNT NumberOfPages, PFN LowestPfn, PFN HighestPfn, PFN PfnAlignment, DWORD Perms); + // set up the system allocations + void InitializeSystemAllocations(); // initializes a memory region struct void ConstructMemoryRegion(VAddr Start, size_t Size, MemoryRegionType Type); // clears all memory region structs void DestroyMemoryRegions(); // map a memory block with the supplied allocation routine - VAddr MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, PFN_COUNT PteNumber, PFN pfn, VAddr HighestAddress = 0); - // helper function which maps a block with VirtualAlloc - VAddr MapBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused, PFN Unused2); - // helper function which reserves a block of virtual memory with VirtualAlloc - VAddr ReserveBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused, PFN Unused2); - // helper function which maps a block with MapViewOfFileEx - VAddr MapBlockWithMapViewOfFileEx(VAddr StartingAddr, size_t ViewSize, size_t VmaEnd, DWORD OffsetLow, PFN pfn); + VAddr MapMemoryBlock(MemoryRegionType Type, PFN_COUNT PteNumber, DWORD Permissions, bool b64Blocks, VAddr HighestAddress = 0); + // helper function which allocates user memory with VirtualAlloc + VAddr MapHostMemory(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Permissions); // constructs a vma - void ConstructVMA(VAddr Start, size_t Size, MemoryRegionType Type, VMAType VmaType, bool bFragFlag, DWORD Perms = XBOX_PAGE_NOACCESS); + void ConstructVMA(VAddr Start, size_t Size, MemoryRegionType Type, VMAType VmaType, DWORD Perms = XBOX_PAGE_NOACCESS); // destructs a vma void DestructVMA(VAddr addr, MemoryRegionType Type, size_t Size); // removes a vma block from the mapped memory @@ -215,8 +208,6 @@ class VMManager : public PhysicalMemory void UpdateMemoryPermissions(VAddr addr, size_t Size, DWORD Perms); // restores persistent memory void RestorePersistentMemory(); - // restores a persistent allocation - void RestorePersistentAllocation(VAddr addr, PFN StartingPfn, PFN EndingPfn, PageType Type); // acquires the critical section void Lock(); // releases the critical section diff --git a/src/emulator/cxbxr-emu.cpp b/src/emulator/cxbxr-emu.cpp index b4f0ef442..cc5bfb19c 100644 --- a/src/emulator/cxbxr-emu.cpp +++ b/src/emulator/cxbxr-emu.cpp @@ -120,7 +120,7 @@ CommandLineToArgvA( return argv; } -DWORD WINAPI Emulate(int system) +DWORD WINAPI Emulate(int system, uint32_t blocks_reserved[384]) { FUNC_EXPORTS @@ -170,7 +170,7 @@ DWORD WINAPI Emulate(int system) return EXIT_FAILURE; } - CxbxKrnlMain(argc, argv); + CxbxKrnlMain(argc, argv, blocks_reserved); LocalFree(argv); diff --git a/src/gui/WinMain.cpp b/src/gui/WinMain.cpp index 42cfcebfd..736599631 100644 --- a/src/gui/WinMain.cpp +++ b/src/gui/WinMain.cpp @@ -52,11 +52,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine return EXIT_FAILURE; } +#ifndef CXBX_LOADER /*! verify Cxbx.exe is loaded to base address 0x00010000 */ if (!VerifyBaseAddr()) { CxbxShowError("Cxbx.exe is not loaded to base address 0x00010000 (which is a requirement for Xbox emulation)"); return EXIT_FAILURE; } +#endif DWORD guiProcessID = 0; // TODO: Convert ALL __argc & __argv to use main(int argc, char** argv) method. @@ -74,9 +76,22 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine } if (bHasLoadArgument) { +#ifndef CXBX_LOADER CxbxKrnlMain(__argc, __argv); EmuShared::Cleanup(); return EXIT_SUCCESS; +#else + std::string szProcArgsBuffer; + for (int i = 0; i < __argc; i++) { + szProcArgsBuffer.append(__argv[i]); + } + + if (!CxbxExec(szProcArgsBuffer, nullptr, false)) { + CxbxShowError("Could not launch Cxbx-R loader!"); + EmuShared::Cleanup(); + return EXIT_FAILURE; + } +#endif } // If 2nd GUI executable is launched, load settings file for GUI for editable support. diff --git a/src/gui/WndMain.cpp b/src/gui/WndMain.cpp index dd7dbc159..5c79f3d10 100644 --- a/src/gui/WndMain.cpp +++ b/src/gui/WndMain.cpp @@ -65,6 +65,8 @@ static int gameLogoWidth, gameLogoHeight; static int splashLogoWidth, splashLogoHeight; +static HANDLE hPersistedMemory = NULL; +static LPVOID PersistedMemoryAddr = nullptr; bool g_SaveOnExit = true; @@ -345,10 +347,27 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP pCMD->dwChildProcID = lParam; // lParam is process ID. std::thread(CrashMonitorWrapper, pCMD).detach(); + g_EmuShared->SetIsEmulating(true); // NOTE: Putting in here raise to low or medium risk due to debugger will launch itself. (Current workaround) g_EmuShared->SetIsReady(true); - break; } + break; + + case ID_GUI_VM_PERSIST_MEM: { + if (lParam) { + hPersistedMemory = OpenFileMapping(FILE_MAP_READ, FALSE, "PersistentMemory"); + assert(hPersistedMemory != NULL); + PersistedMemoryAddr = MapViewOfFile(hPersistedMemory, FILE_MAP_READ, 0, 0, 0); + assert(PersistedMemoryAddr != nullptr); + } + else { + UnmapViewOfFile(PersistedMemoryAddr); + CloseHandle(hPersistedMemory); + PersistedMemoryAddr = nullptr; + hPersistedMemory = NULL; + } + } + break; } } break; @@ -2224,6 +2243,8 @@ void WndMain::StartEmulation(HWND hwndParent, DebuggerState LocalDebuggerState / char szExeFileName[MAX_PATH]; GetModuleFileName(GetModuleHandle(nullptr), szExeFileName, MAX_PATH); + PathRemoveFileSpec(szExeFileName); + PathAppend(szExeFileName, "\\cxbxr-ldr.exe"); bool AttachLocalDebugger = (LocalDebuggerState == debuggerOn); g_EmuShared->SetDebuggingFlag(&AttachLocalDebugger); @@ -2320,6 +2341,12 @@ void WndMain::CrashMonitor(DWORD dwChildProcID) g_EmuShared->GetBootFlags(&iBootFlags); if (!iBootFlags) { + if (hPersistedMemory != NULL) { + UnmapViewOfFile(PersistedMemoryAddr); + CloseHandle(hPersistedMemory); + PersistedMemoryAddr = nullptr; + hPersistedMemory = NULL; + } if (dwExitCode == EXIT_SUCCESS) {// StopEmulation return; } diff --git a/src/gui/resource/ResCxbx.h b/src/gui/resource/ResCxbx.h index 9ee9ebc0c..3de4ffb47 100644 --- a/src/gui/resource/ResCxbx.h +++ b/src/gui/resource/ResCxbx.h @@ -194,7 +194,8 @@ #define ID_GUI_STATUS_LLE_FLAGS 1097 #define ID_GUI_STATUS_XBOX_LED_COLOUR 1098 #define ID_GUI_STATUS_LOG_ENABLED 1099 -#define IDC_AC_MUTE_ON_UNFOCUS_DISABLE 1100 +#define ID_GUI_VM_PERSIST_MEM 1100 +#define IDC_AC_MUTE_ON_UNFOCUS_DISABLE 1101 #define IDC_XBOX_PORT_0 1158 #define IDC_XBOX_PORT_1 1166 #define IDC_XBOX_PORT_2 1174 diff --git a/src/loader/cxbxr-ldr.cpp b/src/loader/cxbxr-ldr.cpp index 9c9ba5906..de15c20e1 100644 --- a/src/loader/cxbxr-ldr.cpp +++ b/src/loader/cxbxr-ldr.cpp @@ -20,6 +20,7 @@ // * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. // * // * (c) 2017-2019 Patrick van Logchem +// * (c) 2019 ego720 // * // * All rights reserved // * @@ -79,24 +80,27 @@ unsigned char virtual_memory_placeholder[VM_PLACEHOLDER_SIZE] = { 0 }; // = { OP // /ENTRY:"rawMain" -void OutputMessage(const char *msg) +void OutputMessage(const char* msg) { - OutputDebugStringA(msg); // Send message to debugger output too + if (msg != nullptr) { + OutputDebugStringA(msg); // Send message to debugger output too - HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD nNumberOfCharsToWrite = 0; - while (msg[nNumberOfCharsToWrite]) nNumberOfCharsToWrite++; // poor-man's strlen() + HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD nNumberOfCharsToWrite = 0; + while (msg[nNumberOfCharsToWrite]) nNumberOfCharsToWrite++; // poor-man's strlen() - // Detect output-redirection to a file - DWORD ConsoleMode; - if (!GetConsoleMode(hConsoleOutput, &ConsoleMode)) { - // Note : assume the output file accepts ANSI encoded characters - DWORD NumberOfBytesWritten; - WriteFile(hConsoleOutput, (const void *)msg, nNumberOfCharsToWrite, &NumberOfBytesWritten, /*lpOverlapped=*/NULL); - } else { - // Write message to console output - DWORD NumberOfCharsWritten; - WriteConsoleA(hConsoleOutput, (const void *)msg, nNumberOfCharsToWrite, &NumberOfCharsWritten, /*lpReserved=*/NULL); + // Detect output-redirection to a file + DWORD ConsoleMode; + if (!GetConsoleMode(hConsoleOutput, &ConsoleMode)) { + // Note : assume the output file accepts ANSI encoded characters + DWORD NumberOfBytesWritten; + WriteFile(hConsoleOutput, (const void*)msg, nNumberOfCharsToWrite, &NumberOfBytesWritten, /*lpOverlapped=*/NULL); + } + else { + // Write message to console output + DWORD NumberOfCharsWritten; + WriteConsoleA(hConsoleOutput, (const void*)msg, nNumberOfCharsToWrite, &NumberOfCharsWritten, /*lpReserved=*/NULL); + } } } @@ -146,7 +150,10 @@ DWORD CALLBACK rawMain() } } - if (!ReserveAddressRanges(system)) { + // Marking this as static to avoid an implicit call to memset, which is not availble in the loader + static uint32_t SystemDevBlocksReserved[384]; + + if (!ReserveAddressRanges(system, SystemDevBlocksReserved)) { // If we get here, emulation lacks important address ranges; Don't launch OutputMessage("Required address range couldn't be reserved!\n"); return ERROR_NOT_ENOUGH_MEMORY; @@ -166,7 +173,7 @@ DWORD CALLBACK rawMain() } // Find the main emulation function in our DLL - typedef void (WINAPI *Emulate_t)(int); + typedef void (WINAPI *Emulate_t)(int, uint32_t[384]); Emulate_t pfnEmulate = (Emulate_t)GetProcAddress(hEmulationDLL, "Emulate"); if (!pfnEmulate) { OutputMessage("Entrypoint not found!\n"); @@ -175,7 +182,7 @@ DWORD CALLBACK rawMain() // Call the main emulation function in our DLL, passing in the results // of the address range reservations - pfnEmulate(system); // TODO : Pass along all data that we've gathered up until here (or rebuild it over there) + pfnEmulate(system, SystemDevBlocksReserved); // TODO : Pass along all data that we've gathered up until here (or rebuild it over there) // Once emulation actually started, execution may never return here // because all code and data that have been used up until now are