diff --git a/import/OpenXDK/include/xboxkrnl/xboxkrnl.h b/import/OpenXDK/include/xboxkrnl/xboxkrnl.h index 663b3ded0..00a16b531 100644 --- a/import/OpenXDK/include/xboxkrnl/xboxkrnl.h +++ b/import/OpenXDK/include/xboxkrnl/xboxkrnl.h @@ -363,22 +363,24 @@ LIST_ENTRY, *PLIST_ENTRY; #define LIST_ENTRY_ACCESS_RECORD(address, type, field) \ ((type*)((UCHAR*)(address) - (ULONG)(&((type*)0)->field))) +#define IS_LIST_EMPTY(ListHead) ((ListHead)->Flink == (ListHead)) + #define LIST_ENTRY_INSERT_HEAD(ListHead, Entry) {\ xboxkrnl::PLIST_ENTRY Flink;\ -Flink = ListHead->Flink;\ +Flink = (ListHead)->Flink;\ (Entry)->Flink = Flink;\ -(Entry)->Blink = ListHead;\ -Flink->Blink = Entry;\ -ListHead->Flink = Entry;\ +(Entry)->Blink = (ListHead);\ +(Flink)->Blink = Entry;\ +(ListHead)->Flink = Entry;\ } #define LIST_ENTRY_INSERT_TAIL(ListHead, Entry) {\ xboxkrnl::PLIST_ENTRY Blink;\ -Blink = ListHead->Blink;\ +Blink = (ListHead)->Blink;\ (Entry)->Flink = ListHead;\ (Entry)->Blink = Blink;\ -Blink->Flink = Entry;\ -ListHead->Blink = Entry;\ +(Blink)->Flink = Entry;\ +(ListHead)->Blink = Entry;\ } #define LIST_ENTRY_REMOVE(Entry) {\ @@ -386,14 +388,18 @@ xboxkrnl::PLIST_ENTRY ExFlink;\ xboxkrnl::PLIST_ENTRY ExBlink;\ ExFlink = (Entry)->Flink;\ ExBlink = (Entry)->Blink;\ -ExFlink->Blink = ExBlink;\ -ExBlink->Flink = ExFlink;\ +(ExFlink)->Blink = ExBlink;\ +(ExBlink)->Flink = ExFlink;\ } #define LIST_ENTRY_REMOVE_AT_HEAD(ListHead) \ (ListHead)->Flink;\ LIST_ENTRY_REMOVE((ListHead)->Flink) +#define REMOVE_HEAD_LIST(ListHead) \ +(ListHead)->Flink;\ +{LIST_ENTRY_REMOVE((ListHead)->Flink)} + // ****************************************************************** // * SLIST_ENTRY // ****************************************************************** diff --git a/src/CxbxKrnl/EmuKrnlEx.cpp b/src/CxbxKrnl/EmuKrnlEx.cpp index 1a14cc6ce..f0342dc17 100644 --- a/src/CxbxKrnl/EmuKrnlEx.cpp +++ b/src/CxbxKrnl/EmuKrnlEx.cpp @@ -47,12 +47,12 @@ namespace xboxkrnl #include "Logging.h" // For LOG_FUNC() #include "EmuEEPROM.h" // For EmuFindEEPROMInfo, EEPROM, XboxFactoryGameRegion #include "EmuKrnlLogging.h" -#include "VMManager.h" +#include "PoolManager.h" // prevent name collisions namespace NtDll { -#include "EmuNtDll.h" // For NtDelayExecution(), etc. + #include "EmuNtDll.h" // For NtDelayExecution(), etc. }; #include "CxbxKrnl.h" // For CxbxKrnlCleanup @@ -212,11 +212,9 @@ XBSYSAPI EXPORTNUM(15) xboxkrnl::PVOID NTAPI xboxkrnl::ExAllocatePoolWithTag LOG_FUNC_BEGIN LOG_FUNC_ARG(NumberOfBytes) LOG_FUNC_ARG(Tag) - LOG_FUNC_END; + LOG_FUNC_END; - PVOID pRet = (xboxkrnl::PVOID)g_VMManager.AllocateZeroed(NumberOfBytes); // Clear, to prevent side-effects on random contents - - LOG_INCOMPLETE(); // TODO : Actually implement ExAllocatePoolWithTag + PVOID pRet = g_PoolManager.AllocatePool(NumberOfBytes, Tag); RETURN(pRet); } @@ -245,7 +243,7 @@ XBSYSAPI EXPORTNUM(17) xboxkrnl::VOID NTAPI xboxkrnl::ExFreePool { LOG_FUNC_ONE_ARG(P); - g_VMManager.Deallocate((VAddr)P); + g_PoolManager.DeallocatePool(P); } // ****************************************************************** @@ -371,8 +369,7 @@ XBSYSAPI EXPORTNUM(23) xboxkrnl::ULONG NTAPI xboxkrnl::ExQueryPoolBlockSize { LOG_FUNC_ONE_ARG(PoolBlock); - // Not strictly correct, but it will do for now - ULONG ret = g_VMManager.QuerySize((VAddr)PoolBlock); + ULONG ret = g_PoolManager.QueryPoolSize(PoolBlock); RETURN(ret); } diff --git a/src/CxbxKrnl/PhysicalMemory.h b/src/CxbxKrnl/PhysicalMemory.h index a75b36c85..fab8f97f0 100644 --- a/src/CxbxKrnl/PhysicalMemory.h +++ b/src/CxbxKrnl/PhysicalMemory.h @@ -199,6 +199,7 @@ typedef enum _PageType #define PAGES_SPANNED_LARGE(Va, Size) ((ULONG)((((VAddr)(Va) & (PAGE_SIZE_LARGE - 1)) + (Size) + (PAGE_SIZE_LARGE - 1)) >> PAGE_SHIFT_LARGE)) #define BYTE_OFFSET(Va) ((ULONG)((VAddr)(Va) & (PAGE_SIZE - 1))) #define BYTE_OFFSET_LARGE(Va) ((ULONG)((VAddr)(Va) & (PAGE_SIZE_LARGE - 1))) +#define PAGE_END(Va) (((ULONG_PTR)(Va) & (PAGE_SIZE - 1)) == 0) /* These macros check if the supplied address is inside a known range */ diff --git a/src/CxbxKrnl/PoolManager.cpp b/src/CxbxKrnl/PoolManager.cpp index 483d47997..173f7e5a8 100644 --- a/src/CxbxKrnl/PoolManager.cpp +++ b/src/CxbxKrnl/PoolManager.cpp @@ -35,6 +35,7 @@ // ****************************************************************** #include "PoolManager.h" +#include #define LOG_PREFIX "PMEM" @@ -76,6 +77,151 @@ void PoolManager::InitializePool() printf(LOG_PREFIX " Pool manager initialized!\n"); } +void* PoolManager::AllocatePool(size_t Size, uint32_t Tag) +{ + PVOID Block; + PPOOL_HEADER Entry; + PPOOL_LOOKASIDE_LIST LookasideList; + PPOOL_HEADER NextEntry; + PPOOL_HEADER SplitEntry; + PPOOL_DESCRIPTOR PoolDesc = &m_NonPagedPoolDescriptor; + ULONG Index; + ULONG ListNumber; + ULONG NeededSize; + xboxkrnl::PLIST_ENTRY ListHead; + ULONG NumberOfPages; + + assert(Size); + + + if (Size > POOL_BUDDY_MAX) { + Lock(); + + PoolDesc->RunningAllocs += 1; + Entry = reinterpret_cast(g_VMManager.AllocateSystemMemory(PoolType, XBOX_PAGE_READWRITE, Size, false)); + + if (Entry != nullptr) { + NumberOfPages = ROUND_UP_4K(Size) >> PAGE_SHIFT; + PoolDesc->TotalBigPages += NumberOfPages; + Unlock(); + } + else { + EmuWarning("AllocatePool returns nullptr"); + Unlock(); + } + + return Entry; + } + + ListNumber = ((Size + POOL_OVERHEAD + (POOL_SMALLEST_BLOCK - 1)) >> POOL_BLOCK_SHIFT); + NeededSize = ListNumber; + + if (NeededSize <= POOL_SMALL_LISTS) { + LookasideList = &m_ExpSmallNPagedPoolLookasideLists[NeededSize - 1]; + LookasideList->TotalAllocates += 1; + + Entry = reinterpret_cast(xboxkrnl::KRNL(InterlockedPopEntrySList(&LookasideList->ListHead))); + + if (Entry != nullptr) { + Entry -= 1; + LookasideList->AllocateHits += 1; + + Entry->PoolType = static_cast(1); + MARK_POOL_HEADER_ALLOCATED(Entry); + + Entry->PoolTag = Tag; + (reinterpret_cast((reinterpret_cast(Entry) + POOL_OVERHEAD)))[0] = 0; + + return reinterpret_cast(Entry) + POOL_OVERHEAD; + } + } + + Lock(); + + PoolDesc->RunningAllocs += 1; + ListHead = &PoolDesc->ListHeads[ListNumber]; + + do { + do { + if (IS_LIST_EMPTY(ListHead) == false) { + Block = REMOVE_HEAD_LIST(ListHead); + Entry = reinterpret_cast((static_cast(Block) - POOL_OVERHEAD)); + + assert(Entry->BlockSize >= NeededSize); + assert(Entry->PoolType == 0); + + if (Entry->BlockSize != NeededSize) { + if (Entry->PreviousSize == 0) { + SplitEntry = reinterpret_cast((reinterpret_cast(Entry) + NeededSize)); + SplitEntry->BlockSize = Entry->BlockSize - static_cast(NeededSize); + SplitEntry->PreviousSize = static_cast(NeededSize); + + NextEntry = reinterpret_cast((reinterpret_cast(SplitEntry) + SplitEntry->BlockSize)); + if (PAGE_END(NextEntry) == false) { + NextEntry->PreviousSize = SplitEntry->BlockSize; + } + } + else { + SplitEntry = Entry; + Entry->BlockSize -= static_cast(NeededSize); + Entry = reinterpret_cast(reinterpret_cast(Entry) + Entry->BlockSize); + Entry->PreviousSize = SplitEntry->BlockSize; + + NextEntry = reinterpret_cast(reinterpret_cast(Entry) + NeededSize); + if (PAGE_END(NextEntry) == false) { + NextEntry->PreviousSize = static_cast(NeededSize); + } + } + Entry->BlockSize = static_cast(NeededSize); + SplitEntry->PoolType = 0; + Index = SplitEntry->BlockSize; + + LIST_ENTRY_INSERT_TAIL(&PoolDesc->ListHeads[Index - 1], (reinterpret_cast((reinterpret_cast(SplitEntry) + + POOL_OVERHEAD)))); + } + + Entry->PoolType = static_cast(1); + + MARK_POOL_HEADER_ALLOCATED(Entry); + + Unlock(); + + Entry->PoolTag = Tag; + (reinterpret_cast((reinterpret_cast(Entry) + POOL_OVERHEAD)))[0] = 0; + + return reinterpret_cast(Entry) + POOL_OVERHEAD; + } + ListHead += 1; + + } while (ListHead != &PoolDesc->ListHeads[POOL_LIST_HEADS]); + + Entry = reinterpret_cast(g_VMManager.AllocateSystemMemory(PoolType, XBOX_PAGE_READWRITE, PAGE_SIZE, false)); + + if (Entry == nullptr) { + EmuWarning("AllocatePool returns nullptr"); + Unlock(); + + return Entry; + } + PoolDesc->TotalPages += 1; + Entry->PoolType = 0; + + if ((PAGE_SIZE / POOL_SMALLEST_BLOCK) > 255) { + Entry->BlockSize = 255; + + } + else { + Entry->BlockSize = static_cast((PAGE_SIZE / POOL_SMALLEST_BLOCK)); + } + + Entry->PreviousSize = 0; + ListHead = &PoolDesc->ListHeads[POOL_LIST_HEADS - 1]; + + LIST_ENTRY_INSERT_HEAD(ListHead, (reinterpret_cast((reinterpret_cast(Entry) + POOL_OVERHEAD)))); + + } while (true); +} + void PoolManager::Lock() { EnterCriticalSection(&m_CriticalSection); diff --git a/src/CxbxKrnl/PoolManager.h b/src/CxbxKrnl/PoolManager.h index af54e5f6d..dadf62398 100644 --- a/src/CxbxKrnl/PoolManager.h +++ b/src/CxbxKrnl/PoolManager.h @@ -53,6 +53,7 @@ typedef struct _POOL_DESCRIPTOR { xboxkrnl::LIST_ENTRY ListHeads[POOL_LIST_HEADS]; } POOL_DESCRIPTOR, *PPOOL_DESCRIPTOR; + typedef struct _POOL_LOOKASIDE_LIST { xboxkrnl::SLIST_HEADER ListHead; USHORT Depth; @@ -62,6 +63,32 @@ typedef struct _POOL_LOOKASIDE_LIST { } POOL_LOOKASIDE_LIST, *PPOOL_LOOKASIDE_LIST; +typedef struct _POOL_HEADER { + union { + struct { + UCHAR PreviousSize; + UCHAR PoolIndex; + UCHAR PoolType; + UCHAR BlockSize; + }; + ULONG Ulong1; + }; + ULONG PoolTag; +} POOL_HEADER, *PPOOL_HEADER; + + +typedef struct _POOL_BLOCK { + UCHAR Fill[1 << POOL_BLOCK_SHIFT]; +} POOL_BLOCK, *PPOOL_BLOCK; + + +#define POOL_OVERHEAD ((LONG)sizeof(POOL_HEADER)) +#define POOL_SMALLEST_BLOCK (sizeof(POOL_BLOCK)) +#define POOL_BUDDY_MAX \ + (PAGE_SIZE - (POOL_OVERHEAD + POOL_SMALLEST_BLOCK )) + + +/* PoolManager class */ class PoolManager { public: @@ -71,6 +98,13 @@ class PoolManager ~PoolManager() { DeleteCriticalSection(&m_CriticalSection); } // initializes the pool manager to the default configuration void InitializePool(); + // allocates pool memory + void* AllocatePool(size_t Size, uint32_t Tag); + // deallocates pool memory + void DeallocatePool(void* addr); + // queries the pool block size + size_t QueryPoolSize(void* addr); + private: // main (and only) pool type available on the Xbox diff --git a/src/CxbxKrnl/VMManager.cpp b/src/CxbxKrnl/VMManager.cpp index d0c0ab66d..1e223187e 100644 --- a/src/CxbxKrnl/VMManager.cpp +++ b/src/CxbxKrnl/VMManager.cpp @@ -42,7 +42,6 @@ #define LOG_PREFIX "VMEM" -#include "VMManager.h" #include "PoolManager.h" #include "Logging.h" #include "EmuShared.h"