diff --git a/src/CxbxKrnl/EmuKrnlMm.cpp b/src/CxbxKrnl/EmuKrnlMm.cpp index 8dec2c1fc..5df731ce7 100644 --- a/src/CxbxKrnl/EmuKrnlMm.cpp +++ b/src/CxbxKrnl/EmuKrnlMm.cpp @@ -150,29 +150,7 @@ XBSYSAPI EXPORTNUM(168) xboxkrnl::PVOID NTAPI xboxkrnl::MmClaimGpuInstanceMemory LOG_FUNC_ARG_OUT(NumberOfPaddingBytes) LOG_FUNC_END; - unsigned int highest_physical_page = XBOX_HIGHEST_PHYSICAL_PAGE; - unsigned int instance_physical_page = XBOX_INSTANCE_PHYSICAL_PAGE; - if (g_bIsChihiro || g_bIsDebug) - { - *NumberOfPaddingBytes = 0; - highest_physical_page = CHIHIRO_HIGHEST_PHYSICAL_PAGE; - } - else - *NumberOfPaddingBytes = CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(X64M_PHYSICAL_PAGE) - - CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(instance_physical_page + NV2A_INSTANCE_PAGE_COUNT); - - DbgPrintf("KNRL: MmClaimGpuInstanceMemory : *NumberOfPaddingBytes = 0x%.8X\n", *NumberOfPaddingBytes); - -#ifdef _DEBUG_TRACE - if (NumberOfBytes != MAXULONG_PTR) - { - if (NumberOfBytes != 20480) - LOG_IGNORED(); - } -#endif - - PVOID Result = (PUCHAR)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(highest_physical_page + 1) - - *NumberOfPaddingBytes; + PVOID Result = (PVOID)g_VMManager.ClaimGpuMemory(NumberOfBytes, (size_t*)NumberOfPaddingBytes); RETURN(Result); } diff --git a/src/CxbxKrnl/PhysicalMemory.cpp b/src/CxbxKrnl/PhysicalMemory.cpp index f8ba6d049..e5c156750 100644 --- a/src/CxbxKrnl/PhysicalMemory.cpp +++ b/src/CxbxKrnl/PhysicalMemory.cpp @@ -588,10 +588,13 @@ bool PhysicalMemory::AllocatePT(PFN_COUNT PteNumber, VAddr addr) return false; } - if (addr < HIGHEST_USER_ADDRESS) { BusyType = PageType::VirtualPageTable; } + if (addr <= HIGHEST_USER_ADDRESS) { BusyType = PageType::VirtualPageTable; } PdeMappedSizeIncrement = 0; - // Now actually commit the PT's + // Now actually commit the PT's. Note that we won't construct the vma's for the PTs since they are outside of all + // memory regions and also there is no need to track them since they will not be deallocated anyway even when all the + // pte's in the PT become invalid (the kernel does this too, BTY) + for (int i = 0; i < PdeNumber; ++i) { pPde = GetPdeAddress(addr += PdeMappedSizeIncrement); @@ -625,8 +628,6 @@ PFN PhysicalMemory::RemoveAndZeroAnyFreePage(PageType BusyType, PMMPTE pPte) // Construct the pfn for the page WritePfn(pfn, pfn, pPte, BusyType); - // NOTE: construct vma here or in AllocatePT? (depends on future NtAllocateVirtualMemory implementation) - return pfn; } diff --git a/src/CxbxKrnl/PhysicalMemory.h b/src/CxbxKrnl/PhysicalMemory.h index e3131beda..10e566b43 100644 --- a/src/CxbxKrnl/PhysicalMemory.h +++ b/src/CxbxKrnl/PhysicalMemory.h @@ -104,7 +104,6 @@ typedef struct _XBOX_PFN { union { ULONG Default; - MMPTE Pte; struct { ULONG LockCount : 16; // Set to prevent page relocation. Used by MmLockUnlockPhysicalPage and others ULONG Busy : 1; // If set, PFN is in use @@ -242,14 +241,16 @@ class PhysicalMemory xboxkrnl::LIST_ENTRY FreeList = { &FreeList , &FreeList }; // highest pfn available for contiguous allocations PAddr m_MaxContiguousPfn = XBOX_CONTIGUOUS_MEMORY_LIMIT; - // the size of memory occupied by the PFN/NV2A instance memory - size_t m_UpperPMemorySize = 32 * PAGE_SIZE; // amount of physical pages free PFN_COUNT m_PhysicalPagesAvailable = X64M_PHYSICAL_PAGE; // array containing the number of pages in use per type PFN_COUNT m_PagesByUsage[PageType::COUNT] = { 0 }; // highest page on the system - PFN_COUNT m_HighestPage = XBOX_HIGHEST_PHYSICAL_PAGE; + PFN m_HighestPage = XBOX_HIGHEST_PHYSICAL_PAGE; + // first page of the nv2a instance memory + PFN m_NV2AInstancePage = XBOX_INSTANCE_PHYSICAL_PAGE; + // number of allocated bytes for the nv2a instance memory + size_t m_NV2AInstanceMemoryBytes = NV2A_INSTANCE_PAGE_COUNT << PAGE_SHIFT; // protected constructor so PhysicalMemory can only be inherited from diff --git a/src/CxbxKrnl/VMManager.cpp b/src/CxbxKrnl/VMManager.cpp index 4f6535de8..733c23c2e 100644 --- a/src/CxbxKrnl/VMManager.cpp +++ b/src/CxbxKrnl/VMManager.cpp @@ -80,7 +80,14 @@ void VMManager::Initialize(HANDLE memory_view, HANDLE PT_view) if (g_bIsChihiro) { m_MaxContiguousPfn = CHIHIRO_CONTIGUOUS_MEMORY_LIMIT; - m_UpperPMemorySize = 48 * PAGE_SIZE; + g_SystemMaxMemory = CHIHIRO_MEMORY_SIZE; + m_PhysicalPagesAvailable = g_SystemMaxMemory >> PAGE_SHIFT; + m_HighestPage = CHIHIRO_HIGHEST_PHYSICAL_PAGE; + m_NV2AInstancePage = CHIHIRO_INSTANCE_PHYSICAL_PAGE; + m_MemoryRegionArray[MemoryRegionType::Contiguous].RegionMap[0].size = CONTIGUOUS_MEMORY_CHIHIRO_SIZE; + } + else if (g_bIsDebug) + { g_SystemMaxMemory = CHIHIRO_MEMORY_SIZE; m_PhysicalPagesAvailable = g_SystemMaxMemory >> PAGE_SHIFT; m_HighestPage = CHIHIRO_HIGHEST_PHYSICAL_PAGE; @@ -157,6 +164,10 @@ void VMManager::InitializePfnDatabase() PFN pfn; PFN pfn_end; PFN result; + PMMPTE PointerPte; + PMMPTE EndingPte; + MMPTE TempPte; + VAddr addr; // Default initialize all the entries of the PFN database @@ -174,89 +185,98 @@ void VMManager::InitializePfnDatabase() FillMemoryUlong((void*)XBOX_PFN_ADDRESS, X64KB * 2, TempPF.Default); // Debug: 128 KiB } - // Construct the pfn of the page directory - pfn = CONVERT_CONTIGUOUS_PHYSICAL_TO_PFN(PAGE_DIRECTORY_PHYSICAL_ADDRESS); - TempPF.Pte.Default = ValidKernelPteBits | PTE_GUARD_END_MASK | PTE_PERSIST_MASK; - RemoveFree(1, &result, 0, pfn, pfn); - WritePfn(pfn, pfn, &TempPF.Pte, PageType::Contiguous); - ConstructVMA(CONTIGUOUS_MEMORY_BASE + (pfn << PAGE_SHIFT), PAGE_SIZE, MemoryRegionType::Contiguous, - VMAType::Allocated, false); + // Construct the pfn of the page directory + AllocateContiguous(PAGE_SIZE, PAGE_DIRECTORY_PHYSICAL_ADDRESS, PAGE_DIRECTORY_PHYSICAL_ADDRESS + PAGE_SIZE, 0, XBOX_PAGE_READWRITE); + // Make this persistent // Construct the pfn's of the kernel pages - pfn = CONVERT_CONTIGUOUS_PHYSICAL_TO_PFN(XBOX_KERNEL_BASE); - pfn_end = CONVERT_CONTIGUOUS_PHYSICAL_TO_PFN(XBOX_KERNEL_BASE + KERNEL_SIZE - 1); - TempPF.Pte.Default = ValidKernelPteBits | PTE_PERSIST_MASK; + AllocateContiguous(KERNEL_SIZE, XBE_IMAGE_BASE, XBE_IMAGE_BASE + KERNEL_SIZE, 0, XBOX_PAGE_EXECUTE_READWRITE); + // Make this persistent - RemoveFree(pfn_end - pfn + 1, &result, 0, pfn, pfn_end); - WritePfn(pfn, pfn_end, &TempPF.Pte, PageType::Contiguous); - ConstructVMA(CONTIGUOUS_MEMORY_BASE + (pfn << PAGE_SHIFT), (pfn_end - pfn + 1) << PAGE_SHIFT, - MemoryRegionType::Contiguous, VMAType::Allocated, false); + // 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 // Construct the pfn's of the pages holding the pfn database if (g_bIsRetail) { pfn = XBOX_PFN_DATABASE_PHYSICAL_PAGE; pfn_end = XBOX_PFN_DATABASE_PHYSICAL_PAGE + 16 - 1; + addr = (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn); + PointerPte = GetPteAddress(addr); + EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); } else if (g_bIsDebug) { pfn = XBOX_PFN_DATABASE_PHYSICAL_PAGE; pfn_end = XBOX_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)); } else { pfn = CHIHIRO_PFN_DATABASE_PHYSICAL_PAGE; 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)); } - TempPF.Pte.Default = ValidKernelPteBits | PTE_PERSIST_MASK; + TempPte.Default = ValidKernelPteBits | PTE_PERSIST_MASK; RemoveFree(pfn_end - pfn + 1, &result, 0, pfn, pfn_end); - WritePfn(pfn, pfn_end, &TempPF.Pte, PageType::Contiguous); - ConstructVMA(CONTIGUOUS_MEMORY_BASE + (pfn << PAGE_SHIFT), (pfn_end - pfn + 1) << PAGE_SHIFT, - MemoryRegionType::Contiguous, VMAType::Allocated, false); + WritePfn(pfn, pfn_end, &TempPte, PageType::Contiguous); + WritePte(PointerPte, EndingPte, TempPte, pfn); + AllocatePT(EndingPte - PointerPte + 1, addr); + ConstructVMA(addr, (pfn_end - pfn + 1) << PAGE_SHIFT, MemoryRegionType::Contiguous, VMAType::Allocated, false); // Construct the pfn's of the pages holding the nv2a instance memory if (g_bIsRetail || g_bIsDebug) { pfn = XBOX_INSTANCE_PHYSICAL_PAGE; pfn_end = XBOX_INSTANCE_PHYSICAL_PAGE + NV2A_INSTANCE_PAGE_COUNT - 1; + addr = (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn); + PointerPte = GetPteAddress(addr); + EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); } else { pfn = CHIHIRO_INSTANCE_PHYSICAL_PAGE; pfn_end = CHIHIRO_INSTANCE_PHYSICAL_PAGE + NV2A_INSTANCE_PAGE_COUNT - 1; + addr = (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn); + PointerPte = GetPteAddress(addr); + EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); } - TempPF.Pte.Default = ValidKernelPteBits; - DISABLE_CACHING(TempPF.Pte); + TempPte.Default = ValidKernelPteBits; + DISABLE_CACHING(TempPte); RemoveFree(pfn_end - pfn + 1, &result, 0, pfn, pfn_end); - WritePfn(pfn, pfn_end, &TempPF.Pte, PageType::Contiguous); - ConstructVMA(CONTIGUOUS_MEMORY_BASE + (pfn << PAGE_SHIFT), NV2A_INSTANCE_PAGE_COUNT << PAGE_SHIFT, - MemoryRegionType::Contiguous, VMAType::Allocated, false); + WritePfn(pfn, pfn_end, &TempPte, PageType::Contiguous); + WritePte(PointerPte, EndingPte, TempPte, pfn); + AllocatePT(EndingPte - PointerPte + 1, addr); + ConstructVMA(addr, NV2A_INSTANCE_PAGE_COUNT << PAGE_SHIFT, MemoryRegionType::Contiguous, VMAType::Allocated, false); if (g_bIsDebug) { // Debug kits have two nv2a instance memory, another at the top of the 128 MiB - pfn = XBOX_INSTANCE_PHYSICAL_PAGE + DEBUGKIT_FIRST_UPPER_HALF_PAGE; - pfn_end = XBOX_INSTANCE_PHYSICAL_PAGE + DEBUGKIT_FIRST_UPPER_HALF_PAGE + NV2A_INSTANCE_PAGE_COUNT - 1; - TempPF.Pte.Default = ValidKernelPteBits; - DISABLE_CACHING(TempPF.Pte); + pfn += DEBUGKIT_FIRST_UPPER_HALF_PAGE; + pfn_end += DEBUGKIT_FIRST_UPPER_HALF_PAGE; + addr = (VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn); + PointerPte = GetPteAddress(addr); + EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn_end)); RemoveFree(pfn_end - pfn + 1, &result, 0, pfn, pfn_end); - WritePfn(pfn, pfn_end, &TempPF.Pte, PageType::Contiguous); - ConstructVMA(CONTIGUOUS_MEMORY_BASE + (pfn << PAGE_SHIFT), NV2A_INSTANCE_PAGE_COUNT << PAGE_SHIFT, - MemoryRegionType::Contiguous, VMAType::Allocated, false); + WritePfn(pfn, pfn_end, &TempPte, PageType::Contiguous); + WritePte(PointerPte, EndingPte, TempPte, pfn); + AllocatePT(EndingPte - PointerPte + 1, addr); + ConstructVMA(addr, NV2A_INSTANCE_PAGE_COUNT << PAGE_SHIFT, MemoryRegionType::Contiguous, VMAType::Allocated, false); } // Construct the pfn of the page used by D3D - pfn = D3D_PHYSICAL_PAGE; - TempPF.Pte.Default = ValidKernelPteBits | PTE_GUARD_END_MASK | PTE_PERSIST_MASK; - - RemoveFree(1, &result, 0, pfn, pfn); - WritePfn(pfn, pfn, &TempPF.Pte, PageType::Contiguous); - ConstructVMA(CONTIGUOUS_MEMORY_BASE, PAGE_SIZE, MemoryRegionType::Contiguous, VMAType::Allocated, false); + AllocateContiguous(PAGE_SIZE, D3D_PHYSICAL_PAGE, D3D_PHYSICAL_PAGE + PAGE_SIZE, 0, XBOX_PAGE_READWRITE); + // Make this persistent } void VMManager::ReinitializePfnDatabase() @@ -274,7 +294,7 @@ void VMManager::ConstructVMA(VAddr Start, size_t Size, MemoryRegionType Type, VM VirtualMemoryArea& vma = vma_handle->second; vma.type = VmaType; vma.permissions = Perms; - if (bFragFlag) { vma.bFragmented = true; } + vma.bFragmented = bFragFlag; // Depending on the splitting done by CarveVMA, 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. @@ -336,6 +356,72 @@ void VMManager::MemoryStatistics(xboxkrnl::PMM_STATISTICS memory_statistics) memory_statistics->ImagePagesCommitted = m_ImageMemoryInUse; } +VAddr VMManager::ClaimGpuMemory(size_t Size, size_t* BytesToSkip) +{ + LOG_FUNC_BEGIN + LOG_FUNC_ARG(Size); + LOG_FUNC_ARG(*BytesToSkip); + LOG_FUNC_END; + + // Note that, even though devkits have 128 MiB, there's no need to have a different case for those, since the instance + // memory is still located 0x10000 bytes from the top of memory just like retail consoles + + if (g_bIsChihiro) + *BytesToSkip = 0; + else + *BytesToSkip = CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(X64M_PHYSICAL_PAGE) - + CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(XBOX_INSTANCE_PHYSICAL_PAGE + NV2A_INSTANCE_PAGE_COUNT); + + if (Size != MAXULONG_PTR) + { + PFN pfn; + PFN EndingPfn; + PMMPTE PointerPte; + PMMPTE EndingPte; + MMPTE TempPte; + + // Actually deallocate the requested number of instance pages. Note that we can't just call DeallocateContiguous + // since that function will always deallocate the entire original allocation + + Lock(); + + Size = ROUND_UP_4K(Size); + + pfn = m_NV2AInstancePage + NV2A_INSTANCE_PAGE_COUNT - (ROUND_UP_4K(m_NV2AInstanceMemoryBytes) >> PAGE_SHIFT); + EndingPfn = m_NV2AInstancePage + NV2A_INSTANCE_PAGE_COUNT - (Size >> PAGE_SHIFT) - 1; + PointerPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn)); + EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(EndingPfn)); + + WritePte(PointerPte, EndingPte, TempPte, 0, true); + WritePfn(pfn, EndingPfn, PointerPte, PageType::Contiguous, true); + InsertFree(pfn, EndingPfn); + DestructVMA(GetVMAIterator((VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn), MemoryRegionType::Contiguous), MemoryRegionType::Contiguous); + + if (g_bIsDebug) + { + // Devkits have also another nv2a instance memory at the top of memory, so free also that + // 3fe0: nv2a; 3ff0: pfn; 4000 + 3fe0: nv2a; 4000 + 3fe0 + 10: free + + pfn += DEBUGKIT_FIRST_UPPER_HALF_PAGE; + EndingPfn += DEBUGKIT_FIRST_UPPER_HALF_PAGE; + PointerPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn)); + EndingPte = GetPteAddress(CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(EndingPfn)); + + WritePte(PointerPte, EndingPte, TempPte, 0, true); + WritePfn(pfn, EndingPfn, PointerPte, PageType::Contiguous, true); + InsertFree(pfn, EndingPfn); + DestructVMA(GetVMAIterator((VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(pfn), MemoryRegionType::Contiguous), MemoryRegionType::Contiguous); + } + m_NV2AInstanceMemoryBytes = Size; + + DbgPrintf("KNRL: MmClaimGpuInstanceMemory : Allocated bytes remaining = 0x%.8X\n", m_NV2AInstanceMemoryBytes); + + Unlock(); + } + + RETURN((VAddr)CONVERT_PFN_TO_CONTIGUOUS_PHYSICAL(m_HighestPage + 1) - *BytesToSkip); +} + VAddr VMManager::Allocate(size_t Size) { LOG_FUNC_ONE_ARG(Size); @@ -1645,18 +1731,21 @@ void VMManager::DestructVMA(VMAIter it, MemoryRegionType Type) { BOOL ret; - if (it->second.bFragmented) + if (Type != MemoryRegionType::Contiguous) { - ret = VirtualFree((void*)it->first, 0, MEM_RELEASE); - } - else - { - ret = UnmapViewOfFile((void*)(ROUND_DOWN(it->first, m_AllocationGranularity))); - } + if (it->second.bFragmented) + { + ret = VirtualFree((void*)it->first, 0, MEM_RELEASE); + } + else + { + ret = UnmapViewOfFile((void*)(ROUND_DOWN(it->first, m_AllocationGranularity))); + } - if (!ret) - { - DbgPrintf("Deallocation routine failed with error %d\n", GetLastError()); + if (!ret) + { + DbgPrintf("Deallocation routine failed with error %d\n", GetLastError()); + } } VMAIter CarvedVmaIt = CarveVMARange(it->first, it->second.size, Type); diff --git a/src/CxbxKrnl/VMManager.h b/src/CxbxKrnl/VMManager.h index b281cdce6..a7c272216 100644 --- a/src/CxbxKrnl/VMManager.h +++ b/src/CxbxKrnl/VMManager.h @@ -155,6 +155,8 @@ class VMManager : public PhysicalMemory DWORD QueryProtection(VAddr addr); // retrieves the size of an allocation size_t QuerySize(VAddr addr); + // MmClaimGpuInstanceMemory implementation + VAddr ClaimGpuMemory(size_t Size, size_t* BytesToSkip); // xbox implementation of NtAllocateVirtualMemory xboxkrnl::NTSTATUS XbAllocateVirtualMemory(VAddr* addr, ULONG zero_bits, size_t* size, DWORD allocation_type, DWORD protect, bool bStub); @@ -175,7 +177,7 @@ class VMManager : public PhysicalMemory CRITICAL_SECTION m_CriticalSection; // amount of image virtual memory in use size_t m_ImageMemoryInUse = 0; - // amount of non - image virtual memory in use + // amount of non-image virtual memory in use size_t m_NonImageMemoryInUse = 0; // the allocation granularity of the host. Needed by MapViewOfFileEx and VirtualAlloc DWORD m_AllocationGranularity;