From 54ba3480e471cc9d2fb8833c043d83725ebee70e Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Thu, 28 Jul 2016 16:56:05 -0500 Subject: [PATCH] xboxkrnl: NtAllocateVirtualMemory - Align the base address to a page boundary rather than returning an error code. Return ACCESS_DENIED if the user requests e.g. a 64k page in a 4k region. --- src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc | 140 ++++++++----------- 1 file changed, 62 insertions(+), 78 deletions(-) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc index dfcd1654f..c5dffebbf 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc @@ -56,29 +56,22 @@ uint32_t FromXdkProtectFlags(uint32_t protect) { return result; } -SHIM_CALL NtAllocateVirtualMemory_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t base_addr_ptr = SHIM_GET_ARG_32(0); - uint32_t base_addr_value = SHIM_MEM_32(base_addr_ptr); - uint32_t region_size_ptr = SHIM_GET_ARG_32(1); - uint32_t region_size_value = SHIM_MEM_32(region_size_ptr); - uint32_t alloc_type = SHIM_GET_ARG_32(2); // X_MEM_* bitmask - uint32_t protect_bits = SHIM_GET_ARG_32(3); // X_PAGE_* bitmask - uint32_t unknown = SHIM_GET_ARG_32(4); - - XELOGD("NtAllocateVirtualMemory(%.8X(%.8X), %.8X(%.8X), %.8X, %.8X, %.8X)", - base_addr_ptr, base_addr_value, region_size_ptr, region_size_value, - alloc_type, protect_bits, unknown); - +dword_result_t NtAllocateVirtualMemory(lpdword_t base_addr_ptr, + lpdword_t region_size_ptr, + dword_t alloc_type, dword_t protect_bits, + dword_t debug_memory) { // NTSTATUS // _Inout_ PVOID *BaseAddress, // _Inout_ PSIZE_T RegionSize, // _In_ ULONG AllocationType, // _In_ ULONG Protect - // ? handle? + // _In_ BOOLEAN DebugMemory - // I've only seen zero. - assert_true(unknown == 0); + assert_not_null(base_addr_ptr); + assert_not_null(region_size_ptr); + + // Set to TRUE when allocation is from devkit memory area. + assert_true(debug_memory == 0); // This allocates memory from the kernel heap, which is initialized on startup // and shared by both the kernel implementation and user code. @@ -86,19 +79,16 @@ SHIM_CALL NtAllocateVirtualMemory_shim(PPCContext* ppc_context, // it's simple today we could extend it to do better things in the future. // Must request a size. - if (!region_size_value) { - SHIM_SET_RETURN_32(X_STATUS_INVALID_PARAMETER); - return; + if (!base_addr_ptr) { + return X_STATUS_INVALID_PARAMETER; } // Check allocation type. if (!(alloc_type & (X_MEM_COMMIT | X_MEM_RESET | X_MEM_RESERVE))) { - SHIM_SET_RETURN_32(X_STATUS_INVALID_PARAMETER); - return; + return X_STATUS_INVALID_PARAMETER; } // If MEM_RESET is set only MEM_RESET can be set. if (alloc_type & X_MEM_RESET && (alloc_type & ~X_MEM_RESET)) { - SHIM_SET_RETURN_32(X_STATUS_INVALID_PARAMETER); - return; + return X_STATUS_INVALID_PARAMETER; } // Don't allow games to set execute bits. if (protect_bits & (X_PAGE_EXECUTE | X_PAGE_EXECUTE_READ | @@ -111,18 +101,14 @@ SHIM_CALL NtAllocateVirtualMemory_shim(PPCContext* ppc_context, if (alloc_type & X_MEM_LARGE_PAGES) { page_size = 64 * 1024; } - if (int32_t(region_size_value) < 0) { - // Some games pass in negative sizes. - region_size_value = -int32_t(region_size_value); - } - uint32_t adjusted_size = xe::round_up(region_size_value, page_size); - // Some games (BF1943) do this, but then if we return an error code it'll - // allocate with a smaller page size. - if (base_addr_value % page_size != 0) { - SHIM_SET_RETURN_32(X_STATUS_MAPPED_ALIGNMENT); - return; - } + // Round the base address down to the nearest page boundary. + uint32_t adjusted_base = *base_addr_ptr - (*base_addr_ptr % page_size); + // For some reason, some games pass in negative sizes. + uint32_t adjusted_size = int32_t(*region_size_ptr) < 0 + ? -int32_t(*region_size_ptr) + : *region_size_ptr; + adjusted_size = xe::round_up(adjusted_size, page_size); // Allocate. uint32_t allocation_type = 0; @@ -138,28 +124,32 @@ SHIM_CALL NtAllocateVirtualMemory_shim(PPCContext* ppc_context, } uint32_t protect = FromXdkProtectFlags(protect_bits); uint32_t address = 0; - if (base_addr_value) { - auto heap = kernel_state->memory()->LookupHeap(base_addr_value); - if (heap->AllocFixed(base_addr_value, adjusted_size, page_size, + if (adjusted_base != 0) { + auto heap = kernel_memory()->LookupHeap(adjusted_base); + if (heap->page_size() != page_size) { + // Specified the wrong page size for the wrong heap. + return X_STATUS_ACCESS_DENIED; + } + + if (heap->AllocFixed(adjusted_base, adjusted_size, page_size, allocation_type, protect)) { - address = base_addr_value; + address = adjusted_base; } } else { bool top_down = !!(alloc_type & X_MEM_TOP_DOWN); - auto heap = kernel_state->memory()->LookupHeapByType(false, page_size); + auto heap = kernel_memory()->LookupHeapByType(false, page_size); heap->Alloc(adjusted_size, page_size, allocation_type, protect, top_down, &address); } if (!address) { // Failed - assume no memory available. - SHIM_SET_RETURN_32(X_STATUS_NO_MEMORY); - return; + return X_STATUS_NO_MEMORY; } // Zero memory, if needed. if (address && !(alloc_type & X_MEM_NOZERO)) { if (alloc_type & X_MEM_COMMIT) { - kernel_state->memory()->Zero(address, adjusted_size); + kernel_memory()->Zero(address, adjusted_size); } } @@ -167,10 +157,12 @@ SHIM_CALL NtAllocateVirtualMemory_shim(PPCContext* ppc_context, // Stash back. // Maybe set X_STATUS_ALREADY_COMMITTED if MEM_COMMIT? - SHIM_SET_MEM_32(base_addr_ptr, address); - SHIM_SET_MEM_32(region_size_ptr, adjusted_size); - SHIM_SET_RETURN_32(X_STATUS_SUCCESS); + *base_addr_ptr = address; + *region_size_ptr = adjusted_size; + return X_STATUS_SUCCESS; } +DECLARE_XBOXKRNL_EXPORT(NtAllocateVirtualMemory, + ExportTag::kImplemented | ExportTag::kMemory); SHIM_CALL NtFreeVirtualMemory_shim(PPCContext* ppc_context, KernelState* kernel_state) { @@ -180,20 +172,20 @@ SHIM_CALL NtFreeVirtualMemory_shim(PPCContext* ppc_context, uint32_t region_size_value = SHIM_MEM_32(region_size_ptr); // X_MEM_DECOMMIT | X_MEM_RELEASE uint32_t free_type = SHIM_GET_ARG_32(2); - uint32_t unknown = SHIM_GET_ARG_32(3); + uint32_t debug_memory = SHIM_GET_ARG_32(3); XELOGD("NtFreeVirtualMemory(%.8X(%.8X), %.8X(%.8X), %.8X, %.8X)", base_addr_ptr, base_addr_value, region_size_ptr, region_size_value, - free_type, unknown); + free_type, debug_memory); // NTSTATUS // _Inout_ PVOID *BaseAddress, // _Inout_ PSIZE_T RegionSize, // _In_ ULONG FreeType - // ? handle? + // _In_ BOOLEAN DebugMemory - // I've only seen zero. - assert_true(unknown == 0); + // Set to TRUE when freeing external devkit memory. + assert_true(debug_memory == 0); if (!base_addr_value) { SHIM_SET_RETURN_32(X_STATUS_MEMORY_NOT_ALLOCATED); @@ -270,26 +262,18 @@ SHIM_CALL NtQueryVirtualMemory_shim(PPCContext* ppc_context, SHIM_SET_RETURN_32(X_STATUS_SUCCESS); } -SHIM_CALL MmAllocatePhysicalMemoryEx_shim(PPCContext* ppc_context, - KernelState* kernel_state) { - uint32_t type = SHIM_GET_ARG_32(0); - uint32_t region_size = SHIM_GET_ARG_32(1); - uint32_t protect_bits = SHIM_GET_ARG_32(2); - uint32_t min_addr_range = SHIM_GET_ARG_32(3); - uint32_t max_addr_range = SHIM_GET_ARG_32(4); - uint32_t alignment = SHIM_GET_ARG_32(5); - - XELOGD("MmAllocatePhysicalMemoryEx(%d, %.8X, %.8X, %.8X, %.8X, %.8X)", type, - region_size, protect_bits, min_addr_range, max_addr_range, alignment); - +dword_result_t MmAllocatePhysicalMemoryEx(dword_t flags, dword_t region_size, + dword_t protect_bits, + dword_t min_addr_range, + dword_t max_addr_range, + dword_t alignment) { // Type will usually be 0 (user request?), where 1 and 2 are sometimes made // by D3D/etc. // Check protection bits. if (!(protect_bits & (X_PAGE_READONLY | X_PAGE_READWRITE))) { XELOGE("MmAllocatePhysicalMemoryEx: bad protection bits"); - SHIM_SET_RETURN_32(0); - return; + return 0; } // Either may be OR'ed into protect_bits: @@ -314,27 +298,23 @@ SHIM_CALL MmAllocatePhysicalMemoryEx_shim(PPCContext* ppc_context, uint32_t adjusted_size = xe::round_up(region_size, page_size); uint32_t adjusted_alignment = xe::round_up(alignment, page_size); - // Callers can pick an address to allocate with min_addr_range/max_addr_range - // and the memory must be allocated there. I haven't seen a game do this, - // and instead they all do min=0 / max=-1 to indicate the system should pick. - // If we have to suport arbitrary placement things will get nasty. - uint32_t allocation_type = kMemoryAllocationReserve | kMemoryAllocationCommit; uint32_t protect = FromXdkProtectFlags(protect_bits); bool top_down = true; - auto heap = kernel_state->memory()->LookupHeapByType(true, page_size); + auto heap = kernel_memory()->LookupHeapByType(true, page_size); uint32_t base_address; if (!heap->AllocRange(min_addr_range, max_addr_range, adjusted_size, adjusted_alignment, allocation_type, protect, top_down, &base_address)) { // Failed - assume no memory available. - SHIM_SET_RETURN_32(0); - return; + return 0; } XELOGD("MmAllocatePhysicalMemoryEx = %.8X", base_address); - SHIM_SET_RETURN_32(base_address); + return base_address; } +DECLARE_XBOXKRNL_EXPORT(MmAllocatePhysicalMemoryEx, + ExportTag::kImplemented | ExportTag::kMemory); SHIM_CALL MmFreePhysicalMemory_shim(PPCContext* ppc_context, KernelState* kernel_state) { @@ -373,7 +353,8 @@ void MmSetAddressProtect(lpvoid_t base_address, dword_t region_size, auto heap = kernel_memory()->LookupHeap(base_address); heap->Protect(base_address.guest_address(), region_size, protect); } -DECLARE_XBOXKRNL_EXPORT(MmSetAddressProtect, ExportTag::kMemory); +DECLARE_XBOXKRNL_EXPORT(MmSetAddressProtect, + ExportTag::kImplemented | ExportTag::kMemory); SHIM_CALL MmQueryAllocationSize_shim(PPCContext* ppc_context, KernelState* kernel_state) { @@ -568,6 +549,12 @@ SHIM_CALL ExFreePool_shim(PPCContext* ppc_context, KernelState* kernel_state) { kernel_state->memory()->SystemHeapFree(base_address); } +dword_result_t KeGetImagePageTableEntry(lpvoid_t address) { + // Unknown + return 1; +} +DECLARE_XBOXKRNL_EXPORT(KeGetImagePageTableEntry, ExportTag::kStub); + SHIM_CALL KeLockL2_shim(PPCContext* ppc_context, KernelState* kernel_state) { // Ignored for now. This is just a perf optimization, I think. // It may be useful as a hint for CPU-GPU transfer. @@ -610,11 +597,8 @@ DECLARE_XBOXKRNL_EXPORT(MmDeleteKernelStack, ExportTag::kImplemented); void RegisterMemoryExports(xe::cpu::ExportResolver* export_resolver, KernelState* kernel_state) { - SHIM_SET_MAPPING("xboxkrnl.exe", NtAllocateVirtualMemory, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtFreeVirtualMemory, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtQueryVirtualMemory, state); - // SHIM_SET_MAPPING("xboxkrnl.exe", MmAllocatePhysicalMemory, state); - SHIM_SET_MAPPING("xboxkrnl.exe", MmAllocatePhysicalMemoryEx, state); SHIM_SET_MAPPING("xboxkrnl.exe", MmFreePhysicalMemory, state); SHIM_SET_MAPPING("xboxkrnl.exe", MmQueryAddressProtect, state); SHIM_SET_MAPPING("xboxkrnl.exe", MmQueryAllocationSize, state);