Xbox permissions work
This commit is contained in:
parent
c1c989c077
commit
bc0e0734ae
|
@ -446,6 +446,111 @@ bool PhysicalMemory::ConvertXboxToSystemPteProtection(DWORD perms, PMMPTE pPte)
|
|||
return true;
|
||||
}
|
||||
|
||||
DWORD PhysicalMemory::PatchXboxPermissions(DWORD Perms)
|
||||
{
|
||||
// Usage notes: this routine expects the permissions to be already sanitized by ConvertXboxToSystemPteProtection 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
|
||||
// necessary because, afaik, the Xbox grants execute rights to all allocations, even if PAGE_EXECUTE was not specified
|
||||
|
||||
if ((Perms >> 4) & 0xF)
|
||||
{
|
||||
// All high nibble flags grant execute rights, nothing to do
|
||||
|
||||
return Perms;
|
||||
}
|
||||
|
||||
if (Perms & XBOX_PAGE_NOACCESS)
|
||||
{
|
||||
// XBOX_PAGE_NOACCESS disables all access, nothing to do
|
||||
|
||||
return Perms;
|
||||
}
|
||||
|
||||
switch (Perms & (XBOX_PAGE_READONLY | XBOX_PAGE_READWRITE))
|
||||
{
|
||||
case XBOX_PAGE_READONLY:
|
||||
return XBOX_PAGE_EXECUTE_READ;
|
||||
|
||||
case XBOX_PAGE_READWRITE:
|
||||
return XBOX_PAGE_EXECUTE_READWRITE;
|
||||
|
||||
default:
|
||||
{
|
||||
// If we reach here it means that both XBOX_PAGE_READONLY and XBOX_PAGE_READWRITE were specified, and so the
|
||||
// input is probably invalid
|
||||
|
||||
DbgPrintf("PatchXboxPermissions: Memory permissions bug detected\n");
|
||||
return XBOX_PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DWORD PhysicalMemory::ConvertXboxToWinProtection(DWORD Perms)
|
||||
{
|
||||
// This function assumes that the supplied permissions have been sanitized already
|
||||
|
||||
DWORD Mask = 0;
|
||||
|
||||
if (Perms & XBOX_PAGE_NOACCESS)
|
||||
{
|
||||
// PAGE_NOACCESS cannot be specified with anything else
|
||||
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
|
||||
DWORD LowNibble = Perms & 0xF;
|
||||
DWORD HighNibble = (Perms >> 4) & 0xF;
|
||||
|
||||
if (HighNibble)
|
||||
{
|
||||
if (HighNibble == 1) { Mask |= PAGE_EXECUTE; }
|
||||
else if (HighNibble == 2) { Mask |= PAGE_EXECUTE_READ; }
|
||||
else { Mask |= PAGE_EXECUTE_READWRITE; }
|
||||
}
|
||||
|
||||
if (LowNibble)
|
||||
{
|
||||
if (LowNibble == 2) { Mask |= PAGE_READONLY; }
|
||||
else { Mask |= PAGE_READWRITE; }
|
||||
}
|
||||
|
||||
// Even though PAGE_NOCACHE and PAGE_WRITECOMBINE are unsupported on shared memory, that's a limitation on our side,
|
||||
// this function still adds them if they are present
|
||||
|
||||
switch (Perms & (XBOX_PAGE_GUARD | XBOX_PAGE_NOCACHE | XBOX_PAGE_WRITECOMBINE))
|
||||
{
|
||||
case XBOX_PAGE_GUARD:
|
||||
{
|
||||
Mask |= PAGE_GUARD;
|
||||
break;
|
||||
}
|
||||
|
||||
case XBOX_PAGE_NOCACHE:
|
||||
{
|
||||
Mask |= PAGE_NOCACHE;
|
||||
break;
|
||||
}
|
||||
|
||||
case XBOX_PAGE_WRITECOMBINE:
|
||||
{
|
||||
Mask |= PAGE_WRITECOMBINE;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// If we reach here it means that more than one permission modifier was specified, and so the input is
|
||||
// probably invalid
|
||||
|
||||
DbgPrintf("ConvertXboxToWinProtection: Memory permissions bug detected\n");
|
||||
return PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
}
|
||||
return Mask;
|
||||
}
|
||||
|
||||
bool PhysicalMemory::AllocatePT(PFN_COUNT PteNumber, VAddr addr)
|
||||
{
|
||||
PMMPTE pPde;
|
||||
|
|
|
@ -50,13 +50,6 @@ namespace xboxkrnl
|
|||
#include <map>
|
||||
|
||||
|
||||
/* PhysicalMemory class error codes */
|
||||
#define PMEMORY_STATUS unsigned int
|
||||
#define PMEMORY_SUCCESS 0x0
|
||||
#define PMEMORY_INSUFFICIENT_MEMORY 0x1
|
||||
#define PMEMORY_ALLOCATE_FRAGMENTED 0x2
|
||||
|
||||
|
||||
/* Global typedefs */
|
||||
typedef uintptr_t VAddr;
|
||||
typedef uintptr_t PAddr;
|
||||
|
@ -266,6 +259,10 @@ class PhysicalMemory
|
|||
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);
|
||||
// convert from Xbox to Windows permissions
|
||||
DWORD ConvertXboxToWinProtection(DWORD Perms);
|
||||
// add execute rights if the permission mask doesn't include it
|
||||
DWORD PatchXboxPermissions(DWORD Perms);
|
||||
// commit page tables (if necessary)
|
||||
bool AllocatePT(PFN_COUNT PteNumber, VAddr addr);
|
||||
// commit whatever free page is available and zero it
|
||||
|
|
|
@ -409,7 +409,7 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz
|
|||
if (RemoveFree(PteNumber, &pfn, 0, 0, m_HighestPage)) // MapViewOfFileEx path
|
||||
{
|
||||
MappingRoutine = &MapBlockWithMapViewOfFileEx;
|
||||
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, Perms, pfn);
|
||||
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, pfn);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
|
@ -426,7 +426,7 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz
|
|||
|
||||
bVAlloc = true;
|
||||
MappingRoutine = &MapBlockWithVirtualAlloc;
|
||||
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, Perms, 0);
|
||||
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, 0);
|
||||
|
||||
if (!addr) { goto Fail; }
|
||||
|
||||
|
@ -469,7 +469,7 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz
|
|||
PointerPte++;
|
||||
}
|
||||
EndingPte = PointerPte + PteNumber - 1;
|
||||
EndingPfn = pfn + (EndingPte - PointerPte);
|
||||
EndingPfn = pfn + PteNumber;
|
||||
|
||||
WritePte(PointerPte, EndingPte, TempPte, pfn);
|
||||
EndingPte->Hardware.GuardOrEnd = 1;
|
||||
|
@ -486,7 +486,17 @@ VAddr VMManager::AllocateSystemMemory(PageType BusyType, DWORD Perms, size_t Siz
|
|||
m_PagesByUsage[BusyType] += PteNumber;
|
||||
}
|
||||
|
||||
ConstructVMA(addr, ROUND_UP_4K(Size), MemoryRegionType::System, VMAType::Allocated, bVAlloc ? true : false);
|
||||
ConstructVMA(addr, ROUND_UP_4K(Size), MemoryRegionType::System, VMAType::Allocated, bVAlloc);
|
||||
|
||||
if (bAddGuardPage)
|
||||
{
|
||||
UpdateMemoryPermissions(addr, PAGE_SIZE, XBOX_PAGE_NOACCESS); // guard page of the stack
|
||||
UpdateMemoryPermissions(addr + PAGE_SIZE, ROUND_UP_4K(Size) - PAGE_SIZE, Perms); // actual stack pages
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateMemoryPermissions(addr, ROUND_UP_4K(Size), Perms);
|
||||
}
|
||||
|
||||
Unlock();
|
||||
RETURN(addr);
|
||||
|
@ -545,13 +555,14 @@ VAddr VMManager::AllocateContiguous(size_t Size, PAddr LowerAddress, PAddr Highe
|
|||
// Finally, write the pte's and the pfn's
|
||||
PointerPte = GetPteAddress(addr);
|
||||
EndingPte = PointerPte + PteNumber - 1;
|
||||
EndingPfn = pfn + (EndingPte - PointerPte);
|
||||
EndingPfn = pfn + PteNumber;
|
||||
|
||||
WritePte(PointerPte, EndingPte, TempPte, pfn);
|
||||
WritePfn(pfn, EndingPfn, PointerPte, PageType::Contiguous, true);
|
||||
EndingPte->Hardware.GuardOrEnd = 1;
|
||||
|
||||
ConstructVMA(addr, ROUND_UP_4K(Size), MemoryRegionType::Contiguous, VMAType::Allocated, false);
|
||||
UpdateMemoryPermissions(addr, ROUND_UP_4K(Size), Perms);
|
||||
|
||||
Unlock();
|
||||
RETURN(addr);
|
||||
|
@ -603,7 +614,7 @@ VAddr VMManager::MapDeviceMemory(PAddr Paddr, size_t Size, DWORD Perms)
|
|||
Lock();
|
||||
|
||||
MappingRoutine = &ReserveBlockWithVirtualAlloc;
|
||||
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, Perms, 0);
|
||||
addr = MapMemoryBlock(MappingRoutine, MemoryRegionType::System, PteNumber, 0);
|
||||
|
||||
if (!addr) { goto Fail; }
|
||||
|
||||
|
@ -980,7 +991,7 @@ xboxkrnl::NTSTATUS VMManager::XbFreeVirtualMemory(VAddr* addr, size_t* size, DWO
|
|||
RETURN(ret);
|
||||
}
|
||||
|
||||
VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, PFN_COUNT PteNumber, DWORD perms, PFN pfn)
|
||||
VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, PFN_COUNT PteNumber, PFN pfn)
|
||||
{
|
||||
PFN pfn;
|
||||
VAddr addr;
|
||||
|
@ -990,7 +1001,6 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type,
|
|||
|
||||
if (pfn) // MapViewOfFileEx specific
|
||||
{
|
||||
ConvertVProtectToMapViewProtection(&perms);
|
||||
FileOffsetLow = ROUND_DOWN(pfn << PAGE_SHIFT, m_AllocationGranularity);
|
||||
Size = (pfn << PAGE_SHIFT) + Size - FileOffsetLow;
|
||||
}
|
||||
|
@ -1017,7 +1027,7 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type,
|
|||
|
||||
size_t vma_end = it->first + it->second.size;
|
||||
|
||||
addr = (this->*MappingRoutine)(addr, Size, vma_end, perms, FileOffsetLow, pfn);
|
||||
addr = (this->*MappingRoutine)(addr, Size, vma_end, FileOffsetLow, pfn);
|
||||
|
||||
if (addr) { return addr; }
|
||||
|
||||
|
@ -1027,7 +1037,7 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type,
|
|||
// If we are here, it means we reached the end of the memory region. In desperation, we also try to map it from the
|
||||
// LastFree iterator and going backwards, since there could be holes created by deallocation operations...
|
||||
|
||||
auto begin_it = m_MemoryRegionArray[Type].RegionMap.begin();
|
||||
VMAIter begin_it = m_MemoryRegionArray[Type].RegionMap.begin();
|
||||
|
||||
if (m_MemoryRegionArray[Type].LastFree == begin_it)
|
||||
{
|
||||
|
@ -1052,7 +1062,7 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type,
|
|||
|
||||
size_t vma_end = it->first + it->second.size;
|
||||
|
||||
addr = (this->*MappingRoutine)(addr, Size, vma_end, perms, FileOffsetLow, pfn);
|
||||
addr = (this->*MappingRoutine)(addr, Size, vma_end, FileOffsetLow, pfn);
|
||||
|
||||
if (addr) { return addr; }
|
||||
}
|
||||
|
@ -1069,12 +1079,12 @@ VAddr VMManager::MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
VAddr VMManager::MapBlockWithMapViewOfFileEx(VAddr StartingAddr, size_t ViewSize, size_t VmaEnd, DWORD Perms,
|
||||
DWORD OffsetLow, PFN pfn)
|
||||
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, Perms, 0, OffsetLow, ViewSize, (void*)StartingAddr) == StartingAddr)
|
||||
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
|
||||
}
|
||||
|
@ -1082,12 +1092,11 @@ VAddr VMManager::MapBlockWithMapViewOfFileEx(VAddr StartingAddr, size_t ViewSize
|
|||
return NULL;
|
||||
}
|
||||
|
||||
VAddr VMManager::MapBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Perms,
|
||||
DWORD Unused, PFN Unused2)
|
||||
VAddr VMManager::MapBlockWithVirtualAlloc(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, XBOX_MEM_RESERVE | XBOX_MEM_COMMIT, Perms) == StartingAddr)
|
||||
if ((VAddr)VirtualAlloc((void*)StartingAddr, Size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE) == StartingAddr)
|
||||
{
|
||||
return StartingAddr;
|
||||
}
|
||||
|
@ -1095,12 +1104,11 @@ VAddr VMManager::MapBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_
|
|||
return NULL;
|
||||
}
|
||||
|
||||
VAddr VMManager::ReserveBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Unused,
|
||||
DWORD Unused2, PFN Unused3)
|
||||
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, XBOX_MEM_RESERVE, XBOX_PAGE_NOACCESS) == StartingAddr)
|
||||
if ((VAddr)VirtualAlloc((void*)StartingAddr, Size, MEM_RESERVE, PAGE_NOACCESS) == StartingAddr)
|
||||
{
|
||||
return StartingAddr;
|
||||
}
|
||||
|
@ -1108,19 +1116,6 @@ VAddr VMManager::ReserveBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, s
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void VMManager::ConvertVProtectToMapViewProtection(DWORD* perms)
|
||||
{
|
||||
// We always grant read / write access to the file view, we just check if we should also grant execute rights
|
||||
|
||||
if (*perms & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE))
|
||||
{
|
||||
*perms = FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE;
|
||||
return;
|
||||
}
|
||||
|
||||
*perms = FILE_MAP_READ | FILE_MAP_WRITE;
|
||||
}
|
||||
|
||||
void VMManager::ReprotectVMARange(VAddr target, size_t size, DWORD new_perms)
|
||||
{
|
||||
VAddr aligned_start = target & ~(UINT_PTR)PAGE_MASK;
|
||||
|
@ -1282,22 +1277,19 @@ VMAIter VMManager::MergeAdjacentVMA(VMAIter vma_handle, MemoryRegionType Type)
|
|||
return vma_handle;
|
||||
}
|
||||
|
||||
VMAIter VMManager::ReprotectVMA(VMAIter vma_handle, DWORD new_perms)
|
||||
void VMManager::UpdateMemoryPermissions(VAddr addr, size_t Size, DWORD Perms)
|
||||
{
|
||||
// PAGE_WRITECOMBINE/PAGE_NOCACHE are not allowed for shared memory, unless SEC_WRITECOMBINE/SEC_NOCACHE flag
|
||||
// was specified when calling the CreateFileMapping function. Considering that Cxbx doesn't emulate the caches,
|
||||
// it's probably safe to ignore these flags
|
||||
|
||||
VirtualMemoryArea& vma = vma_handle->second;
|
||||
DWORD dummy;
|
||||
vma.permissions = new_perms;
|
||||
if (!VirtualProtect((void*)vma.base, vma.size, vma.permissions & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE), &dummy))
|
||||
{
|
||||
CxbxKrnlCleanup("ReprotectVMA: VirtualProtect could not protect the vma! The error code was %d", GetLastError());
|
||||
}
|
||||
UpdatePageTableForVMA(vma);
|
||||
DWORD WindowsPerms = ConvertXboxToWinProtection(PatchXboxPermissions(Perms));
|
||||
|
||||
return MergeAdjacentVMA(vma_handle);
|
||||
DWORD dummy;
|
||||
if (!VirtualProtect((void*)addr, Size, WindowsPerms & ~(PAGE_WRITECOMBINE | PAGE_NOCACHE), &dummy))
|
||||
{
|
||||
DbgPrintf("VirtualProtect failed. The error code was %d\n", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
VMAIter VMManager::CheckExistenceVMA(VAddr addr, MemoryRegionType Type, size_t Size)
|
||||
|
|
|
@ -161,7 +161,7 @@ class VMManager : public PhysicalMemory
|
|||
|
||||
private:
|
||||
// typedef of pointer to a member function mapping a memory block
|
||||
typedef VAddr (VMManager::*MappingFn) (VAddr, size_t, size_t, DWORD, DWORD, PFN);
|
||||
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[MemoryRegionType::COUNT];
|
||||
// handle of the contiguous file mapping
|
||||
|
@ -188,19 +188,17 @@ class VMManager : public PhysicalMemory
|
|||
// clear all memory region structs
|
||||
void DestroyMemoryRegions();
|
||||
// map a memory block with the supplied allocation routine
|
||||
VAddr MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, PFN_COUNT PteNumber, DWORD perms, PFN pfn);
|
||||
VAddr MapMemoryBlock(MappingFn MappingRoutine, MemoryRegionType Type, PFN_COUNT PteNumber, PFN pfn);
|
||||
// helper function which maps a block with VirtualAlloc
|
||||
VAddr MapBlockWithVirtualAlloc(VAddr StartingAddr, size_t Size, size_t VmaEnd, DWORD Perms, DWORD Unused, PFN Unused2);
|
||||
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, DWORD Unused2, PFN Unused3);
|
||||
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 Perms, DWORD OffsetLow, PFN pfn);
|
||||
VAddr MapBlockWithMapViewOfFileEx(VAddr StartingAddr, size_t ViewSize, size_t VmaEnd, DWORD OffsetLow, PFN pfn);
|
||||
// destruct a vma
|
||||
void DestructVMA(VMAIter it, MemoryRegionType Type);
|
||||
// check if a vma exists at the supplied address. Also checks its size if specified
|
||||
VMAIter CheckExistenceVMA(VAddr addr, MemoryRegionType Type, size_t Size = 0);
|
||||
// convert VirtualProtect protection flags to file mapping protection flags
|
||||
void ConvertVProtectToMapViewProtection(DWORD* perms);
|
||||
// changes access permissions for a range of vma's, splitting them if necessary
|
||||
void ReprotectVMARange(VAddr target, size_t size, DWORD new_perms);
|
||||
// checks if a VAddr is valid; returns false if not
|
||||
|
@ -219,8 +217,8 @@ class VMManager : public PhysicalMemory
|
|||
VMAIter SplitVMA(VMAIter vma_handle, u32 offset_in_vma, MemoryRegionType Type);
|
||||
// merges the specified vma with adjacent ones if possible
|
||||
VMAIter MergeAdjacentVMA(VMAIter vma_handle, MemoryRegionType Type);
|
||||
// changes access permissions for a vma
|
||||
VMAIter ReprotectVMA(VMAIter vma_handle, DWORD new_perms);
|
||||
// changes the access permissions of a block of memory
|
||||
void UpdateMemoryPermissions(VAddr addr, size_t Size, DWORD Perms);
|
||||
// acquires the critical section
|
||||
void Lock();
|
||||
// releases the critical section
|
||||
|
|
Loading…
Reference in New Issue