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.
This commit is contained in:
parent
6af8546c49
commit
54ba3480e4
|
@ -56,29 +56,22 @@ uint32_t FromXdkProtectFlags(uint32_t protect) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
SHIM_CALL NtAllocateVirtualMemory_shim(PPCContext* ppc_context,
|
dword_result_t NtAllocateVirtualMemory(lpdword_t base_addr_ptr,
|
||||||
KernelState* kernel_state) {
|
lpdword_t region_size_ptr,
|
||||||
uint32_t base_addr_ptr = SHIM_GET_ARG_32(0);
|
dword_t alloc_type, dword_t protect_bits,
|
||||||
uint32_t base_addr_value = SHIM_MEM_32(base_addr_ptr);
|
dword_t debug_memory) {
|
||||||
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);
|
|
||||||
|
|
||||||
// NTSTATUS
|
// NTSTATUS
|
||||||
// _Inout_ PVOID *BaseAddress,
|
// _Inout_ PVOID *BaseAddress,
|
||||||
// _Inout_ PSIZE_T RegionSize,
|
// _Inout_ PSIZE_T RegionSize,
|
||||||
// _In_ ULONG AllocationType,
|
// _In_ ULONG AllocationType,
|
||||||
// _In_ ULONG Protect
|
// _In_ ULONG Protect
|
||||||
// ? handle?
|
// _In_ BOOLEAN DebugMemory
|
||||||
|
|
||||||
// I've only seen zero.
|
assert_not_null(base_addr_ptr);
|
||||||
assert_true(unknown == 0);
|
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
|
// This allocates memory from the kernel heap, which is initialized on startup
|
||||||
// and shared by both the kernel implementation and user code.
|
// 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.
|
// it's simple today we could extend it to do better things in the future.
|
||||||
|
|
||||||
// Must request a size.
|
// Must request a size.
|
||||||
if (!region_size_value) {
|
if (!base_addr_ptr) {
|
||||||
SHIM_SET_RETURN_32(X_STATUS_INVALID_PARAMETER);
|
return X_STATUS_INVALID_PARAMETER;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Check allocation type.
|
// Check allocation type.
|
||||||
if (!(alloc_type & (X_MEM_COMMIT | X_MEM_RESET | X_MEM_RESERVE))) {
|
if (!(alloc_type & (X_MEM_COMMIT | X_MEM_RESET | X_MEM_RESERVE))) {
|
||||||
SHIM_SET_RETURN_32(X_STATUS_INVALID_PARAMETER);
|
return X_STATUS_INVALID_PARAMETER;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// If MEM_RESET is set only MEM_RESET can be set.
|
// If MEM_RESET is set only MEM_RESET can be set.
|
||||||
if (alloc_type & X_MEM_RESET && (alloc_type & ~X_MEM_RESET)) {
|
if (alloc_type & X_MEM_RESET && (alloc_type & ~X_MEM_RESET)) {
|
||||||
SHIM_SET_RETURN_32(X_STATUS_INVALID_PARAMETER);
|
return X_STATUS_INVALID_PARAMETER;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Don't allow games to set execute bits.
|
// Don't allow games to set execute bits.
|
||||||
if (protect_bits & (X_PAGE_EXECUTE | X_PAGE_EXECUTE_READ |
|
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) {
|
if (alloc_type & X_MEM_LARGE_PAGES) {
|
||||||
page_size = 64 * 1024;
|
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
|
// Round the base address down to the nearest page boundary.
|
||||||
// allocate with a smaller page size.
|
uint32_t adjusted_base = *base_addr_ptr - (*base_addr_ptr % page_size);
|
||||||
if (base_addr_value % page_size != 0) {
|
// For some reason, some games pass in negative sizes.
|
||||||
SHIM_SET_RETURN_32(X_STATUS_MAPPED_ALIGNMENT);
|
uint32_t adjusted_size = int32_t(*region_size_ptr) < 0
|
||||||
return;
|
? -int32_t(*region_size_ptr)
|
||||||
}
|
: *region_size_ptr;
|
||||||
|
adjusted_size = xe::round_up(adjusted_size, page_size);
|
||||||
|
|
||||||
// Allocate.
|
// Allocate.
|
||||||
uint32_t allocation_type = 0;
|
uint32_t allocation_type = 0;
|
||||||
|
@ -138,28 +124,32 @@ SHIM_CALL NtAllocateVirtualMemory_shim(PPCContext* ppc_context,
|
||||||
}
|
}
|
||||||
uint32_t protect = FromXdkProtectFlags(protect_bits);
|
uint32_t protect = FromXdkProtectFlags(protect_bits);
|
||||||
uint32_t address = 0;
|
uint32_t address = 0;
|
||||||
if (base_addr_value) {
|
if (adjusted_base != 0) {
|
||||||
auto heap = kernel_state->memory()->LookupHeap(base_addr_value);
|
auto heap = kernel_memory()->LookupHeap(adjusted_base);
|
||||||
if (heap->AllocFixed(base_addr_value, adjusted_size, page_size,
|
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)) {
|
allocation_type, protect)) {
|
||||||
address = base_addr_value;
|
address = adjusted_base;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bool top_down = !!(alloc_type & X_MEM_TOP_DOWN);
|
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,
|
heap->Alloc(adjusted_size, page_size, allocation_type, protect, top_down,
|
||||||
&address);
|
&address);
|
||||||
}
|
}
|
||||||
if (!address) {
|
if (!address) {
|
||||||
// Failed - assume no memory available.
|
// Failed - assume no memory available.
|
||||||
SHIM_SET_RETURN_32(X_STATUS_NO_MEMORY);
|
return X_STATUS_NO_MEMORY;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero memory, if needed.
|
// Zero memory, if needed.
|
||||||
if (address && !(alloc_type & X_MEM_NOZERO)) {
|
if (address && !(alloc_type & X_MEM_NOZERO)) {
|
||||||
if (alloc_type & X_MEM_COMMIT) {
|
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.
|
// Stash back.
|
||||||
// Maybe set X_STATUS_ALREADY_COMMITTED if MEM_COMMIT?
|
// Maybe set X_STATUS_ALREADY_COMMITTED if MEM_COMMIT?
|
||||||
SHIM_SET_MEM_32(base_addr_ptr, address);
|
*base_addr_ptr = address;
|
||||||
SHIM_SET_MEM_32(region_size_ptr, adjusted_size);
|
*region_size_ptr = adjusted_size;
|
||||||
SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
DECLARE_XBOXKRNL_EXPORT(NtAllocateVirtualMemory,
|
||||||
|
ExportTag::kImplemented | ExportTag::kMemory);
|
||||||
|
|
||||||
SHIM_CALL NtFreeVirtualMemory_shim(PPCContext* ppc_context,
|
SHIM_CALL NtFreeVirtualMemory_shim(PPCContext* ppc_context,
|
||||||
KernelState* kernel_state) {
|
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);
|
uint32_t region_size_value = SHIM_MEM_32(region_size_ptr);
|
||||||
// X_MEM_DECOMMIT | X_MEM_RELEASE
|
// X_MEM_DECOMMIT | X_MEM_RELEASE
|
||||||
uint32_t free_type = SHIM_GET_ARG_32(2);
|
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)",
|
XELOGD("NtFreeVirtualMemory(%.8X(%.8X), %.8X(%.8X), %.8X, %.8X)",
|
||||||
base_addr_ptr, base_addr_value, region_size_ptr, region_size_value,
|
base_addr_ptr, base_addr_value, region_size_ptr, region_size_value,
|
||||||
free_type, unknown);
|
free_type, debug_memory);
|
||||||
|
|
||||||
// NTSTATUS
|
// NTSTATUS
|
||||||
// _Inout_ PVOID *BaseAddress,
|
// _Inout_ PVOID *BaseAddress,
|
||||||
// _Inout_ PSIZE_T RegionSize,
|
// _Inout_ PSIZE_T RegionSize,
|
||||||
// _In_ ULONG FreeType
|
// _In_ ULONG FreeType
|
||||||
// ? handle?
|
// _In_ BOOLEAN DebugMemory
|
||||||
|
|
||||||
// I've only seen zero.
|
// Set to TRUE when freeing external devkit memory.
|
||||||
assert_true(unknown == 0);
|
assert_true(debug_memory == 0);
|
||||||
|
|
||||||
if (!base_addr_value) {
|
if (!base_addr_value) {
|
||||||
SHIM_SET_RETURN_32(X_STATUS_MEMORY_NOT_ALLOCATED);
|
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_SET_RETURN_32(X_STATUS_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
SHIM_CALL MmAllocatePhysicalMemoryEx_shim(PPCContext* ppc_context,
|
dword_result_t MmAllocatePhysicalMemoryEx(dword_t flags, dword_t region_size,
|
||||||
KernelState* kernel_state) {
|
dword_t protect_bits,
|
||||||
uint32_t type = SHIM_GET_ARG_32(0);
|
dword_t min_addr_range,
|
||||||
uint32_t region_size = SHIM_GET_ARG_32(1);
|
dword_t max_addr_range,
|
||||||
uint32_t protect_bits = SHIM_GET_ARG_32(2);
|
dword_t alignment) {
|
||||||
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);
|
|
||||||
|
|
||||||
// Type will usually be 0 (user request?), where 1 and 2 are sometimes made
|
// Type will usually be 0 (user request?), where 1 and 2 are sometimes made
|
||||||
// by D3D/etc.
|
// by D3D/etc.
|
||||||
|
|
||||||
// Check protection bits.
|
// Check protection bits.
|
||||||
if (!(protect_bits & (X_PAGE_READONLY | X_PAGE_READWRITE))) {
|
if (!(protect_bits & (X_PAGE_READONLY | X_PAGE_READWRITE))) {
|
||||||
XELOGE("MmAllocatePhysicalMemoryEx: bad protection bits");
|
XELOGE("MmAllocatePhysicalMemoryEx: bad protection bits");
|
||||||
SHIM_SET_RETURN_32(0);
|
return 0;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Either may be OR'ed into protect_bits:
|
// 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_size = xe::round_up(region_size, page_size);
|
||||||
uint32_t adjusted_alignment = xe::round_up(alignment, 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 allocation_type = kMemoryAllocationReserve | kMemoryAllocationCommit;
|
||||||
uint32_t protect = FromXdkProtectFlags(protect_bits);
|
uint32_t protect = FromXdkProtectFlags(protect_bits);
|
||||||
bool top_down = true;
|
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;
|
uint32_t base_address;
|
||||||
if (!heap->AllocRange(min_addr_range, max_addr_range, adjusted_size,
|
if (!heap->AllocRange(min_addr_range, max_addr_range, adjusted_size,
|
||||||
adjusted_alignment, allocation_type, protect, top_down,
|
adjusted_alignment, allocation_type, protect, top_down,
|
||||||
&base_address)) {
|
&base_address)) {
|
||||||
// Failed - assume no memory available.
|
// Failed - assume no memory available.
|
||||||
SHIM_SET_RETURN_32(0);
|
return 0;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
XELOGD("MmAllocatePhysicalMemoryEx = %.8X", base_address);
|
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,
|
SHIM_CALL MmFreePhysicalMemory_shim(PPCContext* ppc_context,
|
||||||
KernelState* kernel_state) {
|
KernelState* kernel_state) {
|
||||||
|
@ -373,7 +353,8 @@ void MmSetAddressProtect(lpvoid_t base_address, dword_t region_size,
|
||||||
auto heap = kernel_memory()->LookupHeap(base_address);
|
auto heap = kernel_memory()->LookupHeap(base_address);
|
||||||
heap->Protect(base_address.guest_address(), region_size, protect);
|
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,
|
SHIM_CALL MmQueryAllocationSize_shim(PPCContext* ppc_context,
|
||||||
KernelState* kernel_state) {
|
KernelState* kernel_state) {
|
||||||
|
@ -568,6 +549,12 @@ SHIM_CALL ExFreePool_shim(PPCContext* ppc_context, KernelState* kernel_state) {
|
||||||
kernel_state->memory()->SystemHeapFree(base_address);
|
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) {
|
SHIM_CALL KeLockL2_shim(PPCContext* ppc_context, KernelState* kernel_state) {
|
||||||
// Ignored for now. This is just a perf optimization, I think.
|
// Ignored for now. This is just a perf optimization, I think.
|
||||||
// It may be useful as a hint for CPU-GPU transfer.
|
// 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,
|
void RegisterMemoryExports(xe::cpu::ExportResolver* export_resolver,
|
||||||
KernelState* kernel_state) {
|
KernelState* kernel_state) {
|
||||||
SHIM_SET_MAPPING("xboxkrnl.exe", NtAllocateVirtualMemory, state);
|
|
||||||
SHIM_SET_MAPPING("xboxkrnl.exe", NtFreeVirtualMemory, state);
|
SHIM_SET_MAPPING("xboxkrnl.exe", NtFreeVirtualMemory, state);
|
||||||
SHIM_SET_MAPPING("xboxkrnl.exe", NtQueryVirtualMemory, 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", MmFreePhysicalMemory, state);
|
||||||
SHIM_SET_MAPPING("xboxkrnl.exe", MmQueryAddressProtect, state);
|
SHIM_SET_MAPPING("xboxkrnl.exe", MmQueryAddressProtect, state);
|
||||||
SHIM_SET_MAPPING("xboxkrnl.exe", MmQueryAllocationSize, state);
|
SHIM_SET_MAPPING("xboxkrnl.exe", MmQueryAllocationSize, state);
|
||||||
|
|
Loading…
Reference in New Issue