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 | } // namespace Log | ||||||
|  |  | ||||||
| #define LOG_GENERIC(log_class, log_level, ...) \ | #define LOG_GENERIC(log_class, log_level, ...) \ | ||||||
|     ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \ |     ::Log::LogMessage(log_class, log_level, __FILE__, __LINE__, __func__, __VA_ARGS__) | ||||||
|         __FILE__, __LINE__, __func__, __VA_ARGS__) |  | ||||||
|  |  | ||||||
| #ifdef _DEBUG | #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 | #else | ||||||
| #define LOG_TRACE(   log_class, ...) (void(0)) | #define LOG_TRACE(   log_class, ...) (void(0)) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #define LOG_DEBUG(   log_class, ...) LOG_GENERIC(log_class, Debug,    __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, Info,     __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, Warning,  __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, Error,    __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, Critical, __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/address_arbiter.cpp | ||||||
|             hle/kernel/event.cpp |             hle/kernel/event.cpp | ||||||
|             hle/kernel/kernel.cpp |             hle/kernel/kernel.cpp | ||||||
|  |             hle/kernel/memory.cpp | ||||||
|             hle/kernel/mutex.cpp |             hle/kernel/mutex.cpp | ||||||
|             hle/kernel/process.cpp |             hle/kernel/process.cpp | ||||||
|             hle/kernel/resource_limit.cpp |             hle/kernel/resource_limit.cpp | ||||||
| @@ -115,7 +116,6 @@ set(SRCS | |||||||
|             loader/loader.cpp |             loader/loader.cpp | ||||||
|             loader/ncch.cpp |             loader/ncch.cpp | ||||||
|             tracer/recorder.cpp |             tracer/recorder.cpp | ||||||
|             mem_map.cpp |  | ||||||
|             memory.cpp |             memory.cpp | ||||||
|             settings.cpp |             settings.cpp | ||||||
|             system.cpp |             system.cpp | ||||||
| @@ -157,6 +157,7 @@ set(HEADERS | |||||||
|             hle/kernel/address_arbiter.h |             hle/kernel/address_arbiter.h | ||||||
|             hle/kernel/event.h |             hle/kernel/event.h | ||||||
|             hle/kernel/kernel.h |             hle/kernel/kernel.h | ||||||
|  |             hle/kernel/memory.h | ||||||
|             hle/kernel/mutex.h |             hle/kernel/mutex.h | ||||||
|             hle/kernel/process.h |             hle/kernel/process.h | ||||||
|             hle/kernel/resource_limit.h |             hle/kernel/resource_limit.h | ||||||
| @@ -245,7 +246,6 @@ set(HEADERS | |||||||
|             loader/ncch.h |             loader/ncch.h | ||||||
|             tracer/recorder.h |             tracer/recorder.h | ||||||
|             tracer/citrace.h |             tracer/citrace.h | ||||||
|             mem_map.h |  | ||||||
|             memory.h |             memory.h | ||||||
|             memory_setup.h |             memory_setup.h | ||||||
|             settings.h |             settings.h | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ | |||||||
|  |  | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/mem_map.h" |  | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "core/arm/skyeye_common/armstate.h" | #include "core/arm/skyeye_common/armstate.h" | ||||||
| #include "core/arm/skyeye_common/vfp/vfp.h" | #include "core/arm/skyeye_common/vfp/vfp.h" | ||||||
|   | |||||||
| @@ -17,7 +17,6 @@ | |||||||
|  |  | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  |  | ||||||
| #include "core/mem_map.h" |  | ||||||
| #include "core/arm/skyeye_common/arm_regformat.h" | #include "core/arm/skyeye_common/arm_regformat.h" | ||||||
| #include "core/arm/skyeye_common/armstate.h" | #include "core/arm/skyeye_common/armstate.h" | ||||||
| #include "core/arm/skyeye_common/armsupp.h" | #include "core/arm/skyeye_common/armsupp.h" | ||||||
|   | |||||||
| @@ -25,10 +25,6 @@ void Init() { | |||||||
|     config_mem.sys_core_ver = 0x2; |     config_mem.sys_core_ver = 0x2; | ||||||
|     config_mem.unit_info = 0x1; // Bit 0 set for Retail |     config_mem.unit_info = 0x1; // Bit 0 set for Retail | ||||||
|     config_mem.prev_firm = 0; |     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_unk = 0; | ||||||
|     config_mem.firm_version_rev = 0; |     config_mem.firm_version_rev = 0; | ||||||
|     config_mem.firm_version_min = 0x40; |     config_mem.firm_version_min = 0x40; | ||||||
| @@ -36,7 +32,4 @@ void Init() { | |||||||
|     config_mem.firm_sys_core_ver = 0x2; |     config_mem.firm_sys_core_ver = 0x2; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Shutdown() { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| } // namespace | } // namespace | ||||||
|   | |||||||
| @@ -52,6 +52,5 @@ static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE, "Config Memory | |||||||
| extern ConfigMemDef config_mem; | extern ConfigMemDef config_mem; | ||||||
|  |  | ||||||
| void Init(); | void Init(); | ||||||
| void Shutdown(); |  | ||||||
|  |  | ||||||
| } // namespace | } // namespace | ||||||
|   | |||||||
| @@ -172,6 +172,14 @@ template<ResultCode func(u32, s64, s64)> void Wrap() { | |||||||
|     FuncReturn(func(PARAM(0), param1, param2).raw); |     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 | // Function wrappers that return type u32 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -34,8 +34,6 @@ void Reschedule(const char *reason) { | |||||||
|  |  | ||||||
| void Init() { | void Init() { | ||||||
|     Service::Init(); |     Service::Init(); | ||||||
|     ConfigMem::Init(); |  | ||||||
|     SharedPage::Init(); |  | ||||||
|  |  | ||||||
|     g_reschedule = false; |     g_reschedule = false; | ||||||
|  |  | ||||||
| @@ -43,8 +41,6 @@ void Init() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void Shutdown() { | void Shutdown() { | ||||||
|     ConfigMem::Shutdown(); |  | ||||||
|     SharedPage::Shutdown(); |  | ||||||
|     Service::Shutdown(); |     Service::Shutdown(); | ||||||
|  |  | ||||||
|     LOG_DEBUG(Kernel, "shutdown OK"); |     LOG_DEBUG(Kernel, "shutdown OK"); | ||||||
|   | |||||||
| @@ -7,11 +7,14 @@ | |||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  |  | ||||||
|  | #include "core/hle/config_mem.h" | ||||||
| #include "core/hle/kernel/kernel.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/process.h" | ||||||
|  | #include "core/hle/kernel/resource_limit.h" | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
| #include "core/hle/kernel/timer.h" | #include "core/hle/kernel/timer.h" | ||||||
|  | #include "core/hle/shared_page.h" | ||||||
|  |  | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
|  |  | ||||||
| @@ -119,6 +122,13 @@ void HandleTable::Clear() { | |||||||
|  |  | ||||||
| /// Initialize the kernel | /// Initialize the kernel | ||||||
| void Init() { | 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::ResourceLimitsInit(); | ||||||
|     Kernel::ThreadingInit(); |     Kernel::ThreadingInit(); | ||||||
|     Kernel::TimersInit(); |     Kernel::TimersInit(); | ||||||
| @@ -131,11 +141,14 @@ void Init() { | |||||||
|  |  | ||||||
| /// Shutdown the kernel | /// Shutdown the kernel | ||||||
| void Shutdown() { | void Shutdown() { | ||||||
|  |     g_handle_table.Clear(); // Free all kernel objects | ||||||
|  |  | ||||||
|     Kernel::ThreadingShutdown(); |     Kernel::ThreadingShutdown(); | ||||||
|  |     g_current_process = nullptr; | ||||||
|  |  | ||||||
|     Kernel::TimersShutdown(); |     Kernel::TimersShutdown(); | ||||||
|     Kernel::ResourceLimitsShutdown(); |     Kernel::ResourceLimitsShutdown(); | ||||||
|     g_handle_table.Clear(); // Free all kernel objects |     Kernel::MemoryShutdown(); | ||||||
|     g_current_process = nullptr; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace | } // 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/logging/log.h" | ||||||
| #include "common/make_unique.h" | #include "common/make_unique.h" | ||||||
|  |  | ||||||
|  | #include "core/hle/kernel/memory.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/resource_limit.h" | #include "core/hle/kernel/resource_limit.h" | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
| #include "core/hle/kernel/vm_manager.h" | #include "core/hle/kernel/vm_manager.h" | ||||||
| #include "core/mem_map.h" |  | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
|  |  | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| @@ -36,8 +36,7 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { | |||||||
|     process->codeset = std::move(code_set); |     process->codeset = std::move(code_set); | ||||||
|     process->flags.raw = 0; |     process->flags.raw = 0; | ||||||
|     process->flags.memory_region = MemoryRegion::APPLICATION; |     process->flags.memory_region = MemoryRegion::APPLICATION; | ||||||
|     process->address_space = Common::make_unique<VMManager>(); |     Memory::InitLegacyAddressSpace(process->vm_manager); | ||||||
|     Memory::InitLegacyAddressSpace(*process->address_space); |  | ||||||
|  |  | ||||||
|     return process; |     return process; | ||||||
| } | } | ||||||
| @@ -93,9 +92,11 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | |||||||
|             mapping.unk_flag = false; |             mapping.unk_flag = false; | ||||||
|         } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF |         } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF | ||||||
|             // Kernel version |             // Kernel version | ||||||
|             int minor = descriptor & 0xFF; |             kernel_version = descriptor & 0xFFFF; | ||||||
|             int major = (descriptor >> 8) & 0xFF; |  | ||||||
|             LOG_INFO(Loader, "ExHeader kernel version ignored: %d.%d", major, minor); |             int minor = kernel_version & 0xFF; | ||||||
|  |             int major = (kernel_version >> 8) & 0xFF; | ||||||
|  |             LOG_INFO(Loader, "ExHeader kernel version: %d.%d", major, minor); | ||||||
|         } else { |         } else { | ||||||
|             LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x%08X", descriptor); |             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) { | 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 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(); |                 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->code,   VMAPermission::ReadExecute, MemoryState::Code); | ||||||
|     MapSegment(codeset->rodata, VMAPermission::Read,        MemoryState::Code); |     MapSegment(codeset->rodata, VMAPermission::Read,        MemoryState::Code); | ||||||
|     MapSegment(codeset->data,   VMAPermission::ReadWrite,   MemoryState::Private); |     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); |     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() {} | ||||||
| Kernel::Process::~Process() {} | Kernel::Process::~Process() {} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ | |||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  |  | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/vm_manager.h" | ||||||
|  |  | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
|  |  | ||||||
| @@ -48,7 +49,7 @@ union ProcessFlags { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| class ResourceLimit; | class ResourceLimit; | ||||||
| class VMManager; | struct MemoryRegionInfo; | ||||||
|  |  | ||||||
| struct CodeSet final : public Object { | struct CodeSet final : public Object { | ||||||
|     static SharedPtr<CodeSet> Create(std::string name, u64 program_id); |     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. |     /// processes access to specific I/O regions and device memory. | ||||||
|     boost::container::static_vector<AddressMapping, 8> address_mappings; |     boost::container::static_vector<AddressMapping, 8> address_mappings; | ||||||
|     ProcessFlags flags; |     ProcessFlags flags; | ||||||
|  |     /// Kernel compatibility version for this process | ||||||
|  |     u16 kernel_version = 0; | ||||||
|  |  | ||||||
|     /// The id of this process |     /// The id of this process | ||||||
|     u32 process_id = next_process_id++; |     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 |      * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them | ||||||
|      * to this process. |      * to this process. | ||||||
| @@ -123,6 +122,36 @@ public: | |||||||
|      */ |      */ | ||||||
|     void Run(s32 main_thread_priority, u32 stack_size); |     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: | private: | ||||||
|     Process(); |     Process(); | ||||||
|     ~Process() override; |     ~Process() override; | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ | |||||||
|  |  | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  |  | ||||||
| #include "core/mem_map.h" |  | ||||||
| #include "core/hle/kernel/resource_limit.h" | #include "core/hle/kernel/resource_limit.h" | ||||||
|  |  | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
|   | |||||||
| @@ -117,6 +117,7 @@ void Thread::Stop() { | |||||||
|     wait_objects.clear(); |     wait_objects.clear(); | ||||||
|  |  | ||||||
|     Kernel::g_current_process->used_tls_slots[tls_index] = false; |     Kernel::g_current_process->used_tls_slots[tls_index] = false; | ||||||
|  |     g_current_process->misc_memory_used -= Memory::TLS_ENTRY_SIZE; | ||||||
|  |  | ||||||
|     HLE::Reschedule(__func__); |     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"); |     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 |     // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used | ||||||
|     // to initialize the context |     // to initialize the context | ||||||
| @@ -504,7 +506,7 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { | |||||||
| } | } | ||||||
|  |  | ||||||
| VAddr Thread::GetTLSAddress() const { | 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 { | 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 { | bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { | ||||||
|     ASSERT(base + size == next.base); |     ASSERT(base + size == next.base); | ||||||
|     if (permissions != next.permissions || |     if (permissions != next.permissions || | ||||||
| @@ -51,11 +60,15 @@ void VMManager::Reset() { | |||||||
| } | } | ||||||
|  |  | ||||||
| VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { | 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, | 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(block != nullptr); | ||||||
|     ASSERT(offset + size <= block->size()); |     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)); |     return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | ||||||
| } | } | ||||||
|  |  | ||||||
| void VMManager::Unmap(VMAHandle vma_handle) { | VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { | ||||||
|     VMAIter iter = StripIterConstness(vma_handle); |     VirtualMemoryArea& vma = vma_handle->second; | ||||||
|  |  | ||||||
|     VirtualMemoryArea& vma = iter->second; |  | ||||||
|     vma.type = VMAType::Free; |     vma.type = VMAType::Free; | ||||||
|     vma.permissions = VMAPermission::None; |     vma.permissions = VMAPermission::None; | ||||||
|     vma.meminfo_state = MemoryState::Free; |     vma.meminfo_state = MemoryState::Free; | ||||||
| @@ -121,26 +132,67 @@ void VMManager::Unmap(VMAHandle vma_handle) { | |||||||
|  |  | ||||||
|     UpdatePageTableForVMA(vma); |     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); |     VMAIter iter = StripIterConstness(vma_handle); | ||||||
|  |  | ||||||
|     VirtualMemoryArea& vma = iter->second; |     VirtualMemoryArea& vma = iter->second; | ||||||
|     vma.permissions = new_perms; |     vma.permissions = new_perms; | ||||||
|     UpdatePageTableForVMA(vma); |     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) { |     for (const auto& p : vma_map) { | ||||||
|         const VirtualMemoryArea& vma = p.second; |         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::Read    ? 'R' : '-', | ||||||
|             (u8)vma.permissions & (u8)VMAPermission::Write   ? 'W' : '-', |             (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) { | ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { | ||||||
|     ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: %8X", size); |     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: %08X", base); |     ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", base); | ||||||
|  |  | ||||||
|     VMAIter vma_handle = StripIterConstness(FindVMA(base)); |     VMAIter vma_handle = StripIterConstness(FindVMA(base)); | ||||||
|     if (vma_handle == vma_map.end()) { |     if (vma_handle == vma_map.end()) { | ||||||
|         // Target address is outside the range managed by the kernel |         // Target address is outside the range managed by the kernel | ||||||
|         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, |         return ERR_INVALID_ADDRESS; | ||||||
|                 ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E01BF5 |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     VirtualMemoryArea& vma = vma_handle->second; |     VirtualMemoryArea& vma = vma_handle->second; | ||||||
|     if (vma.type != VMAType::Free) { |     if (vma.type != VMAType::Free) { | ||||||
|         // Region is already allocated |         // Region is already allocated | ||||||
|         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, |         return ERR_INVALID_ADDRESS_STATE; | ||||||
|                 ErrorSummary::InvalidState, ErrorLevel::Usage); // 0xE0A01BF5 |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     u32 start_in_vma = base - vma.base; |     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) { |     if (end_in_vma > vma.size) { | ||||||
|         // Requested allocation doesn't fit inside VMA |         // Requested allocation doesn't fit inside VMA | ||||||
|         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, |         return ERR_INVALID_ADDRESS_STATE; | ||||||
|                 ErrorSummary::InvalidState, ErrorLevel::Usage); // 0xE0A01BF5 |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (end_in_vma != vma.size) { |     if (end_in_vma != vma.size) { | ||||||
| @@ -189,6 +238,35 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { | |||||||
|     return MakeResult<VMAIter>(vma_handle); |     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) { | VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) { | ||||||
|     VirtualMemoryArea& old_vma = vma_handle->second; |     VirtualMemoryArea& old_vma = vma_handle->second; | ||||||
|     VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA |     VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA | ||||||
|   | |||||||
| @@ -14,6 +14,14 @@ | |||||||
|  |  | ||||||
| namespace Kernel { | 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 { | enum class VMAType : u8 { | ||||||
|     /// VMA represents an unmapped region of the address space. |     /// VMA represents an unmapped region of the address space. | ||||||
|     Free, |     Free, | ||||||
| @@ -75,7 +83,7 @@ struct VirtualMemoryArea { | |||||||
|     /// Memory block backing this VMA. |     /// Memory block backing this VMA. | ||||||
|     std::shared_ptr<std::vector<u8>> backing_block = nullptr; |     std::shared_ptr<std::vector<u8>> backing_block = nullptr; | ||||||
|     /// Offset into the backing_memory the mapping starts from. |     /// Offset into the backing_memory the mapping starts from. | ||||||
|     u32 offset = 0; |     size_t offset = 0; | ||||||
|  |  | ||||||
|     // Settings for type = BackingMemory |     // Settings for type = BackingMemory | ||||||
|     /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. |     /// 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. |      * @param state MemoryState tag to attach to the VMA. | ||||||
|      */ |      */ | ||||||
|     ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, |     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. |      * 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); |     ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state); | ||||||
|  |  | ||||||
|     /// Unmaps the given VMA. |     /// Unmaps a range of addresses, splitting VMAs as necessary. | ||||||
|     void Unmap(VMAHandle vma); |     ResultCode UnmapRange(VAddr target, u32 size); | ||||||
|  |  | ||||||
|     /// Changes the permissions of the given VMA. |     /// 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 |     /// Dumps the address space layout to the log, for debugging | ||||||
|     void LogLayout() const; |     void LogLayout(Log::Level log_level) const; | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     using VMAIter = decltype(vma_map)::iterator; |     using VMAIter = decltype(vma_map)::iterator; | ||||||
| @@ -178,12 +195,21 @@ private: | |||||||
|     /// Converts a VMAHandle to a mutable VMAIter. |     /// Converts a VMAHandle to a mutable VMAIter. | ||||||
|     VMAIter StripIterConstness(const VMAHandle& iter); |     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 |      * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing | ||||||
|      * the appropriate error checking. |      * the appropriate error checking. | ||||||
|      */ |      */ | ||||||
|     ResultVal<VMAIter> CarveVMA(VAddr base, u32 size); |     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. |      * Splits a VMA in two, at the specified offset. | ||||||
|      * @returns the right side of the split, with the original iterator becoming the left side. |      * @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/hle.h" | ||||||
| #include "core/hle/kernel/event.h" | #include "core/hle/kernel/event.h" | ||||||
| #include "core/hle/kernel/mutex.h" | #include "core/hle/kernel/mutex.h" | ||||||
|  | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/shared_memory.h" | #include "core/hle/kernel/shared_memory.h" | ||||||
| #include "core/hle/kernel/thread.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> notification_event; ///< APT notification event | ||||||
| static Kernel::SharedPtr<Kernel::Event> parameter_event; ///< APT parameter 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 | 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) { | void GetSharedFont(Service::Interface* self) { | ||||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); |     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||||
|  |  | ||||||
|     if (!shared_font.empty()) { |     if (shared_font != nullptr) { | ||||||
|         // TODO(bunnei): This function shouldn't copy the shared font every time it's called. |         // TODO(yuriks): This is a hack to keep this working right now even with our completely | ||||||
|         // Instead, it should probably map the shared font as RO memory. We don't currently have |         // broken shared memory system. | ||||||
|         // an easy way to do this, but the copy should be sufficient for now. |         shared_font_mem->base_address = SHARED_FONT_VADDR; | ||||||
|         memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size()); |         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[0] = IPC::MakeHeader(0x44, 2, 2); | ||||||
|         cmd_buff[1] = RESULT_SUCCESS.raw; // No error |         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 |     // 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.bin" in the Citra "sysdata" directory. | ||||||
|  |  | ||||||
|     shared_font.clear(); |  | ||||||
|     std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; |     std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; | ||||||
|  |  | ||||||
|     FileUtil::CreateFullPath(filepath); // Create path if not already created |     FileUtil::CreateFullPath(filepath); // Create path if not already created | ||||||
| @@ -399,8 +400,8 @@ void Init() { | |||||||
|  |  | ||||||
|     if (file.IsOpen()) { |     if (file.IsOpen()) { | ||||||
|         // Read shared font data |         // Read shared font data | ||||||
|         shared_font.resize((size_t)file.GetSize()); |         shared_font = std::make_shared<std::vector<u8>>((size_t)file.GetSize()); | ||||||
|         file.ReadBytes(shared_font.data(), (size_t)file.GetSize()); |         file.ReadBytes(shared_font->data(), shared_font->size()); | ||||||
|  |  | ||||||
|         // Create shared font memory object |         // Create shared font memory object | ||||||
|         using Kernel::MemoryPermission; |         using Kernel::MemoryPermission; | ||||||
| @@ -424,7 +425,7 @@ void Init() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void Shutdown() { | void Shutdown() { | ||||||
|     shared_font.clear(); |     shared_font = nullptr; | ||||||
|     shared_font_mem = nullptr; |     shared_font_mem = nullptr; | ||||||
|     lock = nullptr; |     lock = nullptr; | ||||||
|     notification_event = nullptr; |     notification_event = nullptr; | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ | |||||||
|  |  | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
|  |  | ||||||
| #include "core/mem_map.h" |  | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "core/hle/kernel/event.h" | #include "core/hle/kernel/event.h" | ||||||
| #include "core/hle/kernel/shared_memory.h" | #include "core/hle/kernel/shared_memory.h" | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ | |||||||
| #include "core/hle/kernel/event.h" | #include "core/hle/kernel/event.h" | ||||||
| #include "core/hle/service/y2r_u.h" | #include "core/hle/service/y2r_u.h" | ||||||
| #include "core/hw/y2r.h" | #include "core/hw/y2r.h" | ||||||
| #include "core/mem_map.h" |  | ||||||
|  |  | ||||||
| #include "video_core/renderer_base.h" | #include "video_core/renderer_base.h" | ||||||
| #include "video_core/utils.h" | #include "video_core/utils.h" | ||||||
|   | |||||||
| @@ -18,7 +18,4 @@ void Init() { | |||||||
|     shared_page.running_hw = 0x1; // product |     shared_page.running_hw = 0x1; // product | ||||||
| } | } | ||||||
|  |  | ||||||
| void Shutdown() { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| } // namespace | } // namespace | ||||||
|   | |||||||
| @@ -54,6 +54,5 @@ static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE, "Shared page st | |||||||
| extern SharedPageDef shared_page; | extern SharedPageDef shared_page; | ||||||
|  |  | ||||||
| void Init(); | void Init(); | ||||||
| void Shutdown(); |  | ||||||
|  |  | ||||||
| } // namespace | } // namespace | ||||||
|   | |||||||
| @@ -10,11 +10,11 @@ | |||||||
| #include "common/symbols.h" | #include "common/symbols.h" | ||||||
|  |  | ||||||
| #include "core/core_timing.h" | #include "core/core_timing.h" | ||||||
| #include "core/mem_map.h" |  | ||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
|  |  | ||||||
| #include "core/hle/kernel/address_arbiter.h" | #include "core/hle/kernel/address_arbiter.h" | ||||||
| #include "core/hle/kernel/event.h" | #include "core/hle/kernel/event.h" | ||||||
|  | #include "core/hle/kernel/memory.h" | ||||||
| #include "core/hle/kernel/mutex.h" | #include "core/hle/kernel/mutex.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/resource_limit.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, | const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, | ||||||
|         ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E |         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 { | enum ControlMemoryOperation { | ||||||
|     MEMORY_OPERATION_HEAP       = 0x00000003, |     MEMOP_FREE    = 1, | ||||||
|     MEMORY_OPERATION_GSP_HEAP   = 0x00010003, |     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 | /// Map application or GSP heap memory | ||||||
| static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { | 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); |         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 |     u32 region = operation & MEMOP_REGION_MASK; | ||||||
|     case MEMORY_OPERATION_HEAP: |     operation &= ~MEMOP_REGION_MASK; | ||||||
|         *out_addr = Memory::MapBlock_Heap(size, operation, permissions); |  | ||||||
|  |     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; |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Map GSP heap memory |     case MEMOP_COMMIT: | ||||||
|     case MEMORY_OPERATION_GSP_HEAP: |     { | ||||||
|         *out_addr = Memory::MapBlock_HeapLinear(size, operation, permissions); |         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; |         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: |     default: | ||||||
|         LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation); |         LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation); | ||||||
|  |         return ERR_INVALID_COMBINATION; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     process.vm_manager.LogLayout(Log::Level::Trace); | ||||||
|  |  | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -537,9 +619,9 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_inf | |||||||
|     if (process == nullptr) |     if (process == nullptr) | ||||||
|         return ERR_INVALID_HANDLE; |         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); |         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||||||
|  |  | ||||||
|     memory_info->base_address = vma->second.base; |     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; |     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 { | namespace { | ||||||
|     struct FunctionDef { |     struct FunctionDef { | ||||||
|         using Func = void(); |         using Func = void(); | ||||||
| @@ -746,7 +874,7 @@ static const FunctionDef SVC_Table[] = { | |||||||
|     {0x28, HLE::Wrap<GetSystemTick>,        "GetSystemTick"}, |     {0x28, HLE::Wrap<GetSystemTick>,        "GetSystemTick"}, | ||||||
|     {0x29, nullptr,                         "GetHandleInfo"}, |     {0x29, nullptr,                         "GetHandleInfo"}, | ||||||
|     {0x2A, nullptr,                         "GetSystemInfo"}, |     {0x2A, nullptr,                         "GetSystemInfo"}, | ||||||
|     {0x2B, nullptr,                         "GetProcessInfo"}, |     {0x2B, HLE::Wrap<GetProcessInfo>,       "GetProcessInfo"}, | ||||||
|     {0x2C, nullptr,                         "GetThreadInfo"}, |     {0x2C, nullptr,                         "GetThreadInfo"}, | ||||||
|     {0x2D, HLE::Wrap<ConnectToPort>,        "ConnectToPort"}, |     {0x2D, HLE::Wrap<ConnectToPort>,        "ConnectToPort"}, | ||||||
|     {0x2E, nullptr,                         "SendSyncRequest1"}, |     {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/logging/log.h" | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
|  |  | ||||||
| #include "core/mem_map.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "core/memory_setup.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]); |         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 | } // namespace | ||||||
|   | |||||||
| @@ -15,6 +15,8 @@ namespace Memory { | |||||||
|  * be mapped. |  * be mapped. | ||||||
|  */ |  */ | ||||||
| const u32 PAGE_SIZE = 0x1000; | 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 | /// Physical memory regions as seen from the ARM11 | ||||||
| enum : PAddr { | enum : PAddr { | ||||||
| @@ -103,8 +105,15 @@ enum : VAddr { | |||||||
|     // hardcoded value. |     // hardcoded value. | ||||||
|     /// Area where TLS (Thread-Local Storage) buffers are allocated. |     /// Area where TLS (Thread-Local Storage) buffers are allocated. | ||||||
|     TLS_AREA_VADDR     = 0x1FF82000, |     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, |     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); | u8 Read8(VAddr addr); | ||||||
| @@ -121,6 +130,17 @@ void WriteBlock(VAddr addr, const u8* data, size_t size); | |||||||
|  |  | ||||||
| u8* GetPointer(VAddr virtual_address); | 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. |  * Gets a pointer to the memory region beginning at the specified physical address. | ||||||
|  * |  * | ||||||
|   | |||||||
| @@ -10,9 +10,6 @@ | |||||||
|  |  | ||||||
| namespace Memory { | namespace Memory { | ||||||
|  |  | ||||||
| const u32 PAGE_MASK = PAGE_SIZE - 1; |  | ||||||
| const int PAGE_BITS = 12; |  | ||||||
|  |  | ||||||
| void InitMemoryMap(); | void InitMemoryMap(); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -4,11 +4,11 @@ | |||||||
|  |  | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/core_timing.h" | #include "core/core_timing.h" | ||||||
| #include "core/mem_map.h" |  | ||||||
| #include "core/system.h" | #include "core/system.h" | ||||||
| #include "core/hw/hw.h" | #include "core/hw/hw.h" | ||||||
| #include "core/hle/hle.h" | #include "core/hle/hle.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/memory.h" | ||||||
|  |  | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
|  |  | ||||||
| @@ -29,7 +29,6 @@ void Shutdown() { | |||||||
|     HLE::Shutdown(); |     HLE::Shutdown(); | ||||||
|     Kernel::Shutdown(); |     Kernel::Shutdown(); | ||||||
|     HW::Shutdown(); |     HW::Shutdown(); | ||||||
|     Memory::Shutdown(); |  | ||||||
|     CoreTiming::Shutdown(); |     CoreTiming::Shutdown(); | ||||||
|     Core::Shutdown(); |     Core::Shutdown(); | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user