Files
Xenia-Canary/src/xenia/kernel/modules/xboxkrnl/xboxkrnl_memory.cc

196 lines
6.0 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/kernel/modules/xboxkrnl/xboxkrnl_memory.h>
#include <xenia/kernel/shim_utils.h>
#include <xenia/kernel/modules/xboxkrnl/kernel_state.h>
#include <xenia/kernel/modules/xboxkrnl/xboxkrnl_private.h>
using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::xboxkrnl;
namespace xe {
namespace kernel {
namespace xboxkrnl {
X_STATUS xeNtAllocateVirtualMemory(
uint32_t* base_addr_ptr, uint32_t* region_size_ptr,
uint32_t allocation_type, uint32_t protect_bits,
uint32_t unknown) {
KernelState* state = shared_kernel_state_;
XEASSERTNOTNULL(state);
// NTSTATUS
// _Inout_ PVOID *BaseAddress,
// _In_ ULONG_PTR ZeroBits,
// _Inout_ PSIZE_T RegionSize,
// _In_ ULONG AllocationType,
// _In_ ULONG Protect
// ? handle?
// I've only seen zero.
XEASSERT(unknown == 0);
// This allocates memory from the kernel heap, which is initialized on startup
// and shared by both the kernel implementation and user code.
// The xe_memory_ref object is used to actually get the memory, and although
// it's simple today we could extend it to do better things in the future.
// Must request a size.
if (!*region_size_ptr) {
return X_STATUS_INVALID_PARAMETER;
}
// Check allocation type.
if (!(allocation_type & (X_MEM_COMMIT | X_MEM_RESET | X_MEM_RESERVE))) {
return X_STATUS_INVALID_PARAMETER;
}
// If MEM_RESET is set only MEM_RESET can be set.
if (allocation_type & X_MEM_RESET && (allocation_type & ~X_MEM_RESET)) {
return X_STATUS_INVALID_PARAMETER;
}
// Don't allow games to set execute bits.
if (protect_bits & (X_PAGE_EXECUTE | X_PAGE_EXECUTE_READ |
X_PAGE_EXECUTE_READWRITE | X_PAGE_EXECUTE_WRITECOPY)) {
return X_STATUS_ACCESS_DENIED;
}
// Adjust size.
uint32_t adjusted_size = *region_size_ptr;
// TODO(benvanik): adjust based on page size flags/etc?
// TODO(benvanik): support different allocation types.
// Right now we treat everything as a commit and ignore allocations that have
// already happened.
if (*base_addr_ptr) {
// Having a pointer already means that this is likely a follow-on COMMIT.
return X_STATUS_SUCCESS;
}
// Allocate.
uint32_t flags = 0;
uint32_t addr = xe_memory_heap_alloc(
state->memory(), *base_addr_ptr, adjusted_size, flags);
if (!addr) {
// Failed - assume no memory available.
return X_STATUS_NO_MEMORY;
}
// Stash back.
// Maybe set X_STATUS_ALREADY_COMMITTED if MEM_COMMIT?
*base_addr_ptr = addr;
*region_size_ptr = adjusted_size;
return X_STATUS_SUCCESS;
}
SHIM_CALL NtAllocateVirtualMemory_shim(
xe_ppc_state_t* ppc_state, KernelState* 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 allocation_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,
allocation_type, protect_bits, unknown);
X_STATUS result = xeNtAllocateVirtualMemory(
&base_addr_value, &region_size_value,
allocation_type, protect_bits, unknown);
if (XSUCCEEDED(result)) {
SHIM_SET_MEM_32(base_addr_ptr, base_addr_value);
SHIM_SET_MEM_32(region_size_ptr, region_size_value);
}
SHIM_SET_RETURN(result);
}
X_STATUS xeNtFreeVirtualMemory(
uint32_t* base_addr_ptr, uint32_t* region_size_ptr,
uint32_t free_type, uint32_t unknown) {
KernelState* state = shared_kernel_state_;
XEASSERTNOTNULL(state);
// NTSTATUS
// _Inout_ PVOID *BaseAddress,
// _Inout_ PSIZE_T RegionSize,
// _In_ ULONG FreeType
// ? handle?
// I've only seen zero.
XEASSERT(unknown == 0);
if (!*base_addr_ptr) {
return X_STATUS_MEMORY_NOT_ALLOCATED;
}
// Free.
uint32_t flags = 0;
uint32_t freed_size = xe_memory_heap_free(state->memory(), *base_addr_ptr,
flags);
if (!freed_size) {
return X_STATUS_UNSUCCESSFUL;
}
// Stash back.
*region_size_ptr = freed_size;
return X_STATUS_SUCCESS;
}
SHIM_CALL NtFreeVirtualMemory_shim(
xe_ppc_state_t* ppc_state, KernelState* 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);
// X_MEM_DECOMMIT | X_MEM_RELEASE
uint32_t free_type = SHIM_GET_ARG_32(2);
uint32_t unknown = 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);
X_STATUS result = xeNtFreeVirtualMemory(
&base_addr_value, &region_size_value,
free_type, unknown);
if (XSUCCEEDED(result)) {
SHIM_SET_MEM_32(base_addr_ptr, base_addr_value);
SHIM_SET_MEM_32(region_size_ptr, region_size_value);
}
SHIM_SET_RETURN(result);
}
} // namespace xboxkrnl
} // namespace kernel
} // namespace xe
void xe::kernel::xboxkrnl::RegisterMemoryExports(
ExportResolver* export_resolver, KernelState* state) {
SHIM_SET_MAPPING("xboxkrnl.exe", NtAllocateVirtualMemory, state);
SHIM_SET_MAPPING("xboxkrnl.exe", NtFreeVirtualMemory, state);
}