Merge pull request #1025 from yuriks/heap-management
Kernel: Correct(er) handling of Heap and Linear Heap allocations
This commit is contained in:
		| @@ -91,17 +91,16 @@ void LogMessage(Class log_class, Level log_level, | ||||
| } // namespace Log | ||||
|  | ||||
| #define LOG_GENERIC(log_class, log_level, ...) \ | ||||
|     ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \ | ||||
|         __FILE__, __LINE__, __func__, __VA_ARGS__) | ||||
|     ::Log::LogMessage(log_class, log_level, __FILE__, __LINE__, __func__, __VA_ARGS__) | ||||
|  | ||||
| #ifdef _DEBUG | ||||
| #define LOG_TRACE(   log_class, ...) LOG_GENERIC(log_class, Trace,    __VA_ARGS__) | ||||
| #define LOG_TRACE(   log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Trace,    __VA_ARGS__) | ||||
| #else | ||||
| #define LOG_TRACE(   log_class, ...) (void(0)) | ||||
| #endif | ||||
|  | ||||
| #define LOG_DEBUG(   log_class, ...) LOG_GENERIC(log_class, Debug,    __VA_ARGS__) | ||||
| #define LOG_INFO(    log_class, ...) LOG_GENERIC(log_class, Info,     __VA_ARGS__) | ||||
| #define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning,  __VA_ARGS__) | ||||
| #define LOG_ERROR(   log_class, ...) LOG_GENERIC(log_class, Error,    __VA_ARGS__) | ||||
| #define LOG_CRITICAL(log_class, ...) LOG_GENERIC(log_class, Critical, __VA_ARGS__) | ||||
| #define LOG_DEBUG(   log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Debug,    __VA_ARGS__) | ||||
| #define LOG_INFO(    log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Info,     __VA_ARGS__) | ||||
| #define LOG_WARNING( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Warning,  __VA_ARGS__) | ||||
| #define LOG_ERROR(   log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error,    __VA_ARGS__) | ||||
| #define LOG_CRITICAL(log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__) | ||||
|   | ||||
| @@ -29,6 +29,7 @@ set(SRCS | ||||
|             hle/kernel/address_arbiter.cpp | ||||
|             hle/kernel/event.cpp | ||||
|             hle/kernel/kernel.cpp | ||||
|             hle/kernel/memory.cpp | ||||
|             hle/kernel/mutex.cpp | ||||
|             hle/kernel/process.cpp | ||||
|             hle/kernel/resource_limit.cpp | ||||
| @@ -115,7 +116,6 @@ set(SRCS | ||||
|             loader/loader.cpp | ||||
|             loader/ncch.cpp | ||||
|             tracer/recorder.cpp | ||||
|             mem_map.cpp | ||||
|             memory.cpp | ||||
|             settings.cpp | ||||
|             system.cpp | ||||
| @@ -157,6 +157,7 @@ set(HEADERS | ||||
|             hle/kernel/address_arbiter.h | ||||
|             hle/kernel/event.h | ||||
|             hle/kernel/kernel.h | ||||
|             hle/kernel/memory.h | ||||
|             hle/kernel/mutex.h | ||||
|             hle/kernel/process.h | ||||
|             hle/kernel/resource_limit.h | ||||
| @@ -245,7 +246,6 @@ set(HEADERS | ||||
|             loader/ncch.h | ||||
|             tracer/recorder.h | ||||
|             tracer/citrace.h | ||||
|             mem_map.h | ||||
|             memory.h | ||||
|             memory_setup.h | ||||
|             settings.h | ||||
|   | ||||
| @@ -4,7 +4,6 @@ | ||||
|  | ||||
| #include "common/swap.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/mem_map.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/arm/skyeye_common/armstate.h" | ||||
| #include "core/arm/skyeye_common/vfp/vfp.h" | ||||
|   | ||||
| @@ -17,7 +17,6 @@ | ||||
|  | ||||
| #include "common/logging/log.h" | ||||
|  | ||||
| #include "core/mem_map.h" | ||||
| #include "core/arm/skyeye_common/arm_regformat.h" | ||||
| #include "core/arm/skyeye_common/armstate.h" | ||||
| #include "core/arm/skyeye_common/armsupp.h" | ||||
|   | ||||
| @@ -25,10 +25,6 @@ void Init() { | ||||
|     config_mem.sys_core_ver = 0x2; | ||||
|     config_mem.unit_info = 0x1; // Bit 0 set for Retail | ||||
|     config_mem.prev_firm = 0; | ||||
|     config_mem.app_mem_type = 0x2; // Default app mem type is 0 | ||||
|     config_mem.app_mem_alloc = 0x06000000; // Set to 96MB, since some games use more than the default (64MB) | ||||
|     config_mem.base_mem_alloc = 0x01400000; // Default base memory is 20MB | ||||
|     config_mem.sys_mem_alloc = Memory::FCRAM_SIZE - (config_mem.app_mem_alloc + config_mem.base_mem_alloc); | ||||
|     config_mem.firm_unk = 0; | ||||
|     config_mem.firm_version_rev = 0; | ||||
|     config_mem.firm_version_min = 0x40; | ||||
| @@ -36,7 +32,4 @@ void Init() { | ||||
|     config_mem.firm_sys_core_ver = 0x2; | ||||
| } | ||||
|  | ||||
| void Shutdown() { | ||||
| } | ||||
|  | ||||
| } // namespace | ||||
|   | ||||
| @@ -52,6 +52,5 @@ static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE, "Config Memory | ||||
| extern ConfigMemDef config_mem; | ||||
|  | ||||
| void Init(); | ||||
| void Shutdown(); | ||||
|  | ||||
| } // namespace | ||||
|   | ||||
| @@ -172,6 +172,14 @@ template<ResultCode func(u32, s64, s64)> void Wrap() { | ||||
|     FuncReturn(func(PARAM(0), param1, param2).raw); | ||||
| } | ||||
|  | ||||
| template<ResultCode func(s64*, Handle, u32)> void Wrap() { | ||||
|     s64 param_1 = 0; | ||||
|     u32 retval = func(¶m_1, PARAM(1), PARAM(2)).raw; | ||||
|     Core::g_app_core->SetReg(1, (u32)param_1); | ||||
|     Core::g_app_core->SetReg(2, (u32)(param_1 >> 32)); | ||||
|     FuncReturn(retval); | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| // Function wrappers that return type u32 | ||||
|  | ||||
|   | ||||
| @@ -34,8 +34,6 @@ void Reschedule(const char *reason) { | ||||
|  | ||||
| void Init() { | ||||
|     Service::Init(); | ||||
|     ConfigMem::Init(); | ||||
|     SharedPage::Init(); | ||||
|  | ||||
|     g_reschedule = false; | ||||
|  | ||||
| @@ -43,8 +41,6 @@ void Init() { | ||||
| } | ||||
|  | ||||
| void Shutdown() { | ||||
|     ConfigMem::Shutdown(); | ||||
|     SharedPage::Shutdown(); | ||||
|     Service::Shutdown(); | ||||
|  | ||||
|     LOG_DEBUG(Kernel, "shutdown OK"); | ||||
|   | ||||
| @@ -7,11 +7,14 @@ | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
|  | ||||
| #include "core/hle/config_mem.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/kernel/timer.h" | ||||
| #include "core/hle/shared_page.h" | ||||
|  | ||||
| namespace Kernel { | ||||
|  | ||||
| @@ -119,6 +122,13 @@ void HandleTable::Clear() { | ||||
|  | ||||
| /// Initialize the kernel | ||||
| void Init() { | ||||
|     ConfigMem::Init(); | ||||
|     SharedPage::Init(); | ||||
|  | ||||
|     // TODO(yuriks): The memory type parameter needs to be determined by the ExHeader field instead | ||||
|     // For now it defaults to the one with a largest allocation to the app | ||||
|     Kernel::MemoryInit(2); // Allocates 96MB to the application | ||||
|  | ||||
|     Kernel::ResourceLimitsInit(); | ||||
|     Kernel::ThreadingInit(); | ||||
|     Kernel::TimersInit(); | ||||
| @@ -131,11 +141,14 @@ void Init() { | ||||
|  | ||||
| /// Shutdown the kernel | ||||
| void Shutdown() { | ||||
|     g_handle_table.Clear(); // Free all kernel objects | ||||
|  | ||||
|     Kernel::ThreadingShutdown(); | ||||
|     g_current_process = nullptr; | ||||
|  | ||||
|     Kernel::TimersShutdown(); | ||||
|     Kernel::ResourceLimitsShutdown(); | ||||
|     g_handle_table.Clear(); // Free all kernel objects | ||||
|     g_current_process = nullptr; | ||||
|     Kernel::MemoryShutdown(); | ||||
| } | ||||
|  | ||||
| } // namespace | ||||
|   | ||||
							
								
								
									
										136
									
								
								src/core/hle/kernel/memory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/core/hle/kernel/memory.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| // Copyright 2014 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
|  | ||||
| #include "core/hle/config_mem.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/shared_page.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/memory_setup.h" | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| namespace Kernel { | ||||
|  | ||||
| static MemoryRegionInfo memory_regions[3]; | ||||
|  | ||||
| /// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each sytem | ||||
| /// memory configuration type. | ||||
| static const u32 memory_region_sizes[8][3] = { | ||||
|     // Old 3DS layouts | ||||
|     {0x04000000, 0x02C00000, 0x01400000}, // 0 | ||||
|     { /* This appears to be unused. */ }, // 1 | ||||
|     {0x06000000, 0x00C00000, 0x01400000}, // 2 | ||||
|     {0x05000000, 0x01C00000, 0x01400000}, // 3 | ||||
|     {0x04800000, 0x02400000, 0x01400000}, // 4 | ||||
|     {0x02000000, 0x04C00000, 0x01400000}, // 5 | ||||
|  | ||||
|     // New 3DS layouts | ||||
|     {0x07C00000, 0x06400000, 0x02000000}, // 6 | ||||
|     {0x0B200000, 0x02E00000, 0x02000000}, // 7 | ||||
| }; | ||||
|  | ||||
| void MemoryInit(u32 mem_type) { | ||||
|     // TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead. | ||||
|     ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!"); | ||||
|     ASSERT(mem_type != 1); | ||||
|  | ||||
|     // The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with | ||||
|     // the sizes specified in the memory_region_sizes table. | ||||
|     VAddr base = 0; | ||||
|     for (int i = 0; i < 3; ++i) { | ||||
|         memory_regions[i].base = base; | ||||
|         memory_regions[i].size = memory_region_sizes[mem_type][i]; | ||||
|         memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>(); | ||||
|  | ||||
|         base += memory_regions[i].size; | ||||
|     } | ||||
|  | ||||
|     // We must've allocated the entire FCRAM by the end | ||||
|     ASSERT(base == Memory::FCRAM_SIZE); | ||||
|  | ||||
|     using ConfigMem::config_mem; | ||||
|     config_mem.app_mem_type = mem_type; | ||||
|     // app_mem_malloc does not always match the configured size for memory_region[0]: in case the | ||||
|     // n3DS type override is in effect it reports the size the game expects, not the real one. | ||||
|     config_mem.app_mem_alloc = memory_region_sizes[mem_type][0]; | ||||
|     config_mem.sys_mem_alloc = memory_regions[1].size; | ||||
|     config_mem.base_mem_alloc = memory_regions[2].size; | ||||
| } | ||||
|  | ||||
| void MemoryShutdown() { | ||||
|     for (auto& region : memory_regions) { | ||||
|         region.base = 0; | ||||
|         region.size = 0; | ||||
|         region.linear_heap_memory = nullptr; | ||||
|     } | ||||
| } | ||||
|  | ||||
| MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) { | ||||
|     switch (region) { | ||||
|     case MemoryRegion::APPLICATION: | ||||
|         return &memory_regions[0]; | ||||
|     case MemoryRegion::SYSTEM: | ||||
|         return &memory_regions[1]; | ||||
|     case MemoryRegion::BASE: | ||||
|         return &memory_regions[2]; | ||||
|     default: | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
| namespace Memory { | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| struct MemoryArea { | ||||
|     u32 base; | ||||
|     u32 size; | ||||
|     const char* name; | ||||
| }; | ||||
|  | ||||
| // We don't declare the IO regions in here since its handled by other means. | ||||
| static MemoryArea memory_areas[] = { | ||||
|     {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE,     "Shared Memory"}, // Shared memory | ||||
|     {VRAM_VADDR,          VRAM_SIZE,              "VRAM"},          // Video memory (VRAM) | ||||
|     {DSP_RAM_VADDR,       DSP_RAM_SIZE,           "DSP RAM"},       // DSP memory | ||||
|     {TLS_AREA_VADDR,      TLS_AREA_SIZE,          "TLS Area"},      // TLS memory | ||||
| }; | ||||
|  | ||||
| } | ||||
|  | ||||
| void Init() { | ||||
|     InitMemoryMap(); | ||||
|     LOG_DEBUG(HW_Memory, "initialized OK"); | ||||
| } | ||||
|  | ||||
| void InitLegacyAddressSpace(Kernel::VMManager& address_space) { | ||||
|     using namespace Kernel; | ||||
|  | ||||
|     for (MemoryArea& area : memory_areas) { | ||||
|         auto block = std::make_shared<std::vector<u8>>(area.size); | ||||
|         address_space.MapMemoryBlock(area.base, std::move(block), 0, area.size, MemoryState::Private).Unwrap(); | ||||
|     } | ||||
|  | ||||
|     auto cfg_mem_vma = address_space.MapBackingMemory(CONFIG_MEMORY_VADDR, | ||||
|             (u8*)&ConfigMem::config_mem, CONFIG_MEMORY_SIZE, MemoryState::Shared).MoveFrom(); | ||||
|     address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); | ||||
|  | ||||
|     auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR, | ||||
|             (u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom(); | ||||
|     address_space.Reprotect(shared_page_vma, VMAPermission::Read); | ||||
| } | ||||
|  | ||||
| } // namespace | ||||
							
								
								
									
										35
									
								
								src/core/hle/kernel/memory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/core/hle/kernel/memory.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| // Copyright 2014 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <memory> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| #include "core/hle/kernel/process.h" | ||||
|  | ||||
| namespace Kernel { | ||||
|  | ||||
| class VMManager; | ||||
|  | ||||
| struct MemoryRegionInfo { | ||||
|     u32 base; // Not an address, but offset from start of FCRAM | ||||
|     u32 size; | ||||
|  | ||||
|     std::shared_ptr<std::vector<u8>> linear_heap_memory; | ||||
| }; | ||||
|  | ||||
| void MemoryInit(u32 mem_type); | ||||
| void MemoryShutdown(); | ||||
| MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); | ||||
|  | ||||
| } | ||||
|  | ||||
| namespace Memory { | ||||
|  | ||||
| void Init(); | ||||
| void InitLegacyAddressSpace(Kernel::VMManager& address_space); | ||||
|  | ||||
| } // namespace | ||||
| @@ -7,11 +7,11 @@ | ||||
| #include "common/logging/log.h" | ||||
| #include "common/make_unique.h" | ||||
|  | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/mem_map.h" | ||||
| #include "core/memory.h" | ||||
|  | ||||
| namespace Kernel { | ||||
| @@ -36,8 +36,7 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { | ||||
|     process->codeset = std::move(code_set); | ||||
|     process->flags.raw = 0; | ||||
|     process->flags.memory_region = MemoryRegion::APPLICATION; | ||||
|     process->address_space = Common::make_unique<VMManager>(); | ||||
|     Memory::InitLegacyAddressSpace(*process->address_space); | ||||
|     Memory::InitLegacyAddressSpace(process->vm_manager); | ||||
|  | ||||
|     return process; | ||||
| } | ||||
| @@ -93,9 +92,11 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | ||||
|             mapping.unk_flag = false; | ||||
|         } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF | ||||
|             // Kernel version | ||||
|             int minor = descriptor & 0xFF; | ||||
|             int major = (descriptor >> 8) & 0xFF; | ||||
|             LOG_INFO(Loader, "ExHeader kernel version ignored: %d.%d", major, minor); | ||||
|             kernel_version = descriptor & 0xFFFF; | ||||
|  | ||||
|             int minor = kernel_version & 0xFF; | ||||
|             int major = (kernel_version >> 8) & 0xFF; | ||||
|             LOG_INFO(Loader, "ExHeader kernel version: %d.%d", major, minor); | ||||
|         } else { | ||||
|             LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x%08X", descriptor); | ||||
|         } | ||||
| @@ -103,20 +104,153 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | ||||
| } | ||||
|  | ||||
| void Process::Run(s32 main_thread_priority, u32 stack_size) { | ||||
|     memory_region = GetMemoryRegion(flags.memory_region); | ||||
|  | ||||
|     auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) { | ||||
|         auto vma = address_space->MapMemoryBlock(segment.addr, codeset->memory, | ||||
|         auto vma = vm_manager.MapMemoryBlock(segment.addr, codeset->memory, | ||||
|                 segment.offset, segment.size, memory_state).Unwrap(); | ||||
|         address_space->Reprotect(vma, permissions); | ||||
|         vm_manager.Reprotect(vma, permissions); | ||||
|         misc_memory_used += segment.size; | ||||
|     }; | ||||
|  | ||||
|     // Map CodeSet segments | ||||
|     MapSegment(codeset->code,   VMAPermission::ReadExecute, MemoryState::Code); | ||||
|     MapSegment(codeset->rodata, VMAPermission::Read,        MemoryState::Code); | ||||
|     MapSegment(codeset->data,   VMAPermission::ReadWrite,   MemoryState::Private); | ||||
|  | ||||
|     address_space->LogLayout(); | ||||
|     // Allocate and map stack | ||||
|     vm_manager.MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size, | ||||
|             std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, MemoryState::Locked | ||||
|             ).Unwrap(); | ||||
|     misc_memory_used += stack_size; | ||||
|  | ||||
|     vm_manager.LogLayout(Log::Level::Debug); | ||||
|     Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); | ||||
| } | ||||
|  | ||||
| VAddr Process::GetLinearHeapBase() const { | ||||
|     return (kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_SIZE) | ||||
|             + memory_region->base; | ||||
| } | ||||
|  | ||||
| VAddr Process::GetLinearHeapLimit() const { | ||||
|     return GetLinearHeapBase() + memory_region->size; | ||||
| } | ||||
|  | ||||
| ResultVal<VAddr> Process::HeapAllocate(VAddr target, u32 size, VMAPermission perms) { | ||||
|     if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || target + size < target) { | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
|  | ||||
|     if (heap_memory == nullptr) { | ||||
|         // Initialize heap | ||||
|         heap_memory = std::make_shared<std::vector<u8>>(); | ||||
|         heap_start = heap_end = target; | ||||
|     } | ||||
|  | ||||
|     // If necessary, expand backing vector to cover new heap extents. | ||||
|     if (target < heap_start) { | ||||
|         heap_memory->insert(begin(*heap_memory), heap_start - target, 0); | ||||
|         heap_start = target; | ||||
|         vm_manager.RefreshMemoryBlockMappings(heap_memory.get()); | ||||
|     } | ||||
|     if (target + size > heap_end) { | ||||
|         heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0); | ||||
|         heap_end = target + size; | ||||
|         vm_manager.RefreshMemoryBlockMappings(heap_memory.get()); | ||||
|     } | ||||
|     ASSERT(heap_end - heap_start == heap_memory->size()); | ||||
|  | ||||
|     CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start, size, MemoryState::Private)); | ||||
|     vm_manager.Reprotect(vma, perms); | ||||
|  | ||||
|     heap_used += size; | ||||
|  | ||||
|     return MakeResult<VAddr>(heap_end - size); | ||||
| } | ||||
|  | ||||
| ResultCode Process::HeapFree(VAddr target, u32 size) { | ||||
|     if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || target + size < target) { | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
|  | ||||
|     ResultCode result = vm_manager.UnmapRange(target, size); | ||||
|     if (result.IsError()) return result; | ||||
|  | ||||
|     heap_used -= size; | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) { | ||||
|     auto& linheap_memory = memory_region->linear_heap_memory; | ||||
|  | ||||
|     VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size(); | ||||
|     // Games and homebrew only ever seem to pass 0 here (which lets the kernel decide the address), | ||||
|     // but explicit addresses are also accepted and respected. | ||||
|     if (target == 0) { | ||||
|         target = heap_end; | ||||
|     } | ||||
|  | ||||
|     if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() || | ||||
|         target > heap_end || target + size < target) { | ||||
|  | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
|  | ||||
|     // Expansion of the linear heap is only allowed if you do an allocation immediatelly at its | ||||
|     // end. It's possible to free gaps in the middle of the heap and then reallocate them later, | ||||
|     // but expansions are only allowed at the end. | ||||
|     if (target == heap_end) { | ||||
|         linheap_memory->insert(linheap_memory->end(), size, 0); | ||||
|         vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); | ||||
|     } | ||||
|  | ||||
|     // TODO(yuriks): As is, this lets processes map memory allocated by other processes from the | ||||
|     // same region. It is unknown if or how the 3DS kernel checks against this. | ||||
|     size_t offset = target - GetLinearHeapBase(); | ||||
|     CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, linheap_memory, offset, size, MemoryState::Continuous)); | ||||
|     vm_manager.Reprotect(vma, perms); | ||||
|  | ||||
|     linear_heap_used += size; | ||||
|  | ||||
|     return MakeResult<VAddr>(target); | ||||
| } | ||||
|  | ||||
| ResultCode Process::LinearFree(VAddr target, u32 size) { | ||||
|     auto& linheap_memory = memory_region->linear_heap_memory; | ||||
|  | ||||
|     if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() || | ||||
|         target + size < target) { | ||||
|  | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
|  | ||||
|     VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size(); | ||||
|     if (target + size > heap_end) { | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
|  | ||||
|     ResultCode result = vm_manager.UnmapRange(target, size); | ||||
|     if (result.IsError()) return result; | ||||
|  | ||||
|     linear_heap_used -= size; | ||||
|  | ||||
|     if (target + size == heap_end) { | ||||
|         // End of linear heap has been freed, so check what's the last allocated block in it and | ||||
|         // reduce the size. | ||||
|         auto vma = vm_manager.FindVMA(target); | ||||
|         ASSERT(vma != vm_manager.vma_map.end()); | ||||
|         ASSERT(vma->second.type == VMAType::Free); | ||||
|         VAddr new_end = vma->second.base; | ||||
|         if (new_end >= GetLinearHeapBase()) { | ||||
|             linheap_memory->resize(new_end - GetLinearHeapBase()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| Kernel::Process::Process() {} | ||||
| Kernel::Process::~Process() {} | ||||
|  | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
|  | ||||
| namespace Kernel { | ||||
|  | ||||
| @@ -48,7 +49,7 @@ union ProcessFlags { | ||||
| }; | ||||
|  | ||||
| class ResourceLimit; | ||||
| class VMManager; | ||||
| struct MemoryRegionInfo; | ||||
|  | ||||
| struct CodeSet final : public Object { | ||||
|     static SharedPtr<CodeSet> Create(std::string name, u64 program_id); | ||||
| @@ -104,14 +105,12 @@ public: | ||||
|     /// processes access to specific I/O regions and device memory. | ||||
|     boost::container::static_vector<AddressMapping, 8> address_mappings; | ||||
|     ProcessFlags flags; | ||||
|     /// Kernel compatibility version for this process | ||||
|     u16 kernel_version = 0; | ||||
|  | ||||
|     /// The id of this process | ||||
|     u32 process_id = next_process_id++; | ||||
|  | ||||
|     /// Bitmask of the used TLS slots | ||||
|     std::bitset<300> used_tls_slots; | ||||
|     std::unique_ptr<VMManager> address_space; | ||||
|  | ||||
|     /** | ||||
|      * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them | ||||
|      * to this process. | ||||
| @@ -123,6 +122,36 @@ public: | ||||
|      */ | ||||
|     void Run(s32 main_thread_priority, u32 stack_size); | ||||
|  | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|     // Memory Management | ||||
|  | ||||
|     VMManager vm_manager; | ||||
|  | ||||
|     // Memory used to back the allocations in the regular heap. A single vector is used to cover | ||||
|     // the entire virtual address space extents that bound the allocations, including any holes. | ||||
|     // This makes deallocation and reallocation of holes fast and keeps process memory contiguous | ||||
|     // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. | ||||
|     std::shared_ptr<std::vector<u8>> heap_memory; | ||||
|     // The left/right bounds of the address space covered by heap_memory. | ||||
|     VAddr heap_start = 0, heap_end = 0; | ||||
|  | ||||
|     u32 heap_used = 0, linear_heap_used = 0, misc_memory_used = 0; | ||||
|  | ||||
|     MemoryRegionInfo* memory_region = nullptr; | ||||
|  | ||||
|     /// Bitmask of the used TLS slots | ||||
|     std::bitset<300> used_tls_slots; | ||||
|  | ||||
|     VAddr GetLinearHeapBase() const; | ||||
|     VAddr GetLinearHeapLimit() const; | ||||
|  | ||||
|     ResultVal<VAddr> HeapAllocate(VAddr target, u32 size, VMAPermission perms); | ||||
|     ResultCode HeapFree(VAddr target, u32 size); | ||||
|  | ||||
|     ResultVal<VAddr> LinearAllocate(VAddr target, u32 size, VMAPermission perms); | ||||
|     ResultCode LinearFree(VAddr target, u32 size); | ||||
|  | ||||
| private: | ||||
|     Process(); | ||||
|     ~Process() override; | ||||
|   | ||||
| @@ -6,7 +6,6 @@ | ||||
|  | ||||
| #include "common/logging/log.h" | ||||
|  | ||||
| #include "core/mem_map.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
|  | ||||
| namespace Kernel { | ||||
|   | ||||
| @@ -117,6 +117,7 @@ void Thread::Stop() { | ||||
|     wait_objects.clear(); | ||||
|  | ||||
|     Kernel::g_current_process->used_tls_slots[tls_index] = false; | ||||
|     g_current_process->misc_memory_used -= Memory::TLS_ENTRY_SIZE; | ||||
|  | ||||
|     HLE::Reschedule(__func__); | ||||
| } | ||||
| @@ -414,6 +415,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | ||||
|     } | ||||
|  | ||||
|     ASSERT_MSG(thread->tls_index != -1, "Out of TLS space"); | ||||
|     g_current_process->misc_memory_used += Memory::TLS_ENTRY_SIZE; | ||||
|  | ||||
|     // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used | ||||
|     // to initialize the context | ||||
| @@ -504,7 +506,7 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { | ||||
| } | ||||
|  | ||||
| VAddr Thread::GetTLSAddress() const { | ||||
|     return Memory::TLS_AREA_VADDR + tls_index * 0x200; | ||||
|     return Memory::TLS_AREA_VADDR + tls_index * Memory::TLS_ENTRY_SIZE; | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|   | ||||
| @@ -11,6 +11,15 @@ | ||||
|  | ||||
| namespace Kernel { | ||||
|  | ||||
| static const char* GetMemoryStateName(MemoryState state) { | ||||
|     static const char* names[] = { | ||||
|         "Free", "Reserved", "IO", "Static", "Code", "Private", "Shared", "Continuous", "Aliased", | ||||
|         "Alias", "AliasCode", "Locked", | ||||
|     }; | ||||
|  | ||||
|     return names[(int)state]; | ||||
| } | ||||
|  | ||||
| bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { | ||||
|     ASSERT(base + size == next.base); | ||||
|     if (permissions != next.permissions || | ||||
| @@ -51,11 +60,15 @@ void VMManager::Reset() { | ||||
| } | ||||
|  | ||||
| VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { | ||||
|     return std::prev(vma_map.upper_bound(target)); | ||||
|     if (target >= MAX_ADDRESS) { | ||||
|         return vma_map.end(); | ||||
|     } else { | ||||
|         return std::prev(vma_map.upper_bound(target)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, | ||||
|         std::shared_ptr<std::vector<u8>> block, u32 offset, u32 size, MemoryState state) { | ||||
|         std::shared_ptr<std::vector<u8>> block, size_t offset, u32 size, MemoryState state) { | ||||
|     ASSERT(block != nullptr); | ||||
|     ASSERT(offset + size <= block->size()); | ||||
|  | ||||
| @@ -106,10 +119,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u3 | ||||
|     return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | ||||
| } | ||||
|  | ||||
| void VMManager::Unmap(VMAHandle vma_handle) { | ||||
|     VMAIter iter = StripIterConstness(vma_handle); | ||||
|  | ||||
|     VirtualMemoryArea& vma = iter->second; | ||||
| VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { | ||||
|     VirtualMemoryArea& vma = vma_handle->second; | ||||
|     vma.type = VMAType::Free; | ||||
|     vma.permissions = VMAPermission::None; | ||||
|     vma.meminfo_state = MemoryState::Free; | ||||
| @@ -121,26 +132,67 @@ void VMManager::Unmap(VMAHandle vma_handle) { | ||||
|  | ||||
|     UpdatePageTableForVMA(vma); | ||||
|  | ||||
|     MergeAdjacent(iter); | ||||
|     return MergeAdjacent(vma_handle); | ||||
| } | ||||
|  | ||||
| void VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) { | ||||
| ResultCode VMManager::UnmapRange(VAddr target, u32 size) { | ||||
|     CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); | ||||
|     VAddr target_end = target + size; | ||||
|  | ||||
|     VMAIter end = vma_map.end(); | ||||
|     // The comparison against the end of the range must be done using addresses since VMAs can be | ||||
|     // merged during this process, causing invalidation of the iterators. | ||||
|     while (vma != end && vma->second.base < target_end) { | ||||
|         vma = std::next(Unmap(vma)); | ||||
|     } | ||||
|  | ||||
|     ASSERT(FindVMA(target)->second.size >= size); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) { | ||||
|     VMAIter iter = StripIterConstness(vma_handle); | ||||
|  | ||||
|     VirtualMemoryArea& vma = iter->second; | ||||
|     vma.permissions = new_perms; | ||||
|     UpdatePageTableForVMA(vma); | ||||
|  | ||||
|     MergeAdjacent(iter); | ||||
|     return MergeAdjacent(iter); | ||||
| } | ||||
|  | ||||
| void VMManager::LogLayout() const { | ||||
| ResultCode VMManager::ReprotectRange(VAddr target, u32 size, VMAPermission new_perms) { | ||||
|     CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); | ||||
|     VAddr target_end = target + size; | ||||
|  | ||||
|     VMAIter end = vma_map.end(); | ||||
|     // The comparison against the end of the range must be done using addresses since VMAs can be | ||||
|     // merged during this process, causing invalidation of the iterators. | ||||
|     while (vma != end && vma->second.base < target_end) { | ||||
|         vma = std::next(StripIterConstness(Reprotect(vma, new_perms))); | ||||
|     } | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { | ||||
|     // If this ever proves to have a noticeable performance impact, allow users of the function to | ||||
|     // specify a specific range of addresses to limit the scan to. | ||||
|     for (const auto& p : vma_map) { | ||||
|         const VirtualMemoryArea& vma = p.second; | ||||
|         LOG_DEBUG(Kernel, "%08X - %08X  size: %8X %c%c%c", vma.base, vma.base + vma.size, vma.size, | ||||
|         if (block == vma.backing_block.get()) { | ||||
|             UpdatePageTableForVMA(vma); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void VMManager::LogLayout(Log::Level log_level) const { | ||||
|     for (const auto& p : vma_map) { | ||||
|         const VirtualMemoryArea& vma = p.second; | ||||
|         LOG_GENERIC(Log::Class::Kernel, log_level, "%08X - %08X  size: %8X %c%c%c %s", | ||||
|             vma.base, vma.base + vma.size, vma.size, | ||||
|             (u8)vma.permissions & (u8)VMAPermission::Read    ? 'R' : '-', | ||||
|             (u8)vma.permissions & (u8)VMAPermission::Write   ? 'W' : '-', | ||||
|             (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-'); | ||||
|             (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-', GetMemoryStateName(vma.meminfo_state)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -151,21 +203,19 @@ VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle & iter) { | ||||
| } | ||||
|  | ||||
| ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { | ||||
|     ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: %8X", size); | ||||
|     ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: %08X", base); | ||||
|     ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size); | ||||
|     ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", base); | ||||
|  | ||||
|     VMAIter vma_handle = StripIterConstness(FindVMA(base)); | ||||
|     if (vma_handle == vma_map.end()) { | ||||
|         // Target address is outside the range managed by the kernel | ||||
|         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, | ||||
|                 ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E01BF5 | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
|  | ||||
|     VirtualMemoryArea& vma = vma_handle->second; | ||||
|     if (vma.type != VMAType::Free) { | ||||
|         // Region is already allocated | ||||
|         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, | ||||
|                 ErrorSummary::InvalidState, ErrorLevel::Usage); // 0xE0A01BF5 | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
|  | ||||
|     u32 start_in_vma = base - vma.base; | ||||
| @@ -173,8 +223,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { | ||||
|  | ||||
|     if (end_in_vma > vma.size) { | ||||
|         // Requested allocation doesn't fit inside VMA | ||||
|         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, | ||||
|                 ErrorSummary::InvalidState, ErrorLevel::Usage); // 0xE0A01BF5 | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
|  | ||||
|     if (end_in_vma != vma.size) { | ||||
| @@ -189,6 +238,35 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { | ||||
|     return MakeResult<VMAIter>(vma_handle); | ||||
| } | ||||
|  | ||||
| ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u32 size) { | ||||
|     ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size); | ||||
|     ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", target); | ||||
|  | ||||
|     VAddr target_end = target + size; | ||||
|     ASSERT(target_end >= target); | ||||
|     ASSERT(target_end <= MAX_ADDRESS); | ||||
|     ASSERT(size > 0); | ||||
|  | ||||
|     VMAIter begin_vma = StripIterConstness(FindVMA(target)); | ||||
|     VMAIter i_end = vma_map.lower_bound(target_end); | ||||
|     for (auto i = begin_vma; i != i_end; ++i) { | ||||
|         if (i->second.type == VMAType::Free) { | ||||
|             return ERR_INVALID_ADDRESS_STATE; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (target != begin_vma->second.base) { | ||||
|         begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base); | ||||
|     } | ||||
|  | ||||
|     VMAIter end_vma = StripIterConstness(FindVMA(target_end)); | ||||
|     if (end_vma != vma_map.end() && target_end != end_vma->second.base) { | ||||
|         end_vma = SplitVMA(end_vma, target_end - end_vma->second.base); | ||||
|     } | ||||
|  | ||||
|     return MakeResult<VMAIter>(begin_vma); | ||||
| } | ||||
|  | ||||
| VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) { | ||||
|     VirtualMemoryArea& old_vma = vma_handle->second; | ||||
|     VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA | ||||
|   | ||||
| @@ -14,6 +14,14 @@ | ||||
|  | ||||
| namespace Kernel { | ||||
|  | ||||
| const ResultCode ERR_INVALID_ADDRESS{ // 0xE0E01BF5 | ||||
|         ErrorDescription::InvalidAddress, ErrorModule::OS, | ||||
|         ErrorSummary::InvalidArgument, ErrorLevel::Usage}; | ||||
|  | ||||
| const ResultCode ERR_INVALID_ADDRESS_STATE{ // 0xE0A01BF5 | ||||
|         ErrorDescription::InvalidAddress, ErrorModule::OS, | ||||
|         ErrorSummary::InvalidState, ErrorLevel::Usage}; | ||||
|  | ||||
| enum class VMAType : u8 { | ||||
|     /// VMA represents an unmapped region of the address space. | ||||
|     Free, | ||||
| @@ -75,7 +83,7 @@ struct VirtualMemoryArea { | ||||
|     /// Memory block backing this VMA. | ||||
|     std::shared_ptr<std::vector<u8>> backing_block = nullptr; | ||||
|     /// Offset into the backing_memory the mapping starts from. | ||||
|     u32 offset = 0; | ||||
|     size_t offset = 0; | ||||
|  | ||||
|     // Settings for type = BackingMemory | ||||
|     /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. | ||||
| @@ -141,7 +149,7 @@ public: | ||||
|      * @param state MemoryState tag to attach to the VMA. | ||||
|      */ | ||||
|     ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, | ||||
|             u32 offset, u32 size, MemoryState state); | ||||
|             size_t offset, u32 size, MemoryState state); | ||||
|  | ||||
|     /** | ||||
|      * Maps an unmanaged host memory pointer at a given address. | ||||
| @@ -163,14 +171,23 @@ public: | ||||
|      */ | ||||
|     ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state); | ||||
|  | ||||
|     /// Unmaps the given VMA. | ||||
|     void Unmap(VMAHandle vma); | ||||
|     /// Unmaps a range of addresses, splitting VMAs as necessary. | ||||
|     ResultCode UnmapRange(VAddr target, u32 size); | ||||
|  | ||||
|     /// Changes the permissions of the given VMA. | ||||
|     void Reprotect(VMAHandle vma, VMAPermission new_perms); | ||||
|     VMAHandle Reprotect(VMAHandle vma, VMAPermission new_perms); | ||||
|  | ||||
|     /// Changes the permissions of a range of addresses, splitting VMAs as necessary. | ||||
|     ResultCode ReprotectRange(VAddr target, u32 size, VMAPermission new_perms); | ||||
|  | ||||
|     /** | ||||
|      * Scans all VMAs and updates the page table range of any that use the given vector as backing | ||||
|      * memory. This should be called after any operation that causes reallocation of the vector. | ||||
|      */ | ||||
|     void RefreshMemoryBlockMappings(const std::vector<u8>* block); | ||||
|  | ||||
|     /// Dumps the address space layout to the log, for debugging | ||||
|     void LogLayout() const; | ||||
|     void LogLayout(Log::Level log_level) const; | ||||
|  | ||||
| private: | ||||
|     using VMAIter = decltype(vma_map)::iterator; | ||||
| @@ -178,12 +195,21 @@ private: | ||||
|     /// Converts a VMAHandle to a mutable VMAIter. | ||||
|     VMAIter StripIterConstness(const VMAHandle& iter); | ||||
|  | ||||
|     /// Unmaps the given VMA. | ||||
|     VMAIter Unmap(VMAIter vma); | ||||
|  | ||||
|     /** | ||||
|      * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing | ||||
|      * the appropriate error checking. | ||||
|      */ | ||||
|     ResultVal<VMAIter> CarveVMA(VAddr base, u32 size); | ||||
|  | ||||
|     /** | ||||
|      * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each | ||||
|      * end of the range. | ||||
|      */ | ||||
|     ResultVal<VMAIter> CarveVMARange(VAddr base, u32 size); | ||||
|  | ||||
|     /** | ||||
|      * Splits a VMA in two, at the specified offset. | ||||
|      * @returns the right side of the split, with the original iterator becoming the left side. | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
| #include "core/hle/hle.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
|  | ||||
| @@ -37,7 +38,7 @@ static Kernel::SharedPtr<Kernel::Mutex> lock; | ||||
| static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event | ||||
| static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter event | ||||
|  | ||||
| static std::vector<u8> shared_font; | ||||
| static std::shared_ptr<std::vector<u8>> shared_font; | ||||
|  | ||||
| static u32 cpu_percent; ///< CPU time available to the running application | ||||
|  | ||||
| @@ -74,11 +75,12 @@ void Initialize(Service::Interface* self) { | ||||
| void GetSharedFont(Service::Interface* self) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|  | ||||
|     if (!shared_font.empty()) { | ||||
|         // TODO(bunnei): This function shouldn't copy the shared font every time it's called. | ||||
|         // Instead, it should probably map the shared font as RO memory. We don't currently have | ||||
|         // an easy way to do this, but the copy should be sufficient for now. | ||||
|         memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size()); | ||||
|     if (shared_font != nullptr) { | ||||
|         // TODO(yuriks): This is a hack to keep this working right now even with our completely | ||||
|         // broken shared memory system. | ||||
|         shared_font_mem->base_address = SHARED_FONT_VADDR; | ||||
|         Kernel::g_current_process->vm_manager.MapMemoryBlock(shared_font_mem->base_address, | ||||
|                 shared_font, 0, shared_font_mem->size, Kernel::MemoryState::Shared); | ||||
|  | ||||
|         cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); | ||||
|         cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||||
| @@ -391,7 +393,6 @@ void Init() { | ||||
|     // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file | ||||
|     // "shared_font.bin" in the Citra "sysdata" directory. | ||||
|  | ||||
|     shared_font.clear(); | ||||
|     std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; | ||||
|  | ||||
|     FileUtil::CreateFullPath(filepath); // Create path if not already created | ||||
| @@ -399,8 +400,8 @@ void Init() { | ||||
|  | ||||
|     if (file.IsOpen()) { | ||||
|         // Read shared font data | ||||
|         shared_font.resize((size_t)file.GetSize()); | ||||
|         file.ReadBytes(shared_font.data(), (size_t)file.GetSize()); | ||||
|         shared_font = std::make_shared<std::vector<u8>>((size_t)file.GetSize()); | ||||
|         file.ReadBytes(shared_font->data(), shared_font->size()); | ||||
|  | ||||
|         // Create shared font memory object | ||||
|         using Kernel::MemoryPermission; | ||||
| @@ -424,7 +425,7 @@ void Init() { | ||||
| } | ||||
|  | ||||
| void Shutdown() { | ||||
|     shared_font.clear(); | ||||
|     shared_font = nullptr; | ||||
|     shared_font_mem = nullptr; | ||||
|     lock = nullptr; | ||||
|     notification_event = nullptr; | ||||
|   | ||||
| @@ -4,7 +4,6 @@ | ||||
|  | ||||
| #include "common/bit_field.h" | ||||
|  | ||||
| #include "core/mem_map.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/service/y2r_u.h" | ||||
| #include "core/hw/y2r.h" | ||||
| #include "core/mem_map.h" | ||||
|  | ||||
| #include "video_core/renderer_base.h" | ||||
| #include "video_core/utils.h" | ||||
|   | ||||
| @@ -18,7 +18,4 @@ void Init() { | ||||
|     shared_page.running_hw = 0x1; // product | ||||
| } | ||||
|  | ||||
| void Shutdown() { | ||||
| } | ||||
|  | ||||
| } // namespace | ||||
|   | ||||
| @@ -54,6 +54,5 @@ static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE, "Shared page st | ||||
| extern SharedPageDef shared_page; | ||||
|  | ||||
| void Init(); | ||||
| void Shutdown(); | ||||
|  | ||||
| } // namespace | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
|     // Map normal heap memory | ||||
|     case MEMORY_OPERATION_HEAP: | ||||
|         *out_addr = Memory::MapBlock_Heap(size, operation, permissions); | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     // Map GSP heap memory | ||||
|     case MEMORY_OPERATION_GSP_HEAP: | ||||
|         *out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions); | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     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"}, | ||||
|   | ||||
| @@ -1,163 +0,0 @@ | ||||
| // Copyright 2014 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
|  | ||||
| #include "core/hle/config_mem.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/shared_page.h" | ||||
| #include "core/mem_map.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/memory_setup.h" | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| namespace Memory { | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| struct MemoryArea { | ||||
|     u32 base; | ||||
|     u32 size; | ||||
|     const char* name; | ||||
| }; | ||||
|  | ||||
| // We don't declare the IO regions in here since its handled by other means. | ||||
| static MemoryArea memory_areas[] = { | ||||
|     {HEAP_VADDR,          HEAP_SIZE,              "Heap"},          // Application heap (main memory) | ||||
|     {SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE,     "Shared Memory"}, // Shared memory | ||||
|     {LINEAR_HEAP_VADDR,   LINEAR_HEAP_SIZE,       "Linear Heap"},   // Linear heap (main memory) | ||||
|     {VRAM_VADDR,          VRAM_SIZE,              "VRAM"},          // Video memory (VRAM) | ||||
|     {DSP_RAM_VADDR,       DSP_RAM_SIZE,           "DSP RAM"},       // DSP memory | ||||
|     {TLS_AREA_VADDR,      TLS_AREA_SIZE,          "TLS Area"},      // TLS memory | ||||
| }; | ||||
|  | ||||
| /// Represents a block of memory mapped by ControlMemory/MapMemoryBlock | ||||
| struct MemoryBlock { | ||||
|     MemoryBlock() : handle(0), base_address(0), address(0), size(0), operation(0), permissions(0) { | ||||
|     } | ||||
|     u32 handle; | ||||
|     u32 base_address; | ||||
|     u32 address; | ||||
|     u32 size; | ||||
|     u32 operation; | ||||
|     u32 permissions; | ||||
|  | ||||
|     const u32 GetVirtualAddress() const{ | ||||
|         return base_address + address; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| static std::map<u32, MemoryBlock> heap_map; | ||||
| static std::map<u32, MemoryBlock> heap_linear_map; | ||||
|  | ||||
| } | ||||
|  | ||||
| u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions) { | ||||
|     MemoryBlock block; | ||||
|  | ||||
|     block.base_address  = HEAP_VADDR; | ||||
|     block.size          = size; | ||||
|     block.operation     = operation; | ||||
|     block.permissions   = permissions; | ||||
|  | ||||
|     if (heap_map.size() > 0) { | ||||
|         const MemoryBlock last_block = heap_map.rbegin()->second; | ||||
|         block.address = last_block.address + last_block.size; | ||||
|     } | ||||
|     heap_map[block.GetVirtualAddress()] = block; | ||||
|  | ||||
|     return block.GetVirtualAddress(); | ||||
| } | ||||
|  | ||||
| u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions) { | ||||
|     MemoryBlock block; | ||||
|  | ||||
|     block.base_address  = LINEAR_HEAP_VADDR; | ||||
|     block.size          = size; | ||||
|     block.operation     = operation; | ||||
|     block.permissions   = permissions; | ||||
|  | ||||
|     if (heap_linear_map.size() > 0) { | ||||
|         const MemoryBlock last_block = heap_linear_map.rbegin()->second; | ||||
|         block.address = last_block.address + last_block.size; | ||||
|     } | ||||
|     heap_linear_map[block.GetVirtualAddress()] = block; | ||||
|  | ||||
|     return block.GetVirtualAddress(); | ||||
| } | ||||
|  | ||||
| PAddr VirtualToPhysicalAddress(const VAddr addr) { | ||||
|     if (addr == 0) { | ||||
|         return 0; | ||||
|     } else if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) { | ||||
|         return addr - VRAM_VADDR + VRAM_PADDR; | ||||
|     } else if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) { | ||||
|         return addr - LINEAR_HEAP_VADDR + FCRAM_PADDR; | ||||
|     } else if (addr >= DSP_RAM_VADDR && addr < DSP_RAM_VADDR_END) { | ||||
|         return addr - DSP_RAM_VADDR + DSP_RAM_PADDR; | ||||
|     } else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) { | ||||
|         return addr - IO_AREA_VADDR + IO_AREA_PADDR; | ||||
|     } | ||||
|  | ||||
|     LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08x", addr); | ||||
|     // To help with debugging, set bit on address so that it's obviously invalid. | ||||
|     return addr | 0x80000000; | ||||
| } | ||||
|  | ||||
| VAddr PhysicalToVirtualAddress(const PAddr addr) { | ||||
|     if (addr == 0) { | ||||
|         return 0; | ||||
|     } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { | ||||
|         return addr - VRAM_PADDR + VRAM_VADDR; | ||||
|     } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { | ||||
|         return addr - FCRAM_PADDR + LINEAR_HEAP_VADDR; | ||||
|     } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) { | ||||
|         return addr - DSP_RAM_PADDR + DSP_RAM_VADDR; | ||||
|     } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) { | ||||
|         return addr - IO_AREA_PADDR + IO_AREA_VADDR; | ||||
|     } | ||||
|  | ||||
|     LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08x", addr); | ||||
|     // To help with debugging, set bit on address so that it's obviously invalid. | ||||
|     return addr | 0x80000000; | ||||
| } | ||||
|  | ||||
| void Init() { | ||||
|     InitMemoryMap(); | ||||
|     LOG_DEBUG(HW_Memory, "initialized OK"); | ||||
| } | ||||
|  | ||||
| void InitLegacyAddressSpace(Kernel::VMManager& address_space) { | ||||
|     using namespace Kernel; | ||||
|  | ||||
|     for (MemoryArea& area : memory_areas) { | ||||
|         auto block = std::make_shared<std::vector<u8>>(area.size); | ||||
|         address_space.MapMemoryBlock(area.base, std::move(block), 0, area.size, MemoryState::Private).Unwrap(); | ||||
|     } | ||||
|  | ||||
|     auto cfg_mem_vma = address_space.MapBackingMemory(CONFIG_MEMORY_VADDR, | ||||
|             (u8*)&ConfigMem::config_mem, CONFIG_MEMORY_SIZE, MemoryState::Shared).MoveFrom(); | ||||
|     address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); | ||||
|  | ||||
|     auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR, | ||||
|             (u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom(); | ||||
|     address_space.Reprotect(shared_page_vma, VMAPermission::Read); | ||||
| } | ||||
|  | ||||
| void Shutdown() { | ||||
|     heap_map.clear(); | ||||
|     heap_linear_map.clear(); | ||||
|  | ||||
|     LOG_DEBUG(HW_Memory, "shutdown OK"); | ||||
| } | ||||
|  | ||||
| } // namespace | ||||
| @@ -1,46 +0,0 @@ | ||||
| // Copyright 2014 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace Kernel { | ||||
| class VMManager; | ||||
| } | ||||
|  | ||||
| namespace Memory { | ||||
|  | ||||
| void Init(); | ||||
| void InitLegacyAddressSpace(Kernel::VMManager& address_space); | ||||
| void Shutdown(); | ||||
|  | ||||
| /** | ||||
|  * Maps a block of memory on the heap | ||||
|  * @param size Size of block in bytes | ||||
|  * @param operation Memory map operation type | ||||
|  * @param permissions Memory allocation permissions | ||||
|  */ | ||||
| u32 MapBlock_Heap(u32 size, u32 operation, u32 permissions); | ||||
|  | ||||
| /** | ||||
|  * Maps a block of memory on the GSP heap | ||||
|  * @param size Size of block in bytes | ||||
|  * @param operation Memory map operation type | ||||
|  * @param permissions Control memory permissions | ||||
|  */ | ||||
| u32 MapBlock_HeapLinear(u32 size, u32 operation, u32 permissions); | ||||
|  | ||||
| /** | ||||
|  * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical | ||||
|  * address. This should be used by services to translate addresses for use by the hardware. | ||||
|  */ | ||||
| PAddr VirtualToPhysicalAddress(VAddr addr); | ||||
|  | ||||
| /** | ||||
|  * Undoes a mapping performed by VirtualToPhysicalAddress(). | ||||
|  */ | ||||
| VAddr PhysicalToVirtualAddress(PAddr addr); | ||||
|  | ||||
| } // namespace | ||||
| @@ -9,7 +9,7 @@ | ||||
| #include "common/logging/log.h" | ||||
| #include "common/swap.h" | ||||
|  | ||||
| #include "core/mem_map.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/memory_setup.h" | ||||
|  | ||||
| @@ -198,4 +198,42 @@ void WriteBlock(const VAddr addr, const u8* data, const size_t size) { | ||||
|         Write8(addr + offset, data[offset]); | ||||
| } | ||||
|  | ||||
| PAddr VirtualToPhysicalAddress(const VAddr addr) { | ||||
|     if (addr == 0) { | ||||
|         return 0; | ||||
|     } else if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) { | ||||
|         return addr - VRAM_VADDR + VRAM_PADDR; | ||||
|     } else if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) { | ||||
|         return addr - LINEAR_HEAP_VADDR + FCRAM_PADDR; | ||||
|     } else if (addr >= DSP_RAM_VADDR && addr < DSP_RAM_VADDR_END) { | ||||
|         return addr - DSP_RAM_VADDR + DSP_RAM_PADDR; | ||||
|     } else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) { | ||||
|         return addr - IO_AREA_VADDR + IO_AREA_PADDR; | ||||
|     } else if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) { | ||||
|         return addr - NEW_LINEAR_HEAP_VADDR + FCRAM_PADDR; | ||||
|     } | ||||
|  | ||||
|     LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08X", addr); | ||||
|     // To help with debugging, set bit on address so that it's obviously invalid. | ||||
|     return addr | 0x80000000; | ||||
| } | ||||
|  | ||||
| VAddr PhysicalToVirtualAddress(const PAddr addr) { | ||||
|     if (addr == 0) { | ||||
|         return 0; | ||||
|     } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { | ||||
|         return addr - VRAM_PADDR + VRAM_VADDR; | ||||
|     } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { | ||||
|         return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapBase(); | ||||
|     } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) { | ||||
|         return addr - DSP_RAM_PADDR + DSP_RAM_VADDR; | ||||
|     } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) { | ||||
|         return addr - IO_AREA_PADDR + IO_AREA_VADDR; | ||||
|     } | ||||
|  | ||||
|     LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08X", addr); | ||||
|     // To help with debugging, set bit on address so that it's obviously invalid. | ||||
|     return addr | 0x80000000; | ||||
| } | ||||
|  | ||||
| } // namespace | ||||
|   | ||||
| @@ -15,6 +15,8 @@ namespace Memory { | ||||
|  * be mapped. | ||||
|  */ | ||||
| const u32 PAGE_SIZE = 0x1000; | ||||
| const u32 PAGE_MASK = PAGE_SIZE - 1; | ||||
| const int PAGE_BITS = 12; | ||||
|  | ||||
| /// Physical memory regions as seen from the ARM11 | ||||
| enum : PAddr { | ||||
| @@ -103,8 +105,15 @@ enum : VAddr { | ||||
|     // hardcoded value. | ||||
|     /// Area where TLS (Thread-Local Storage) buffers are allocated. | ||||
|     TLS_AREA_VADDR     = 0x1FF82000, | ||||
|     TLS_AREA_SIZE      = 0x00030000, // Each TLS buffer is 0x200 bytes, allows for 300 threads | ||||
|     TLS_ENTRY_SIZE     = 0x200, | ||||
|     TLS_AREA_SIZE      = 300 * TLS_ENTRY_SIZE, // Allows for up to 300 threads | ||||
|     TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE, | ||||
|  | ||||
|  | ||||
|     /// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS. | ||||
|     NEW_LINEAR_HEAP_VADDR     = 0x30000000, | ||||
|     NEW_LINEAR_HEAP_SIZE      = 0x10000000, | ||||
|     NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, | ||||
| }; | ||||
|  | ||||
| u8 Read8(VAddr addr); | ||||
| @@ -121,6 +130,17 @@ void WriteBlock(VAddr addr, const u8* data, size_t size); | ||||
|  | ||||
| u8* GetPointer(VAddr virtual_address); | ||||
|  | ||||
| /** | ||||
| * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical | ||||
| * address. This should be used by services to translate addresses for use by the hardware. | ||||
| */ | ||||
| PAddr VirtualToPhysicalAddress(VAddr addr); | ||||
|  | ||||
| /** | ||||
| * Undoes a mapping performed by VirtualToPhysicalAddress(). | ||||
| */ | ||||
| VAddr PhysicalToVirtualAddress(PAddr addr); | ||||
|  | ||||
| /** | ||||
|  * Gets a pointer to the memory region beginning at the specified physical address. | ||||
|  * | ||||
|   | ||||
| @@ -10,9 +10,6 @@ | ||||
|  | ||||
| namespace Memory { | ||||
|  | ||||
| const u32 PAGE_MASK = PAGE_SIZE - 1; | ||||
| const int PAGE_BITS = 12; | ||||
|  | ||||
| void InitMemoryMap(); | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -4,11 +4,11 @@ | ||||
|  | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/mem_map.h" | ||||
| #include "core/system.h" | ||||
| #include "core/hw/hw.h" | ||||
| #include "core/hle/hle.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
|  | ||||
| #include "video_core/video_core.h" | ||||
|  | ||||
| @@ -29,7 +29,6 @@ void Shutdown() { | ||||
|     HLE::Shutdown(); | ||||
|     Kernel::Shutdown(); | ||||
|     HW::Shutdown(); | ||||
|     Memory::Shutdown(); | ||||
|     CoreTiming::Shutdown(); | ||||
|     Core::Shutdown(); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user