diff options
author | Yuri Kunde Schlesner <yuriks@yuriks.net> | 2015-08-22 14:01:57 -0700 |
---|---|---|
committer | Yuri Kunde Schlesner <yuriks@yuriks.net> | 2015-08-22 14:01:57 -0700 |
commit | 3efb205a68d38fe377b2c27349d91ec4c6a2d390 (patch) | |
tree | 4326d8394e66fec3831ce6b9851da689e5aa78e8 /src/core/hle/svc.cpp | |
parent | d8908aef6389c83590e5f6fa3a9dc5ea5fe8fced (diff) | |
parent | 840b85690b5de1e7ca89763ff6ca58cbe9b6d68f (diff) |
Merge pull request #1025 from yuriks/heap-management
Kernel: Correct(er) handling of Heap and Linear Heap allocations
Diffstat (limited to 'src/core/hle/svc.cpp')
-rw-r--r-- | src/core/hle/svc.cpp | 158 |
1 files changed, 143 insertions, 15 deletions
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index bb64fdfb7..89ac45a6f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -10,11 +10,11 @@ #include "common/symbols.h" #include "core/core_timing.h" -#include "core/mem_map.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/memory.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" @@ -41,32 +41,114 @@ const ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel, const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E +const ResultCode ERR_MISALIGNED_ADDRESS{ // 0xE0E01BF1 + ErrorDescription::MisalignedAddress, ErrorModule::OS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage}; +const ResultCode ERR_MISALIGNED_SIZE{ // 0xE0E01BF2 + ErrorDescription::MisalignedSize, ErrorModule::OS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage}; +const ResultCode ERR_INVALID_COMBINATION{ // 0xE0E01BEE + ErrorDescription::InvalidCombination, ErrorModule::OS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage}; + enum ControlMemoryOperation { - MEMORY_OPERATION_HEAP = 0x00000003, - MEMORY_OPERATION_GSP_HEAP = 0x00010003, + MEMOP_FREE = 1, + MEMOP_RESERVE = 2, // This operation seems to be unsupported in the kernel + MEMOP_COMMIT = 3, + MEMOP_MAP = 4, + MEMOP_UNMAP = 5, + MEMOP_PROTECT = 6, + MEMOP_OPERATION_MASK = 0xFF, + + MEMOP_REGION_APP = 0x100, + MEMOP_REGION_SYSTEM = 0x200, + MEMOP_REGION_BASE = 0x300, + MEMOP_REGION_MASK = 0xF00, + + MEMOP_LINEAR = 0x10000, }; /// Map application or GSP heap memory static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { - LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", + using namespace Kernel; + + LOG_DEBUG(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=0x%X, permissions=0x%08X", operation, addr0, addr1, size, permissions); - switch (operation) { + if ((addr0 & Memory::PAGE_MASK) != 0 || (addr1 & Memory::PAGE_MASK) != 0) { + return ERR_MISALIGNED_ADDRESS; + } + if ((size & Memory::PAGE_MASK) != 0) { + return ERR_MISALIGNED_SIZE; + } + + u32 region = operation & MEMOP_REGION_MASK; + operation &= ~MEMOP_REGION_MASK; + + if (region != 0) { + LOG_WARNING(Kernel_SVC, "ControlMemory with specified region not supported, region=%X", region); + } + + if ((permissions & (u32)MemoryPermission::ReadWrite) != permissions) { + return ERR_INVALID_COMBINATION; + } + VMAPermission vma_permissions = (VMAPermission)permissions; + + auto& process = *g_current_process; + + switch (operation & MEMOP_OPERATION_MASK) { + case MEMOP_FREE: + { + if (addr0 >= Memory::HEAP_VADDR && addr0 < Memory::HEAP_VADDR_END) { + ResultCode result = process.HeapFree(addr0, size); + if (result.IsError()) return result; + } else if (addr0 >= process.GetLinearHeapBase() && addr0 < process.GetLinearHeapLimit()) { + ResultCode result = process.LinearFree(addr0, size); + if (result.IsError()) return result; + } else { + return ERR_INVALID_ADDRESS; + } + *out_addr = addr0; + break; + } + + case MEMOP_COMMIT: + { + if (operation & MEMOP_LINEAR) { + CASCADE_RESULT(*out_addr, process.LinearAllocate(addr0, size, vma_permissions)); + } else { + CASCADE_RESULT(*out_addr, process.HeapAllocate(addr0, size, vma_permissions)); + } + break; + } - // Map normal heap memory - case MEMORY_OPERATION_HEAP: - *out_addr = Memory::MapBlock_Heap(size, operation, permissions); + case MEMOP_MAP: // TODO: This is just a hack to avoid regressions until memory aliasing is implemented + { + CASCADE_RESULT(*out_addr, process.HeapAllocate(addr0, size, vma_permissions)); break; + } + + case MEMOP_UNMAP: // TODO: This is just a hack to avoid regressions until memory aliasing is implemented + { + ResultCode result = process.HeapFree(addr0, size); + if (result.IsError()) return result; + break; + } - // Map GSP heap memory - case MEMORY_OPERATION_GSP_HEAP: - *out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions); + case MEMOP_PROTECT: + { + ResultCode result = process.vm_manager.ReprotectRange(addr0, size, vma_permissions); + if (result.IsError()) return result; break; + } - // Unknown ControlMemory operation default: LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation); + return ERR_INVALID_COMBINATION; } + + process.vm_manager.LogLayout(Log::Level::Trace); + return RESULT_SUCCESS; } @@ -537,9 +619,9 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_inf if (process == nullptr) return ERR_INVALID_HANDLE; - auto vma = process->address_space->FindVMA(addr); + auto vma = process->vm_manager.FindVMA(addr); - if (vma == process->address_space->vma_map.end()) + if (vma == Kernel::g_current_process->vm_manager.vma_map.end()) return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); memory_info->base_address = vma->second.base; @@ -692,6 +774,52 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 return RESULT_SUCCESS; } +static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) { + LOG_TRACE(Kernel_SVC, "called process=0x%08X type=%u", process_handle, type); + + using Kernel::Process; + Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle); + if (process == nullptr) + return ERR_INVALID_HANDLE; + + switch (type) { + case 0: + case 2: + // TODO(yuriks): Type 0 returns a slightly higher number than type 2, but I'm not sure + // what's the difference between them. + *out = process->heap_used + process->linear_heap_used + process->misc_memory_used; + break; + case 1: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + // These are valid, but not implemented yet + LOG_ERROR(Kernel_SVC, "unimplemented GetProcessInfo type=%u", type); + break; + case 20: + *out = Memory::FCRAM_PADDR - process->GetLinearHeapBase(); + break; + default: + LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type=%u", type); + + if (type >= 21 && type <= 23) { + return ResultCode( // 0xE0E01BF4 + ErrorDescription::NotImplemented, ErrorModule::OS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); + } else { + return ResultCode( // 0xD8E007ED + ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + } + break; + } + + return RESULT_SUCCESS; +} + namespace { struct FunctionDef { using Func = void(); @@ -746,7 +874,7 @@ static const FunctionDef SVC_Table[] = { {0x28, HLE::Wrap<GetSystemTick>, "GetSystemTick"}, {0x29, nullptr, "GetHandleInfo"}, {0x2A, nullptr, "GetSystemInfo"}, - {0x2B, nullptr, "GetProcessInfo"}, + {0x2B, HLE::Wrap<GetProcessInfo>, "GetProcessInfo"}, {0x2C, nullptr, "GetThreadInfo"}, {0x2D, HLE::Wrap<ConnectToPort>, "ConnectToPort"}, {0x2E, nullptr, "SendSyncRequest1"}, |