/** ****************************************************************************** * 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 #include #include #include 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, ®ion_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, ®ion_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); }