mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #3666 from bunnei/new-vmm
Implement a new virtual memory manager
This commit is contained in:
		
						commit
						b8f5c71f2d
					
				
							
								
								
									
										2
									
								
								externals/dynarmic
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										2
									
								
								externals/dynarmic
									
									
									
									
										vendored
									
									
								
							| @ -1 +1 @@ | ||||
| Subproject commit f6ae9e1c3311b747b7b91fd903c62bf40b3b9c88 | ||||
| Subproject commit 57b987c185ae6677861cbf781f08ed1649b0543e | ||||
| @ -36,9 +36,9 @@ public: | ||||
|     } | ||||
| 
 | ||||
|     void SetWaveIndex(std::size_t index); | ||||
|     std::vector<s16> DequeueSamples(std::size_t sample_count, Memory::Memory& memory); | ||||
|     std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory); | ||||
|     void UpdateState(); | ||||
|     void RefreshBuffer(Memory::Memory& memory); | ||||
|     void RefreshBuffer(Core::Memory::Memory& memory); | ||||
| 
 | ||||
| private: | ||||
|     bool is_in_use{}; | ||||
| @ -66,13 +66,14 @@ public: | ||||
|         return info; | ||||
|     } | ||||
| 
 | ||||
|     void UpdateState(Memory::Memory& memory); | ||||
|     void UpdateState(Core::Memory::Memory& memory); | ||||
| 
 | ||||
| private: | ||||
|     EffectOutStatus out_status{}; | ||||
|     EffectInStatus info{}; | ||||
| }; | ||||
| AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_, | ||||
| 
 | ||||
| AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, | ||||
|                              AudioRendererParameter params, | ||||
|                              std::shared_ptr<Kernel::WritableEvent> buffer_event, | ||||
|                              std::size_t instance_number) | ||||
| @ -208,7 +209,7 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) { | ||||
| } | ||||
| 
 | ||||
| std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count, | ||||
|                                                            Memory::Memory& memory) { | ||||
|                                                            Core::Memory::Memory& memory) { | ||||
|     if (!IsPlaying()) { | ||||
|         return {}; | ||||
|     } | ||||
| @ -258,7 +259,7 @@ void AudioRenderer::VoiceState::UpdateState() { | ||||
|     is_in_use = info.is_in_use; | ||||
| } | ||||
| 
 | ||||
| void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) { | ||||
| void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) { | ||||
|     const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; | ||||
|     const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; | ||||
|     std::vector<s16> new_samples(wave_buffer_size / sizeof(s16)); | ||||
| @ -310,7 +311,7 @@ void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) { | ||||
|     is_refresh_pending = false; | ||||
| } | ||||
| 
 | ||||
| void AudioRenderer::EffectState::UpdateState(Memory::Memory& memory) { | ||||
| void AudioRenderer::EffectState::UpdateState(Core::Memory::Memory& memory) { | ||||
|     if (info.is_new) { | ||||
|         out_status.state = EffectStatus::New; | ||||
|     } else { | ||||
|  | ||||
| @ -22,7 +22,7 @@ namespace Kernel { | ||||
| class WritableEvent; | ||||
| } | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
| @ -221,7 +221,7 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size | ||||
| 
 | ||||
| class AudioRenderer { | ||||
| public: | ||||
|     AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_, | ||||
|     AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, | ||||
|                   AudioRendererParameter params, | ||||
|                   std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number); | ||||
|     ~AudioRenderer(); | ||||
| @ -244,7 +244,7 @@ private: | ||||
|     std::vector<EffectState> effects; | ||||
|     std::unique_ptr<AudioOut> audio_out; | ||||
|     StreamPtr stream; | ||||
|     Memory::Memory& memory; | ||||
|     Core::Memory::Memory& memory; | ||||
| }; | ||||
| 
 | ||||
| } // namespace AudioCore
 | ||||
|  | ||||
| @ -155,6 +155,8 @@ add_library(common STATIC | ||||
|     uuid.cpp | ||||
|     uuid.h | ||||
|     vector_math.h | ||||
|     virtual_buffer.cpp | ||||
|     virtual_buffer.h | ||||
|     web_result.h | ||||
|     zstd_compression.cpp | ||||
|     zstd_compression.h | ||||
|  | ||||
| @ -38,6 +38,13 @@ constexpr bool IsWordAligned(T value) { | ||||
|     return (value & 0b11) == 0; | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| constexpr bool IsAligned(T value, std::size_t alignment) { | ||||
|     using U = typename std::make_unsigned<T>::type; | ||||
|     const U mask = static_cast<U>(alignment - 1); | ||||
|     return (value & mask) == 0; | ||||
| } | ||||
| 
 | ||||
| template <typename T, std::size_t Align = 16> | ||||
| class AlignmentAllocator { | ||||
| public: | ||||
|  | ||||
| @ -55,6 +55,38 @@ __declspec(dllimport) void __stdcall DebugBreak(void); | ||||
| // Defined in Misc.cpp.
 | ||||
| std::string GetLastErrorMsg(); | ||||
| 
 | ||||
| #define DECLARE_ENUM_FLAG_OPERATORS(type)                                                          \ | ||||
|     constexpr type operator|(type a, type b) noexcept {                                            \ | ||||
|         using T = std::underlying_type_t<type>;                                                    \ | ||||
|         return static_cast<type>(static_cast<T>(a) | static_cast<T>(b));                           \ | ||||
|     }                                                                                              \ | ||||
|     constexpr type operator&(type a, type b) noexcept {                                            \ | ||||
|         using T = std::underlying_type_t<type>;                                                    \ | ||||
|         return static_cast<type>(static_cast<T>(a) & static_cast<T>(b));                           \ | ||||
|     }                                                                                              \ | ||||
|     constexpr type& operator|=(type& a, type b) noexcept {                                         \ | ||||
|         using T = std::underlying_type_t<type>;                                                    \ | ||||
|         a = static_cast<type>(static_cast<T>(a) | static_cast<T>(b));                              \ | ||||
|         return a;                                                                                  \ | ||||
|     }                                                                                              \ | ||||
|     constexpr type& operator&=(type& a, type b) noexcept {                                         \ | ||||
|         using T = std::underlying_type_t<type>;                                                    \ | ||||
|         a = static_cast<type>(static_cast<T>(a) & static_cast<T>(b));                              \ | ||||
|         return a;                                                                                  \ | ||||
|     }                                                                                              \ | ||||
|     constexpr type operator~(type key) noexcept {                                                  \ | ||||
|         using T = std::underlying_type_t<type>;                                                    \ | ||||
|         return static_cast<type>(~static_cast<T>(key));                                            \ | ||||
|     }                                                                                              \ | ||||
|     constexpr bool True(type key) noexcept {                                                       \ | ||||
|         using T = std::underlying_type_t<type>;                                                    \ | ||||
|         return static_cast<T>(key) != 0;                                                           \ | ||||
|     }                                                                                              \ | ||||
|     constexpr bool False(type key) noexcept {                                                      \ | ||||
|         using T = std::underlying_type_t<type>;                                                    \ | ||||
|         return static_cast<T>(key) == 0;                                                           \ | ||||
|     } | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| constexpr u32 MakeMagic(char a, char b, char c, char d) { | ||||
|  | ||||
| @ -6,36 +6,20 @@ | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| PageTable::PageTable(std::size_t page_size_in_bits) : page_size_in_bits{page_size_in_bits} {} | ||||
| PageTable::PageTable() = default; | ||||
| 
 | ||||
| PageTable::~PageTable() = default; | ||||
| 
 | ||||
| void PageTable::Resize(std::size_t address_space_width_in_bits) { | ||||
|     const std::size_t num_page_table_entries = 1ULL | ||||
|                                                << (address_space_width_in_bits - page_size_in_bits); | ||||
| 
 | ||||
| void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, | ||||
|                        bool has_attribute) { | ||||
|     const std::size_t num_page_table_entries{1ULL | ||||
|                                              << (address_space_width_in_bits - page_size_in_bits)}; | ||||
|     pointers.resize(num_page_table_entries); | ||||
|     attributes.resize(num_page_table_entries); | ||||
| 
 | ||||
|     // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
 | ||||
|     // vector size is subsequently decreased (via resize), the vector might not automatically
 | ||||
|     // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
 | ||||
|     // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
 | ||||
| 
 | ||||
|     pointers.shrink_to_fit(); | ||||
|     attributes.shrink_to_fit(); | ||||
| } | ||||
| 
 | ||||
| BackingPageTable::BackingPageTable(std::size_t page_size_in_bits) : PageTable{page_size_in_bits} {} | ||||
| 
 | ||||
| BackingPageTable::~BackingPageTable() = default; | ||||
| 
 | ||||
| void BackingPageTable::Resize(std::size_t address_space_width_in_bits) { | ||||
|     PageTable::Resize(address_space_width_in_bits); | ||||
|     const std::size_t num_page_table_entries = 1ULL | ||||
|                                                << (address_space_width_in_bits - page_size_in_bits); | ||||
|     backing_addr.resize(num_page_table_entries); | ||||
|     backing_addr.shrink_to_fit(); | ||||
| 
 | ||||
|     if (has_attribute) { | ||||
|         attributes.resize(num_page_table_entries); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace Common
 | ||||
|  | ||||
| @ -5,9 +5,12 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <boost/icl/interval_map.hpp> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "common/memory_hook.h" | ||||
| #include "common/virtual_buffer.h" | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| @ -47,7 +50,7 @@ struct SpecialRegion { | ||||
|  * mimics the way a real CPU page table works. | ||||
|  */ | ||||
| struct PageTable { | ||||
|     explicit PageTable(std::size_t page_size_in_bits); | ||||
|     PageTable(); | ||||
|     ~PageTable(); | ||||
| 
 | ||||
|     /**
 | ||||
| @ -56,40 +59,18 @@ struct PageTable { | ||||
|      * | ||||
|      * @param address_space_width_in_bits The address size width in bits. | ||||
|      */ | ||||
|     void Resize(std::size_t address_space_width_in_bits); | ||||
|     void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, | ||||
|                 bool has_attribute); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Vector of memory pointers backing each page. An entry can only be non-null if the | ||||
|      * corresponding entry in the `attributes` vector is of type `Memory`. | ||||
|      */ | ||||
|     std::vector<u8*> pointers; | ||||
|     VirtualBuffer<u8*> pointers; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is | ||||
|      * of type `Special`. | ||||
|      */ | ||||
|     boost::icl::interval_map<u64, std::set<SpecialRegion>> special_regions; | ||||
|     VirtualBuffer<u64> backing_addr; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then | ||||
|      * the corresponding entry in `pointers` MUST be set to null. | ||||
|      */ | ||||
|     std::vector<PageType> attributes; | ||||
| 
 | ||||
|     const std::size_t page_size_in_bits{}; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * A more advanced Page Table with the ability to save a backing address when using it | ||||
|  * depends on another MMU. | ||||
|  */ | ||||
| struct BackingPageTable : PageTable { | ||||
|     explicit BackingPageTable(std::size_t page_size_in_bits); | ||||
|     ~BackingPageTable(); | ||||
| 
 | ||||
|     void Resize(std::size_t address_space_width_in_bits); | ||||
| 
 | ||||
|     std::vector<u64> backing_addr; | ||||
|     VirtualBuffer<PageType> attributes; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Common
 | ||||
|  | ||||
| @ -12,10 +12,17 @@ template <typename Func> | ||||
| struct ScopeExitHelper { | ||||
|     explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {} | ||||
|     ~ScopeExitHelper() { | ||||
|         func(); | ||||
|         if (active) { | ||||
|             func(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void Cancel() { | ||||
|         active = false; | ||||
|     } | ||||
| 
 | ||||
|     Func func; | ||||
|     bool active{true}; | ||||
| }; | ||||
| 
 | ||||
| template <typename Func> | ||||
|  | ||||
							
								
								
									
										52
									
								
								src/common/virtual_buffer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/common/virtual_buffer.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| #include <windows.h> | ||||
| #else | ||||
| #include <stdio.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/types.h> | ||||
| #if defined __APPLE__ || defined __FreeBSD__ || defined __OpenBSD__ | ||||
| #include <sys/sysctl.h> | ||||
| #elif defined __HAIKU__ | ||||
| #include <OS.h> | ||||
| #else | ||||
| #include <sys/sysinfo.h> | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "common/virtual_buffer.h" | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| void* AllocateMemoryPages(std::size_t size) { | ||||
| #ifdef _WIN32 | ||||
|     void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)}; | ||||
| #else | ||||
|     void* base{mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)}; | ||||
| 
 | ||||
|     if (base == MAP_FAILED) { | ||||
|         base = nullptr; | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|     ASSERT(base); | ||||
| 
 | ||||
|     return base; | ||||
| } | ||||
| 
 | ||||
| void FreeMemoryPages(void* base, std::size_t size) { | ||||
|     if (!base) { | ||||
|         return; | ||||
|     } | ||||
| #ifdef _WIN32 | ||||
|     ASSERT(VirtualFree(base, 0, MEM_RELEASE)); | ||||
| #else | ||||
|     ASSERT(munmap(base, size) == 0); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| } // namespace Common
 | ||||
							
								
								
									
										58
									
								
								src/common/virtual_buffer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/common/virtual_buffer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_funcs.h" | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| void* AllocateMemoryPages(std::size_t size); | ||||
| void FreeMemoryPages(void* base, std::size_t size); | ||||
| 
 | ||||
| template <typename T> | ||||
| class VirtualBuffer final : NonCopyable { | ||||
| public: | ||||
|     constexpr VirtualBuffer() = default; | ||||
|     explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { | ||||
|         base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); | ||||
|     } | ||||
| 
 | ||||
|     ~VirtualBuffer() { | ||||
|         FreeMemoryPages(base_ptr, alloc_size); | ||||
|     } | ||||
| 
 | ||||
|     void resize(std::size_t count) { | ||||
|         FreeMemoryPages(base_ptr, alloc_size); | ||||
| 
 | ||||
|         alloc_size = count * sizeof(T); | ||||
|         base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); | ||||
|     } | ||||
| 
 | ||||
|     constexpr const T& operator[](std::size_t index) const { | ||||
|         return base_ptr[index]; | ||||
|     } | ||||
| 
 | ||||
|     constexpr T& operator[](std::size_t index) { | ||||
|         return base_ptr[index]; | ||||
|     } | ||||
| 
 | ||||
|     constexpr T* data() { | ||||
|         return base_ptr; | ||||
|     } | ||||
| 
 | ||||
|     constexpr const T* data() const { | ||||
|         return base_ptr; | ||||
|     } | ||||
| 
 | ||||
|     constexpr std::size_t size() const { | ||||
|         return alloc_size / sizeof(T); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::size_t alloc_size{}; | ||||
|     T* base_ptr{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Common
 | ||||
| @ -35,6 +35,8 @@ add_library(core STATIC | ||||
|     crypto/ctr_encryption_layer.h | ||||
|     crypto/xts_encryption_layer.cpp | ||||
|     crypto/xts_encryption_layer.h | ||||
|     device_memory.cpp | ||||
|     device_memory.h | ||||
|     file_sys/bis_factory.cpp | ||||
|     file_sys/bis_factory.h | ||||
|     file_sys/card_image.cpp | ||||
| @ -152,6 +154,23 @@ add_library(core STATIC | ||||
|     hle/kernel/hle_ipc.h | ||||
|     hle/kernel/kernel.cpp | ||||
|     hle/kernel/kernel.h | ||||
|     hle/kernel/memory/address_space_info.cpp | ||||
|     hle/kernel/memory/address_space_info.h | ||||
|     hle/kernel/memory/memory_block.h | ||||
|     hle/kernel/memory/memory_block_manager.cpp | ||||
|     hle/kernel/memory/memory_block_manager.h | ||||
|     hle/kernel/memory/memory_layout.h | ||||
|     hle/kernel/memory/memory_manager.cpp | ||||
|     hle/kernel/memory/memory_manager.h | ||||
|     hle/kernel/memory/memory_types.h | ||||
|     hle/kernel/memory/page_linked_list.h | ||||
|     hle/kernel/memory/page_heap.cpp | ||||
|     hle/kernel/memory/page_heap.h | ||||
|     hle/kernel/memory/page_table.cpp | ||||
|     hle/kernel/memory/page_table.h | ||||
|     hle/kernel/memory/slab_heap.h | ||||
|     hle/kernel/memory/system_control.cpp | ||||
|     hle/kernel/memory/system_control.h | ||||
|     hle/kernel/mutex.cpp | ||||
|     hle/kernel/mutex.h | ||||
|     hle/kernel/object.cpp | ||||
| @ -178,6 +197,7 @@ add_library(core STATIC | ||||
|     hle/kernel/shared_memory.h | ||||
|     hle/kernel/svc.cpp | ||||
|     hle/kernel/svc.h | ||||
|     hle/kernel/svc_types.h | ||||
|     hle/kernel/svc_wrap.h | ||||
|     hle/kernel/synchronization_object.cpp | ||||
|     hle/kernel/synchronization_object.h | ||||
| @ -189,8 +209,6 @@ add_library(core STATIC | ||||
|     hle/kernel/time_manager.h | ||||
|     hle/kernel/transfer_memory.cpp | ||||
|     hle/kernel/transfer_memory.h | ||||
|     hle/kernel/vm_manager.cpp | ||||
|     hle/kernel/vm_manager.h | ||||
|     hle/kernel/writable_event.cpp | ||||
|     hle/kernel/writable_event.h | ||||
|     hle/lock.cpp | ||||
|  | ||||
| @ -60,7 +60,7 @@ static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size."); | ||||
| 
 | ||||
| using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; | ||||
| 
 | ||||
| Symbols GetSymbols(VAddr text_offset, Memory::Memory& memory) { | ||||
| Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) { | ||||
|     const auto mod_offset = text_offset + memory.Read32(text_offset + 4); | ||||
| 
 | ||||
|     if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || | ||||
|  | ||||
| @ -26,28 +26,28 @@ public: | ||||
|     virtual ~ARM_Interface() = default; | ||||
| 
 | ||||
|     struct ThreadContext32 { | ||||
|         std::array<u32, 16> cpu_registers; | ||||
|         u32 cpsr; | ||||
|         std::array<u8, 4> padding; | ||||
|         std::array<u64, 32> fprs; | ||||
|         u32 fpscr; | ||||
|         u32 fpexc; | ||||
|         u32 tpidr; | ||||
|         std::array<u32, 16> cpu_registers{}; | ||||
|         u32 cpsr{}; | ||||
|         std::array<u8, 4> padding{}; | ||||
|         std::array<u64, 32> fprs{}; | ||||
|         u32 fpscr{}; | ||||
|         u32 fpexc{}; | ||||
|         u32 tpidr{}; | ||||
|     }; | ||||
|     // Internally within the kernel, it expects the AArch32 version of the
 | ||||
|     // thread context to be 344 bytes in size.
 | ||||
|     static_assert(sizeof(ThreadContext32) == 0x158); | ||||
| 
 | ||||
|     struct ThreadContext64 { | ||||
|         std::array<u64, 31> cpu_registers; | ||||
|         u64 sp; | ||||
|         u64 pc; | ||||
|         u32 pstate; | ||||
|         std::array<u8, 4> padding; | ||||
|         std::array<u128, 32> vector_registers; | ||||
|         u32 fpcr; | ||||
|         u32 fpsr; | ||||
|         u64 tpidr; | ||||
|         std::array<u64, 31> cpu_registers{}; | ||||
|         u64 sp{}; | ||||
|         u64 pc{}; | ||||
|         u32 pstate{}; | ||||
|         std::array<u8, 4> padding{}; | ||||
|         std::array<u128, 32> vector_registers{}; | ||||
|         u32 fpcr{}; | ||||
|         u32 fpsr{}; | ||||
|         u64 tpidr{}; | ||||
|     }; | ||||
|     // Internally within the kernel, it expects the AArch64 version of the
 | ||||
|     // thread context to be 800 bytes in size.
 | ||||
|  | ||||
| @ -67,7 +67,7 @@ public: | ||||
|     } | ||||
| 
 | ||||
|     void CallSVC(u32 swi) override { | ||||
|         Kernel::CallSVC(parent.system, swi); | ||||
|         Kernel::Svc::Call(parent.system, swi); | ||||
|     } | ||||
| 
 | ||||
|     void AddTicks(u64 ticks) override { | ||||
|  | ||||
| @ -15,7 +15,7 @@ | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/arm/exclusive_monitor.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,7 @@ | ||||
| #include <dynarmic/A64/config.h> | ||||
| #include "common/logging/log.h" | ||||
| #include "common/microprofile.h" | ||||
| #include "common/page_table.h" | ||||
| #include "core/arm/dynarmic/arm_dynarmic_64.h" | ||||
| #include "core/core.h" | ||||
| #include "core/core_manager.h" | ||||
| @ -18,7 +19,6 @@ | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/scheduler.h" | ||||
| #include "core/hle/kernel/svc.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Core { | ||||
| @ -103,7 +103,7 @@ public: | ||||
|     } | ||||
| 
 | ||||
|     void CallSVC(u32 swi) override { | ||||
|         Kernel::CallSVC(parent.system, swi); | ||||
|         Kernel::Svc::Call(parent.system, swi); | ||||
|     } | ||||
| 
 | ||||
|     void AddTicks(u64 ticks) override { | ||||
| @ -159,6 +159,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& | ||||
|     // Unpredictable instructions
 | ||||
|     config.define_unpredictable_behaviour = true; | ||||
| 
 | ||||
|     config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; | ||||
|     config.only_detect_misalignment_via_page_table_on_page_boundary = true; | ||||
| 
 | ||||
|     return std::make_shared<Dynarmic::A64::Jit>(config); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -15,7 +15,7 @@ | ||||
| #include "core/arm/exclusive_monitor.h" | ||||
| #include "core/arm/unicorn/arm_unicorn.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
| @ -92,7 +92,7 @@ public: | ||||
| private: | ||||
|     friend class ARM_Dynarmic_64; | ||||
|     Dynarmic::A64::ExclusiveMonitor monitor; | ||||
|     Memory::Memory& memory; | ||||
|     Core::Memory::Memory& memory; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Core
 | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -266,7 +266,7 @@ void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) { | ||||
| 
 | ||||
|     switch (ec) { | ||||
|     case 0x15: // SVC
 | ||||
|         Kernel::CallSVC(arm_instance->system, iss); | ||||
|         Kernel::Svc::Call(arm_instance->system, iss); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -14,6 +14,7 @@ | ||||
| #include "core/core_manager.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/cpu_manager.h" | ||||
| #include "core/device_memory.h" | ||||
| #include "core/file_sys/bis_factory.h" | ||||
| #include "core/file_sys/card_image.h" | ||||
| #include "core/file_sys/mode.h" | ||||
| @ -140,6 +141,8 @@ struct System::Impl { | ||||
|     ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { | ||||
|         LOG_DEBUG(HW_Memory, "initialized OK"); | ||||
| 
 | ||||
|         device_memory = std::make_unique<Core::DeviceMemory>(system); | ||||
| 
 | ||||
|         core_timing.Initialize(); | ||||
|         kernel.Initialize(); | ||||
|         cpu_manager.Initialize(); | ||||
| @ -276,6 +279,7 @@ struct System::Impl { | ||||
|         telemetry_session.reset(); | ||||
|         perf_stats.reset(); | ||||
|         gpu_core.reset(); | ||||
|         device_memory.reset(); | ||||
| 
 | ||||
|         // Close all CPU/threading state
 | ||||
|         cpu_manager.Shutdown(); | ||||
| @ -346,7 +350,8 @@ struct System::Impl { | ||||
|     std::unique_ptr<Loader::AppLoader> app_loader; | ||||
|     std::unique_ptr<Tegra::GPU> gpu_core; | ||||
|     std::unique_ptr<Hardware::InterruptManager> interrupt_manager; | ||||
|     Memory::Memory memory; | ||||
|     std::unique_ptr<Core::DeviceMemory> device_memory; | ||||
|     Core::Memory::Memory memory; | ||||
|     CpuManager cpu_manager; | ||||
|     bool is_powered_on = false; | ||||
|     bool exit_lock = false; | ||||
| @ -472,6 +477,14 @@ Kernel::Process* System::CurrentProcess() { | ||||
|     return impl->kernel.CurrentProcess(); | ||||
| } | ||||
| 
 | ||||
| Core::DeviceMemory& System::DeviceMemory() { | ||||
|     return *impl->device_memory; | ||||
| } | ||||
| 
 | ||||
| const Core::DeviceMemory& System::DeviceMemory() const { | ||||
|     return *impl->device_memory; | ||||
| } | ||||
| 
 | ||||
| const Kernel::Process* System::CurrentProcess() const { | ||||
|     return impl->kernel.CurrentProcess(); | ||||
| } | ||||
| @ -505,7 +518,7 @@ Memory::Memory& System::Memory() { | ||||
|     return impl->memory; | ||||
| } | ||||
| 
 | ||||
| const Memory::Memory& System::Memory() const { | ||||
| const Core::Memory::Memory& System::Memory() const { | ||||
|     return impl->memory; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -36,9 +36,10 @@ class AppLoader; | ||||
| enum class ResultStatus : u16; | ||||
| } // namespace Loader
 | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| struct CheatEntry; | ||||
| } // namespace Memory
 | ||||
| class Memory; | ||||
| } // namespace Core::Memory
 | ||||
| 
 | ||||
| namespace Service { | ||||
| 
 | ||||
| @ -86,14 +87,11 @@ namespace Core::Hardware { | ||||
| class InterruptManager; | ||||
| } | ||||
| 
 | ||||
| namespace Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
| namespace Core { | ||||
| 
 | ||||
| class ARM_Interface; | ||||
| class CoreManager; | ||||
| class DeviceMemory; | ||||
| class ExclusiveMonitor; | ||||
| class FrameLimiter; | ||||
| class PerfStats; | ||||
| @ -230,10 +228,10 @@ public: | ||||
|     const ExclusiveMonitor& Monitor() const; | ||||
| 
 | ||||
|     /// Gets a mutable reference to the system memory instance.
 | ||||
|     Memory::Memory& Memory(); | ||||
|     Core::Memory::Memory& Memory(); | ||||
| 
 | ||||
|     /// Gets a constant reference to the system memory instance.
 | ||||
|     const Memory::Memory& Memory() const; | ||||
|     const Core::Memory::Memory& Memory() const; | ||||
| 
 | ||||
|     /// Gets a mutable reference to the GPU interface
 | ||||
|     Tegra::GPU& GPU(); | ||||
| @ -259,6 +257,12 @@ public: | ||||
|     /// Gets the global scheduler
 | ||||
|     const Kernel::GlobalScheduler& GlobalScheduler() const; | ||||
| 
 | ||||
|     /// Gets the manager for the guest device memory
 | ||||
|     Core::DeviceMemory& DeviceMemory(); | ||||
| 
 | ||||
|     /// Gets the manager for the guest device memory
 | ||||
|     const Core::DeviceMemory& DeviceMemory() const; | ||||
| 
 | ||||
|     /// Provides a pointer to the current process
 | ||||
|     Kernel::Process* CurrentProcess(); | ||||
| 
 | ||||
|  | ||||
| @ -22,7 +22,7 @@ namespace Core::Timing { | ||||
| class CoreTiming; | ||||
| } | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										15
									
								
								src/core/device_memory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/core/device_memory.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "core/core.h" | ||||
| #include "core/device_memory.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Core { | ||||
| 
 | ||||
| DeviceMemory::DeviceMemory(System& system) : buffer{DramMemoryMap::Size}, system{system} {} | ||||
| 
 | ||||
| DeviceMemory::~DeviceMemory() = default; | ||||
| 
 | ||||
| } // namespace Core
 | ||||
							
								
								
									
										51
									
								
								src/core/device_memory.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/core/device_memory.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/virtual_buffer.h" | ||||
| 
 | ||||
| namespace Core { | ||||
| 
 | ||||
| class System; | ||||
| 
 | ||||
| namespace DramMemoryMap { | ||||
| enum : u64 { | ||||
|     Base = 0x80000000ULL, | ||||
|     Size = 0x100000000ULL, | ||||
|     End = Base + Size, | ||||
|     KernelReserveBase = Base + 0x60000, | ||||
|     SlabHeapBase = KernelReserveBase + 0x85000, | ||||
|     SlapHeapSize = 0xa21000, | ||||
|     SlabHeapEnd = SlabHeapBase + SlapHeapSize, | ||||
| }; | ||||
| }; // namespace DramMemoryMap
 | ||||
| 
 | ||||
| class DeviceMemory : NonCopyable { | ||||
| public: | ||||
|     explicit DeviceMemory(Core::System& system); | ||||
|     ~DeviceMemory(); | ||||
| 
 | ||||
|     template <typename T> | ||||
|     PAddr GetPhysicalAddr(const T* ptr) const { | ||||
|         return (reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(buffer.data())) + | ||||
|                DramMemoryMap::Base; | ||||
|     } | ||||
| 
 | ||||
|     u8* GetPointer(PAddr addr) { | ||||
|         return buffer.data() + (addr - DramMemoryMap::Base); | ||||
|     } | ||||
| 
 | ||||
|     const u8* GetPointer(PAddr addr) const { | ||||
|         return buffer.data() + (addr - DramMemoryMap::Base); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     Common::VirtualBuffer<u8> buffer; | ||||
|     Core::System& system; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Core
 | ||||
| @ -249,7 +249,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | ||||
| } | ||||
| 
 | ||||
| namespace { | ||||
| std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder( | ||||
| std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder( | ||||
|     const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_, | ||||
|     const VirtualDir& base_path, bool upper) { | ||||
|     const auto build_id_raw = Common::HexToString(build_id_, upper); | ||||
| @ -269,14 +269,14 @@ std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder( | ||||
|         return std::nullopt; | ||||
|     } | ||||
| 
 | ||||
|     Memory::TextCheatParser parser; | ||||
|     Core::Memory::TextCheatParser parser; | ||||
|     return parser.Parse( | ||||
|         system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size())); | ||||
| } | ||||
| 
 | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| std::vector<Memory::CheatEntry> PatchManager::CreateCheatList( | ||||
| std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( | ||||
|     const Core::System& system, const std::array<u8, 32>& build_id_) const { | ||||
|     const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); | ||||
|     if (load_dir == nullptr) { | ||||
| @ -289,7 +289,7 @@ std::vector<Memory::CheatEntry> PatchManager::CreateCheatList( | ||||
|     std::sort(patch_dirs.begin(), patch_dirs.end(), | ||||
|               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||||
| 
 | ||||
|     std::vector<Memory::CheatEntry> out; | ||||
|     std::vector<Core::Memory::CheatEntry> out; | ||||
|     for (const auto& subdir : patch_dirs) { | ||||
|         if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) { | ||||
|             continue; | ||||
|  | ||||
| @ -51,8 +51,8 @@ public: | ||||
|     bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; | ||||
| 
 | ||||
|     // Creates a CheatList object with all
 | ||||
|     std::vector<Memory::CheatEntry> CreateCheatList(const Core::System& system, | ||||
|                                                     const std::array<u8, 0x20>& build_id) const; | ||||
|     std::vector<Core::Memory::CheatEntry> CreateCheatList( | ||||
|         const Core::System& system, const std::array<u8, 0x20>& build_id) const; | ||||
| 
 | ||||
|     // Currently tracked RomFS patches:
 | ||||
|     // - Game Updates
 | ||||
|  | ||||
| @ -37,9 +37,9 @@ | ||||
| #include "core/core.h" | ||||
| #include "core/core_manager.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/scheduler.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| @ -643,7 +643,7 @@ static void HandleQuery() { | ||||
|         SendReply(target_xml); | ||||
|     } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) { | ||||
|         const VAddr base_address = | ||||
|             Core::System::GetInstance().CurrentProcess()->VMManager().GetCodeRegionBaseAddress(); | ||||
|             Core::System::GetInstance().CurrentProcess()->PageTable().GetCodeRegionStart(); | ||||
|         std::string buffer = fmt::format("TextSeg={:0x}", base_address); | ||||
|         SendReply(buffer.c_str()); | ||||
|     } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { | ||||
|  | ||||
| @ -47,7 +47,8 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern | ||||
|     return MakeResult(std::move(client_session)); | ||||
| } | ||||
| 
 | ||||
| ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) { | ||||
| ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, | ||||
|                                           Core::Memory::Memory& memory) { | ||||
|     // Keep ServerSession alive until we're done working with it.
 | ||||
|     if (!parent->Server()) { | ||||
|         return ERR_SESSION_CLOSED_BY_REMOTE; | ||||
|  | ||||
| @ -12,7 +12,7 @@ | ||||
| 
 | ||||
| union ResultCode; | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
| @ -42,7 +42,7 @@ public: | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory); | ||||
|     ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); | ||||
| 
 | ||||
|     bool ShouldWait(const Thread* thread) const override; | ||||
| 
 | ||||
|  | ||||
| @ -14,6 +14,7 @@ constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; | ||||
| constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; | ||||
| constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; | ||||
| constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; | ||||
| constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; | ||||
| constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104}; | ||||
| constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; | ||||
| constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; | ||||
|  | ||||
| @ -18,15 +18,20 @@ | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/core_timing_util.h" | ||||
| #include "core/device_memory.h" | ||||
| #include "core/hardware_properties.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory/memory_layout.h" | ||||
| #include "core/hle/kernel/memory/memory_manager.h" | ||||
| #include "core/hle/kernel/memory/slab_heap.h" | ||||
| #include "core/hle/kernel/physical_core.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/kernel/scheduler.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/kernel/synchronization.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/kernel/time_manager.h" | ||||
| @ -110,6 +115,7 @@ struct KernelCore::Impl { | ||||
| 
 | ||||
|         InitializePhysicalCores(); | ||||
|         InitializeSystemResourceLimit(kernel); | ||||
|         InitializeMemoryLayout(); | ||||
|         InitializeThreads(); | ||||
|         InitializePreemption(); | ||||
|     } | ||||
| @ -154,12 +160,17 @@ struct KernelCore::Impl { | ||||
|         system_resource_limit = ResourceLimit::Create(kernel); | ||||
| 
 | ||||
|         // If setting the default system values fails, then something seriously wrong has occurred.
 | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000) | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x100000000) | ||||
|                    .IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess()); | ||||
| 
 | ||||
|         if (!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0) || | ||||
|             !system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0x60000)) { | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void InitializeThreads() { | ||||
| @ -237,6 +248,57 @@ struct KernelCore::Impl { | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     void InitializeMemoryLayout() { | ||||
|         // Initialize memory layout
 | ||||
|         constexpr Memory::MemoryLayout layout{Memory::MemoryLayout::GetDefaultLayout()}; | ||||
|         constexpr std::size_t hid_size{0x40000}; | ||||
|         constexpr std::size_t font_size{0x1100000}; | ||||
|         constexpr std::size_t irs_size{0x8000}; | ||||
|         constexpr std::size_t time_size{0x1000}; | ||||
|         constexpr PAddr hid_addr{layout.System().StartAddress()}; | ||||
|         constexpr PAddr font_pa{layout.System().StartAddress() + hid_size}; | ||||
|         constexpr PAddr irs_addr{layout.System().StartAddress() + hid_size + font_size}; | ||||
|         constexpr PAddr time_addr{layout.System().StartAddress() + hid_size + font_size + irs_size}; | ||||
| 
 | ||||
|         // Initialize memory manager
 | ||||
|         memory_manager = std::make_unique<Memory::MemoryManager>(); | ||||
|         memory_manager->InitializeManager(Memory::MemoryManager::Pool::Application, | ||||
|                                           layout.Application().StartAddress(), | ||||
|                                           layout.Application().EndAddress()); | ||||
|         memory_manager->InitializeManager(Memory::MemoryManager::Pool::Applet, | ||||
|                                           layout.Applet().StartAddress(), | ||||
|                                           layout.Applet().EndAddress()); | ||||
|         memory_manager->InitializeManager(Memory::MemoryManager::Pool::System, | ||||
|                                           layout.System().StartAddress(), | ||||
|                                           layout.System().EndAddress()); | ||||
| 
 | ||||
|         hid_shared_mem = Kernel::SharedMemory::Create( | ||||
|             system.Kernel(), system.DeviceMemory(), nullptr, | ||||
|             {hid_addr, hid_size / Memory::PageSize}, Memory::MemoryPermission::None, | ||||
|             Memory::MemoryPermission::Read, hid_addr, hid_size, "HID:SharedMemory"); | ||||
|         font_shared_mem = Kernel::SharedMemory::Create( | ||||
|             system.Kernel(), system.DeviceMemory(), nullptr, | ||||
|             {font_pa, font_size / Memory::PageSize}, Memory::MemoryPermission::None, | ||||
|             Memory::MemoryPermission::Read, font_pa, font_size, "Font:SharedMemory"); | ||||
|         irs_shared_mem = Kernel::SharedMemory::Create( | ||||
|             system.Kernel(), system.DeviceMemory(), nullptr, | ||||
|             {irs_addr, irs_size / Memory::PageSize}, Memory::MemoryPermission::None, | ||||
|             Memory::MemoryPermission::Read, irs_addr, irs_size, "IRS:SharedMemory"); | ||||
|         time_shared_mem = Kernel::SharedMemory::Create( | ||||
|             system.Kernel(), system.DeviceMemory(), nullptr, | ||||
|             {time_addr, time_size / Memory::PageSize}, Memory::MemoryPermission::None, | ||||
|             Memory::MemoryPermission::Read, time_addr, time_size, "Time:SharedMemory"); | ||||
| 
 | ||||
|         // Allocate slab heaps
 | ||||
|         user_slab_heap_pages = std::make_unique<Memory::SlabHeap<Memory::Page>>(); | ||||
| 
 | ||||
|         // Initialize slab heaps
 | ||||
|         constexpr u64 user_slab_heap_size{0x3de000}; | ||||
|         user_slab_heap_pages->Initialize( | ||||
|             system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase), | ||||
|             user_slab_heap_size); | ||||
|     } | ||||
| 
 | ||||
|     std::atomic<u32> next_object_id{0}; | ||||
|     std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin}; | ||||
|     std::atomic<u64> next_user_process_id{Process::ProcessIDMin}; | ||||
| @ -271,6 +333,16 @@ struct KernelCore::Impl { | ||||
|     std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads; | ||||
|     std::mutex register_thread_mutex; | ||||
| 
 | ||||
|     // Kernel memory management
 | ||||
|     std::unique_ptr<Memory::MemoryManager> memory_manager; | ||||
|     std::unique_ptr<Memory::SlabHeap<Memory::Page>> user_slab_heap_pages; | ||||
| 
 | ||||
|     // Shared memory for services
 | ||||
|     std::shared_ptr<Kernel::SharedMemory> hid_shared_mem; | ||||
|     std::shared_ptr<Kernel::SharedMemory> font_shared_mem; | ||||
|     std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; | ||||
|     std::shared_ptr<Kernel::SharedMemory> time_shared_mem; | ||||
| 
 | ||||
|     // System context
 | ||||
|     Core::System& system; | ||||
| }; | ||||
| @ -437,4 +509,52 @@ Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const { | ||||
|     return impl->GetCurrentEmuThreadID(); | ||||
| } | ||||
| 
 | ||||
| Memory::MemoryManager& KernelCore::MemoryManager() { | ||||
|     return *impl->memory_manager; | ||||
| } | ||||
| 
 | ||||
| const Memory::MemoryManager& KernelCore::MemoryManager() const { | ||||
|     return *impl->memory_manager; | ||||
| } | ||||
| 
 | ||||
| Memory::SlabHeap<Memory::Page>& KernelCore::GetUserSlabHeapPages() { | ||||
|     return *impl->user_slab_heap_pages; | ||||
| } | ||||
| 
 | ||||
| const Memory::SlabHeap<Memory::Page>& KernelCore::GetUserSlabHeapPages() const { | ||||
|     return *impl->user_slab_heap_pages; | ||||
| } | ||||
| 
 | ||||
| Kernel::SharedMemory& KernelCore::GetHidSharedMem() { | ||||
|     return *impl->hid_shared_mem; | ||||
| } | ||||
| 
 | ||||
| const Kernel::SharedMemory& KernelCore::GetHidSharedMem() const { | ||||
|     return *impl->hid_shared_mem; | ||||
| } | ||||
| 
 | ||||
| Kernel::SharedMemory& KernelCore::GetFontSharedMem() { | ||||
|     return *impl->font_shared_mem; | ||||
| } | ||||
| 
 | ||||
| const Kernel::SharedMemory& KernelCore::GetFontSharedMem() const { | ||||
|     return *impl->font_shared_mem; | ||||
| } | ||||
| 
 | ||||
| Kernel::SharedMemory& KernelCore::GetIrsSharedMem() { | ||||
|     return *impl->irs_shared_mem; | ||||
| } | ||||
| 
 | ||||
| const Kernel::SharedMemory& KernelCore::GetIrsSharedMem() const { | ||||
|     return *impl->irs_shared_mem; | ||||
| } | ||||
| 
 | ||||
| Kernel::SharedMemory& KernelCore::GetTimeSharedMem() { | ||||
|     return *impl->time_shared_mem; | ||||
| } | ||||
| 
 | ||||
| const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const { | ||||
|     return *impl->time_shared_mem; | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -8,6 +8,7 @@ | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
| #include "core/hle/kernel/memory/memory_types.h" | ||||
| #include "core/hle/kernel/object.h" | ||||
| 
 | ||||
| namespace Core { | ||||
| @ -23,6 +24,12 @@ struct EventType; | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| namespace Memory { | ||||
| class MemoryManager; | ||||
| template <typename T> | ||||
| class SlabHeap; | ||||
| } // namespace Memory
 | ||||
| 
 | ||||
| class AddressArbiter; | ||||
| class ClientPort; | ||||
| class GlobalScheduler; | ||||
| @ -31,6 +38,7 @@ class PhysicalCore; | ||||
| class Process; | ||||
| class ResourceLimit; | ||||
| class Scheduler; | ||||
| class SharedMemory; | ||||
| class Synchronization; | ||||
| class Thread; | ||||
| class TimeManager; | ||||
| @ -147,6 +155,42 @@ public: | ||||
|     /// Register the current thread as a non CPU core thread.
 | ||||
|     void RegisterHostThread(); | ||||
| 
 | ||||
|     /// Gets the virtual memory manager for the kernel.
 | ||||
|     Memory::MemoryManager& MemoryManager(); | ||||
| 
 | ||||
|     /// Gets the virtual memory manager for the kernel.
 | ||||
|     const Memory::MemoryManager& MemoryManager() const; | ||||
| 
 | ||||
|     /// Gets the slab heap allocated for user space pages.
 | ||||
|     Memory::SlabHeap<Memory::Page>& GetUserSlabHeapPages(); | ||||
| 
 | ||||
|     /// Gets the slab heap allocated for user space pages.
 | ||||
|     const Memory::SlabHeap<Memory::Page>& GetUserSlabHeapPages() const; | ||||
| 
 | ||||
|     /// Gets the shared memory object for HID services.
 | ||||
|     Kernel::SharedMemory& GetHidSharedMem(); | ||||
| 
 | ||||
|     /// Gets the shared memory object for HID services.
 | ||||
|     const Kernel::SharedMemory& GetHidSharedMem() const; | ||||
| 
 | ||||
|     /// Gets the shared memory object for font services.
 | ||||
|     Kernel::SharedMemory& GetFontSharedMem(); | ||||
| 
 | ||||
|     /// Gets the shared memory object for font services.
 | ||||
|     const Kernel::SharedMemory& GetFontSharedMem() const; | ||||
| 
 | ||||
|     /// Gets the shared memory object for IRS services.
 | ||||
|     Kernel::SharedMemory& GetIrsSharedMem(); | ||||
| 
 | ||||
|     /// Gets the shared memory object for IRS services.
 | ||||
|     const Kernel::SharedMemory& GetIrsSharedMem() const; | ||||
| 
 | ||||
|     /// Gets the shared memory object for Time services.
 | ||||
|     Kernel::SharedMemory& GetTimeSharedMem(); | ||||
| 
 | ||||
|     /// Gets the shared memory object for Time services.
 | ||||
|     const Kernel::SharedMemory& GetTimeSharedMem() const; | ||||
| 
 | ||||
| private: | ||||
|     friend class Object; | ||||
|     friend class Process; | ||||
|  | ||||
							
								
								
									
										118
									
								
								src/core/hle/kernel/memory/address_space_info.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/core/hle/kernel/memory/address_space_info.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,118 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| // This file references various implementation details from Atmosphère, an open-source firmware for
 | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX.
 | ||||
| 
 | ||||
| #include <array> | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "core/hle/kernel/memory/address_space_info.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| enum : u64 { | ||||
|     Size_1_MB = 0x100000, | ||||
|     Size_2_MB = 2 * Size_1_MB, | ||||
|     Size_128_MB = 128 * Size_1_MB, | ||||
|     Size_1_GB = 0x40000000, | ||||
|     Size_2_GB = 2 * Size_1_GB, | ||||
|     Size_4_GB = 4 * Size_1_GB, | ||||
|     Size_6_GB = 6 * Size_1_GB, | ||||
|     Size_64_GB = 64 * Size_1_GB, | ||||
|     Size_512_GB = 512 * Size_1_GB, | ||||
|     Invalid = std::numeric_limits<u64>::max(), | ||||
| }; | ||||
| 
 | ||||
| // clang-format off
 | ||||
| constexpr std::array<AddressSpaceInfo, 13> AddressSpaceInfos{{ | ||||
|    { 32 /*bit_width*/, Size_2_MB   /*addr*/, Size_1_GB   - Size_2_MB   /*size*/, AddressSpaceInfo::Type::Is32Bit,    }, | ||||
|    { 32 /*bit_width*/, Size_1_GB   /*addr*/, Size_4_GB   - Size_1_GB   /*size*/, AddressSpaceInfo::Type::Small64Bit, }, | ||||
|    { 32 /*bit_width*/, Invalid     /*addr*/, Size_1_GB                 /*size*/, AddressSpaceInfo::Type::Heap,       }, | ||||
|    { 32 /*bit_width*/, Invalid     /*addr*/, Size_1_GB                 /*size*/, AddressSpaceInfo::Type::Alias,      }, | ||||
|    { 36 /*bit_width*/, Size_128_MB /*addr*/, Size_2_GB   - Size_128_MB /*size*/, AddressSpaceInfo::Type::Is32Bit,    }, | ||||
|    { 36 /*bit_width*/, Size_2_GB   /*addr*/, Size_64_GB  - Size_2_GB   /*size*/, AddressSpaceInfo::Type::Small64Bit, }, | ||||
|    { 36 /*bit_width*/, Invalid     /*addr*/, Size_6_GB                 /*size*/, AddressSpaceInfo::Type::Heap,       }, | ||||
|    { 36 /*bit_width*/, Invalid     /*addr*/, Size_6_GB                 /*size*/, AddressSpaceInfo::Type::Alias,      }, | ||||
|    { 39 /*bit_width*/, Size_128_MB /*addr*/, Size_512_GB - Size_128_MB /*size*/, AddressSpaceInfo::Type::Large64Bit, }, | ||||
|    { 39 /*bit_width*/, Invalid     /*addr*/, Size_64_GB                /*size*/, AddressSpaceInfo::Type::Is32Bit     }, | ||||
|    { 39 /*bit_width*/, Invalid     /*addr*/, Size_6_GB                 /*size*/, AddressSpaceInfo::Type::Heap,       }, | ||||
|    { 39 /*bit_width*/, Invalid     /*addr*/, Size_64_GB                /*size*/, AddressSpaceInfo::Type::Alias,      }, | ||||
|    { 39 /*bit_width*/, Invalid     /*addr*/, Size_2_GB                 /*size*/, AddressSpaceInfo::Type::Stack,      }, | ||||
| }}; | ||||
| // clang-format on
 | ||||
| 
 | ||||
| constexpr bool IsAllowedIndexForAddress(std::size_t index) { | ||||
|     return index < std::size(AddressSpaceInfos) && AddressSpaceInfos[index].GetAddress() != Invalid; | ||||
| } | ||||
| 
 | ||||
| constexpr std::size_t | ||||
|     AddressSpaceIndices32Bit[static_cast<std::size_t>(AddressSpaceInfo::Type::Count)]{ | ||||
|         0, 1, 0, 2, 0, 3, | ||||
|     }; | ||||
| 
 | ||||
| constexpr std::size_t | ||||
|     AddressSpaceIndices36Bit[static_cast<std::size_t>(AddressSpaceInfo::Type::Count)]{ | ||||
|         4, 5, 4, 6, 4, 7, | ||||
|     }; | ||||
| 
 | ||||
| constexpr std::size_t | ||||
|     AddressSpaceIndices39Bit[static_cast<std::size_t>(AddressSpaceInfo::Type::Count)]{ | ||||
|         9, 8, 8, 10, 12, 11, | ||||
|     }; | ||||
| 
 | ||||
| constexpr bool IsAllowed32BitType(AddressSpaceInfo::Type type) { | ||||
|     return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit && | ||||
|            type != AddressSpaceInfo::Type::Stack; | ||||
| } | ||||
| 
 | ||||
| constexpr bool IsAllowed36BitType(AddressSpaceInfo::Type type) { | ||||
|     return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit && | ||||
|            type != AddressSpaceInfo::Type::Stack; | ||||
| } | ||||
| 
 | ||||
| constexpr bool IsAllowed39BitType(AddressSpaceInfo::Type type) { | ||||
|     return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Small64Bit; | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, AddressSpaceInfo::Type type) { | ||||
|     const std::size_t index{static_cast<std::size_t>(type)}; | ||||
|     switch (width) { | ||||
|     case 32: | ||||
|         ASSERT(IsAllowed32BitType(type)); | ||||
|         ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[index])); | ||||
|         return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].GetAddress(); | ||||
|     case 36: | ||||
|         ASSERT(IsAllowed36BitType(type)); | ||||
|         ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[index])); | ||||
|         return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].GetAddress(); | ||||
|     case 39: | ||||
|         ASSERT(IsAllowed39BitType(type)); | ||||
|         ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index])); | ||||
|         return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].GetAddress(); | ||||
|     } | ||||
|     UNREACHABLE(); | ||||
| } | ||||
| 
 | ||||
| std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, AddressSpaceInfo::Type type) { | ||||
|     const std::size_t index{static_cast<std::size_t>(type)}; | ||||
|     switch (width) { | ||||
|     case 32: | ||||
|         ASSERT(IsAllowed32BitType(type)); | ||||
|         return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].GetSize(); | ||||
|     case 36: | ||||
|         ASSERT(IsAllowed36BitType(type)); | ||||
|         return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].GetSize(); | ||||
|     case 39: | ||||
|         ASSERT(IsAllowed39BitType(type)); | ||||
|         return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].GetSize(); | ||||
|     } | ||||
|     UNREACHABLE(); | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										54
									
								
								src/core/hle/kernel/memory/address_space_info.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/core/hle/kernel/memory/address_space_info.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| // This file references various implementation details from Atmosphère, an open-source firmware for
 | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| class AddressSpaceInfo final : NonCopyable { | ||||
| public: | ||||
|     enum class Type : u32 { | ||||
|         Is32Bit = 0, | ||||
|         Small64Bit = 1, | ||||
|         Large64Bit = 2, | ||||
|         Heap = 3, | ||||
|         Stack = 4, | ||||
|         Alias = 5, | ||||
|         Count, | ||||
|     }; | ||||
| 
 | ||||
| private: | ||||
|     std::size_t bit_width{}; | ||||
|     std::size_t addr{}; | ||||
|     std::size_t size{}; | ||||
|     Type type{}; | ||||
| 
 | ||||
| public: | ||||
|     static u64 GetAddressSpaceStart(std::size_t width, Type type); | ||||
|     static std::size_t GetAddressSpaceSize(std::size_t width, Type type); | ||||
| 
 | ||||
|     constexpr AddressSpaceInfo(std::size_t bit_width, std::size_t addr, std::size_t size, Type type) | ||||
|         : bit_width{bit_width}, addr{addr}, size{size}, type{type} {} | ||||
| 
 | ||||
|     constexpr std::size_t GetWidth() const { | ||||
|         return bit_width; | ||||
|     } | ||||
|     constexpr std::size_t GetAddress() const { | ||||
|         return addr; | ||||
|     } | ||||
|     constexpr std::size_t GetSize() const { | ||||
|         return size; | ||||
|     } | ||||
|     constexpr Type GetType() const { | ||||
|         return type; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										318
									
								
								src/core/hle/kernel/memory/memory_block.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								src/core/hle/kernel/memory/memory_block.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,318 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| // This file references various implementation details from Atmosphère, an open-source firmware for
 | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/alignment.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/memory/memory_types.h" | ||||
| #include "core/hle/kernel/svc_types.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| enum class MemoryState : u32 { | ||||
|     None = 0, | ||||
|     Mask = 0xFFFFFFFF, // TODO(bunnei): This should probable be 0xFF
 | ||||
|     All = ~None, | ||||
| 
 | ||||
|     FlagCanReprotect = (1 << 8), | ||||
|     FlagCanDebug = (1 << 9), | ||||
|     FlagCanUseIpc = (1 << 10), | ||||
|     FlagCanUseNonDeviceIpc = (1 << 11), | ||||
|     FlagCanUseNonSecureIpc = (1 << 12), | ||||
|     FlagMapped = (1 << 13), | ||||
|     FlagCode = (1 << 14), | ||||
|     FlagCanAlias = (1 << 15), | ||||
|     FlagCanCodeAlias = (1 << 16), | ||||
|     FlagCanTransfer = (1 << 17), | ||||
|     FlagCanQueryPhysical = (1 << 18), | ||||
|     FlagCanDeviceMap = (1 << 19), | ||||
|     FlagCanAlignedDeviceMap = (1 << 20), | ||||
|     FlagCanIpcUserBuffer = (1 << 21), | ||||
|     FlagReferenceCounted = (1 << 22), | ||||
|     FlagCanMapProcess = (1 << 23), | ||||
|     FlagCanChangeAttribute = (1 << 24), | ||||
|     FlagCanCodeMemory = (1 << 25), | ||||
| 
 | ||||
|     FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | | ||||
|                 FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical | | ||||
|                 FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer | | ||||
|                 FlagReferenceCounted | FlagCanChangeAttribute, | ||||
| 
 | ||||
|     FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | | ||||
|                 FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap | | ||||
|                 FlagCanAlignedDeviceMap | FlagReferenceCounted, | ||||
| 
 | ||||
|     FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap, | ||||
| 
 | ||||
|     Free = static_cast<u32>(Svc::MemoryState::Free), | ||||
|     Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped, | ||||
|     Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical, | ||||
|     Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess, | ||||
|     CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess | | ||||
|                FlagCanCodeMemory, | ||||
|     Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted, | ||||
|     Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory, | ||||
| 
 | ||||
|     AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess | | ||||
|                 FlagCanCodeAlias, | ||||
|     AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData | | ||||
|                     FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory, | ||||
| 
 | ||||
|     Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap | | ||||
|           FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | ||||
| 
 | ||||
|     Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap | | ||||
|             FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | ||||
| 
 | ||||
|     ThreadLocal = | ||||
|         static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, | ||||
| 
 | ||||
|     Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc | | ||||
|                  FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | | ||||
|                  FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | ||||
| 
 | ||||
|     SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc | | ||||
|                        FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | ||||
| 
 | ||||
|     SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | | ||||
|                  FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | ||||
| 
 | ||||
|     Inaccessible = static_cast<u32>(Svc::MemoryState::Inaccessible), | ||||
| 
 | ||||
|     NonSecureIpc = static_cast<u32>(Svc::MemoryState::NonSecureIpc) | FlagsMisc | | ||||
|                    FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, | ||||
| 
 | ||||
|     NonDeviceIpc = | ||||
|         static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc, | ||||
| 
 | ||||
|     Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped, | ||||
| 
 | ||||
|     GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped | | ||||
|                     FlagReferenceCounted | FlagCanDebug, | ||||
|     CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted, | ||||
| }; | ||||
| DECLARE_ENUM_FLAG_OPERATORS(MemoryState); | ||||
| 
 | ||||
| static_assert(static_cast<u32>(MemoryState::Free) == 0x00000000); | ||||
| static_assert(static_cast<u32>(MemoryState::Io) == 0x00002001); | ||||
| static_assert(static_cast<u32>(MemoryState::Static) == 0x00042002); | ||||
| static_assert(static_cast<u32>(MemoryState::Code) == 0x00DC7E03); | ||||
| static_assert(static_cast<u32>(MemoryState::CodeData) == 0x03FEBD04); | ||||
| static_assert(static_cast<u32>(MemoryState::Normal) == 0x037EBD05); | ||||
| static_assert(static_cast<u32>(MemoryState::Shared) == 0x00402006); | ||||
| static_assert(static_cast<u32>(MemoryState::AliasCode) == 0x00DD7E08); | ||||
| static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09); | ||||
| static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A); | ||||
| static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B); | ||||
| static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C); | ||||
| static_assert(static_cast<u32>(MemoryState::Transfered) == 0x015C3C0D); | ||||
| static_assert(static_cast<u32>(MemoryState::SharedTransfered) == 0x005C380E); | ||||
| static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F); | ||||
| static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010); | ||||
| static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811); | ||||
| static_assert(static_cast<u32>(MemoryState::NonDeviceIpc) == 0x004C2812); | ||||
| static_assert(static_cast<u32>(MemoryState::Kernel) == 0x00002013); | ||||
| static_assert(static_cast<u32>(MemoryState::GeneratedCode) == 0x00402214); | ||||
| static_assert(static_cast<u32>(MemoryState::CodeOut) == 0x00402015); | ||||
| 
 | ||||
| enum class MemoryPermission : u8 { | ||||
|     None = 0, | ||||
|     Mask = static_cast<u8>(~None), | ||||
| 
 | ||||
|     Read = 1 << 0, | ||||
|     Write = 1 << 1, | ||||
|     Execute = 1 << 2, | ||||
| 
 | ||||
|     ReadAndWrite = Read | Write, | ||||
|     ReadAndExecute = Read | Execute, | ||||
| 
 | ||||
|     UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | | ||||
|                                Svc::MemoryPermission::Execute), | ||||
| }; | ||||
| DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); | ||||
| 
 | ||||
| enum class MemoryAttribute : u8 { | ||||
|     None = 0x00, | ||||
|     Mask = 0x7F, | ||||
|     All = Mask, | ||||
|     DontCareMask = 0x80, | ||||
| 
 | ||||
|     Locked = static_cast<u8>(Svc::MemoryAttribute::Locked), | ||||
|     IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked), | ||||
|     DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared), | ||||
|     Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached), | ||||
| 
 | ||||
|     IpcAndDeviceMapped = IpcLocked | DeviceShared, | ||||
|     LockedAndIpcLocked = Locked | IpcLocked, | ||||
|     DeviceSharedAndUncached = DeviceShared | Uncached | ||||
| }; | ||||
| DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute); | ||||
| 
 | ||||
| static_assert((static_cast<u8>(MemoryAttribute::Mask) & | ||||
|                static_cast<u8>(MemoryAttribute::DontCareMask)) == 0); | ||||
| 
 | ||||
| struct MemoryInfo { | ||||
|     VAddr addr{}; | ||||
|     std::size_t size{}; | ||||
|     MemoryState state{}; | ||||
|     MemoryPermission perm{}; | ||||
|     MemoryAttribute attribute{}; | ||||
|     MemoryPermission original_perm{}; | ||||
|     u16 ipc_lock_count{}; | ||||
|     u16 device_use_count{}; | ||||
| 
 | ||||
|     constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { | ||||
|         return { | ||||
|             addr, | ||||
|             size, | ||||
|             static_cast<Svc::MemoryState>(state & MemoryState::Mask), | ||||
|             static_cast<Svc::MemoryAttribute>(attribute & MemoryAttribute::Mask), | ||||
|             static_cast<Svc::MemoryPermission>(perm & MemoryPermission::UserMask), | ||||
|             ipc_lock_count, | ||||
|             device_use_count, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     constexpr VAddr GetAddress() const { | ||||
|         return addr; | ||||
|     } | ||||
|     constexpr std::size_t GetSize() const { | ||||
|         return size; | ||||
|     } | ||||
|     constexpr std::size_t GetNumPages() const { | ||||
|         return GetSize() / PageSize; | ||||
|     } | ||||
|     constexpr VAddr GetEndAddress() const { | ||||
|         return GetAddress() + GetSize(); | ||||
|     } | ||||
|     constexpr VAddr GetLastAddress() const { | ||||
|         return GetEndAddress() - 1; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class MemoryBlock final { | ||||
|     friend class MemoryBlockManager; | ||||
| 
 | ||||
| private: | ||||
|     VAddr addr{}; | ||||
|     std::size_t num_pages{}; | ||||
|     MemoryState state{MemoryState::None}; | ||||
|     u16 ipc_lock_count{}; | ||||
|     u16 device_use_count{}; | ||||
|     MemoryPermission perm{MemoryPermission::None}; | ||||
|     MemoryPermission original_perm{MemoryPermission::None}; | ||||
|     MemoryAttribute attribute{MemoryAttribute::None}; | ||||
| 
 | ||||
| public: | ||||
|     static constexpr int Compare(const MemoryBlock& lhs, const MemoryBlock& rhs) { | ||||
|         if (lhs.GetAddress() < rhs.GetAddress()) { | ||||
|             return -1; | ||||
|         } else if (lhs.GetAddress() <= rhs.GetLastAddress()) { | ||||
|             return 0; | ||||
|         } else { | ||||
|             return 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| public: | ||||
|     constexpr MemoryBlock() = default; | ||||
|     constexpr MemoryBlock(VAddr addr, std::size_t num_pages, MemoryState state, | ||||
|                           MemoryPermission perm, MemoryAttribute attribute) | ||||
|         : addr{addr}, num_pages(num_pages), state{state}, perm{perm}, attribute{attribute} {} | ||||
| 
 | ||||
|     constexpr VAddr GetAddress() const { | ||||
|         return addr; | ||||
|     } | ||||
| 
 | ||||
|     constexpr std::size_t GetNumPages() const { | ||||
|         return num_pages; | ||||
|     } | ||||
| 
 | ||||
|     constexpr std::size_t GetSize() const { | ||||
|         return GetNumPages() * PageSize; | ||||
|     } | ||||
| 
 | ||||
|     constexpr VAddr GetEndAddress() const { | ||||
|         return GetAddress() + GetSize(); | ||||
|     } | ||||
| 
 | ||||
|     constexpr VAddr GetLastAddress() const { | ||||
|         return GetEndAddress() - 1; | ||||
|     } | ||||
| 
 | ||||
|     constexpr MemoryInfo GetMemoryInfo() const { | ||||
|         return { | ||||
|             GetAddress(), GetSize(),     state,          perm, | ||||
|             attribute,    original_perm, ipc_lock_count, device_use_count, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const { | ||||
|         constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask | | ||||
|                                                       MemoryAttribute::IpcLocked | | ||||
|                                                       MemoryAttribute::DeviceShared}; | ||||
|         return state == s && perm == p && | ||||
|                (attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask); | ||||
|     } | ||||
| 
 | ||||
|     constexpr bool HasSameProperties(const MemoryBlock& rhs) const { | ||||
|         return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm && | ||||
|                attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count && | ||||
|                device_use_count == rhs.device_use_count; | ||||
|     } | ||||
| 
 | ||||
|     constexpr bool Contains(VAddr start) const { | ||||
|         return GetAddress() <= start && start <= GetEndAddress(); | ||||
|     } | ||||
| 
 | ||||
|     constexpr void Add(std::size_t count) { | ||||
|         ASSERT(count > 0); | ||||
|         ASSERT(GetAddress() + count * PageSize - 1 < GetEndAddress() + count * PageSize - 1); | ||||
| 
 | ||||
|         num_pages += count; | ||||
|     } | ||||
| 
 | ||||
|     constexpr void Update(MemoryState new_state, MemoryPermission new_perm, | ||||
|                           MemoryAttribute new_attribute) { | ||||
|         ASSERT(original_perm == MemoryPermission::None); | ||||
|         ASSERT((attribute & MemoryAttribute::IpcLocked) == MemoryAttribute::None); | ||||
| 
 | ||||
|         state = new_state; | ||||
|         perm = new_perm; | ||||
| 
 | ||||
|         // TODO(bunnei): Is this right?
 | ||||
|         attribute = static_cast<MemoryAttribute>( | ||||
|             new_attribute /*| (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared))*/); | ||||
|     } | ||||
| 
 | ||||
|     constexpr MemoryBlock Split(VAddr split_addr) { | ||||
|         ASSERT(GetAddress() < split_addr); | ||||
|         ASSERT(Contains(split_addr)); | ||||
|         ASSERT(Common::IsAligned(split_addr, PageSize)); | ||||
| 
 | ||||
|         MemoryBlock block; | ||||
|         block.addr = addr; | ||||
|         block.num_pages = (split_addr - GetAddress()) / PageSize; | ||||
|         block.state = state; | ||||
|         block.ipc_lock_count = ipc_lock_count; | ||||
|         block.device_use_count = device_use_count; | ||||
|         block.perm = perm; | ||||
|         block.original_perm = original_perm; | ||||
|         block.attribute = attribute; | ||||
| 
 | ||||
|         addr = split_addr; | ||||
|         num_pages -= block.num_pages; | ||||
| 
 | ||||
|         return block; | ||||
|     } | ||||
| }; | ||||
| static_assert(std::is_trivially_destructible<MemoryBlock>::value); | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										190
									
								
								src/core/hle/kernel/memory/memory_block_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								src/core/hle/kernel/memory/memory_block_manager.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,190 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "core/hle/kernel/memory/memory_block_manager.h" | ||||
| #include "core/hle/kernel/memory/memory_types.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| MemoryBlockManager::MemoryBlockManager(VAddr start_addr, VAddr end_addr) | ||||
|     : start_addr{start_addr}, end_addr{end_addr} { | ||||
|     const u64 num_pages{(end_addr - start_addr) / PageSize}; | ||||
|     memory_block_tree.emplace_back(start_addr, num_pages, MemoryState::Free, MemoryPermission::None, | ||||
|                                    MemoryAttribute::None); | ||||
| } | ||||
| 
 | ||||
| MemoryBlockManager::iterator MemoryBlockManager::FindIterator(VAddr addr) { | ||||
|     auto node{memory_block_tree.begin()}; | ||||
|     while (node != end()) { | ||||
|         const VAddr end_addr{node->GetNumPages() * PageSize + node->GetAddress()}; | ||||
|         if (node->GetAddress() <= addr && end_addr - 1 >= addr) { | ||||
|             return node; | ||||
|         } | ||||
|         node = std::next(node); | ||||
|     } | ||||
|     return end(); | ||||
| } | ||||
| 
 | ||||
| VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages, | ||||
|                                        std::size_t num_pages, std::size_t align, std::size_t offset, | ||||
|                                        std::size_t guard_pages) { | ||||
|     if (num_pages == 0) { | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     const VAddr region_end{region_start + region_num_pages * PageSize}; | ||||
|     const VAddr region_last{region_end - 1}; | ||||
|     for (auto it{FindIterator(region_start)}; it != memory_block_tree.cend(); it++) { | ||||
|         const auto info{it->GetMemoryInfo()}; | ||||
|         if (region_last < info.GetAddress()) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         if (info.state != MemoryState::Free) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         VAddr area{(info.GetAddress() <= region_start) ? region_start : info.GetAddress()}; | ||||
|         area += guard_pages * PageSize; | ||||
| 
 | ||||
|         const VAddr offset_area{Common::AlignDown(area, align) + offset}; | ||||
|         area = (area <= offset_area) ? offset_area : offset_area + align; | ||||
| 
 | ||||
|         const VAddr area_end{area + num_pages * PageSize + guard_pages * PageSize}; | ||||
|         const VAddr area_last{area_end - 1}; | ||||
| 
 | ||||
|         if (info.GetAddress() <= area && area < area_last && area_last <= region_last && | ||||
|             area_last <= info.GetLastAddress()) { | ||||
|             return area; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState prev_state, | ||||
|                                 MemoryPermission prev_perm, MemoryAttribute prev_attribute, | ||||
|                                 MemoryState state, MemoryPermission perm, | ||||
|                                 MemoryAttribute attribute) { | ||||
|     const std::size_t prev_count{memory_block_tree.size()}; | ||||
|     const VAddr end_addr{addr + num_pages * PageSize}; | ||||
|     iterator node{memory_block_tree.begin()}; | ||||
| 
 | ||||
|     prev_attribute |= MemoryAttribute::IpcAndDeviceMapped; | ||||
| 
 | ||||
|     while (node != memory_block_tree.end()) { | ||||
|         MemoryBlock* block{&(*node)}; | ||||
|         iterator next_node{std::next(node)}; | ||||
|         const VAddr cur_addr{block->GetAddress()}; | ||||
|         const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; | ||||
| 
 | ||||
|         if (addr < cur_end_addr && cur_addr < end_addr) { | ||||
|             if (!block->HasProperties(prev_state, prev_perm, prev_attribute)) { | ||||
|                 node = next_node; | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             iterator new_node{node}; | ||||
|             if (addr > cur_addr) { | ||||
|                 memory_block_tree.insert(node, block->Split(addr)); | ||||
|             } | ||||
| 
 | ||||
|             if (end_addr < cur_end_addr) { | ||||
|                 new_node = memory_block_tree.insert(node, block->Split(end_addr)); | ||||
|             } | ||||
| 
 | ||||
|             new_node->Update(state, perm, attribute); | ||||
| 
 | ||||
|             MergeAdjacent(new_node, next_node); | ||||
|         } | ||||
| 
 | ||||
|         if (cur_end_addr - 1 >= end_addr - 1) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         node = next_node; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState state, | ||||
|                                 MemoryPermission perm, MemoryAttribute attribute) { | ||||
|     const std::size_t prev_count{memory_block_tree.size()}; | ||||
|     const VAddr end_addr{addr + num_pages * PageSize}; | ||||
|     iterator node{memory_block_tree.begin()}; | ||||
| 
 | ||||
|     while (node != memory_block_tree.end()) { | ||||
|         MemoryBlock* block{&(*node)}; | ||||
|         iterator next_node{std::next(node)}; | ||||
|         const VAddr cur_addr{block->GetAddress()}; | ||||
|         const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; | ||||
| 
 | ||||
|         if (addr < cur_end_addr && cur_addr < end_addr) { | ||||
|             iterator new_node{node}; | ||||
| 
 | ||||
|             if (addr > cur_addr) { | ||||
|                 memory_block_tree.insert(node, block->Split(addr)); | ||||
|             } | ||||
| 
 | ||||
|             if (end_addr < cur_end_addr) { | ||||
|                 new_node = memory_block_tree.insert(node, block->Split(end_addr)); | ||||
|             } | ||||
| 
 | ||||
|             new_node->Update(state, perm, attribute); | ||||
| 
 | ||||
|             MergeAdjacent(new_node, next_node); | ||||
|         } | ||||
| 
 | ||||
|         if (cur_end_addr - 1 >= end_addr - 1) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         node = next_node; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) { | ||||
|     const_iterator it{FindIterator(start)}; | ||||
|     MemoryInfo info{}; | ||||
|     do { | ||||
|         info = it->GetMemoryInfo(); | ||||
|         func(info); | ||||
|         it = std::next(it); | ||||
|     } while (info.addr + info.size - 1 < end - 1 && it != cend()); | ||||
| } | ||||
| 
 | ||||
| void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) { | ||||
|     MemoryBlock* block{&(*it)}; | ||||
| 
 | ||||
|     auto EraseIt = [&](const iterator it_to_erase) { | ||||
|         if (next_it == it_to_erase) { | ||||
|             next_it = std::next(next_it); | ||||
|         } | ||||
|         memory_block_tree.erase(it_to_erase); | ||||
|     }; | ||||
| 
 | ||||
|     if (it != memory_block_tree.begin()) { | ||||
|         MemoryBlock* prev{&(*std::prev(it))}; | ||||
| 
 | ||||
|         if (block->HasSameProperties(*prev)) { | ||||
|             const iterator prev_it{std::prev(it)}; | ||||
| 
 | ||||
|             prev->Add(block->GetNumPages()); | ||||
|             EraseIt(it); | ||||
| 
 | ||||
|             it = prev_it; | ||||
|             block = prev; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (it != cend()) { | ||||
|         const MemoryBlock* const next{&(*std::next(it))}; | ||||
| 
 | ||||
|         if (block->HasSameProperties(*next)) { | ||||
|             block->Add(next->GetNumPages()); | ||||
|             EraseIt(std::next(it)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										64
									
								
								src/core/hle/kernel/memory/memory_block_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/core/hle/kernel/memory/memory_block_manager.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <list> | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/memory/memory_block.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| class MemoryBlockManager final { | ||||
| public: | ||||
|     using MemoryBlockTree = std::list<MemoryBlock>; | ||||
|     using iterator = MemoryBlockTree::iterator; | ||||
|     using const_iterator = MemoryBlockTree::const_iterator; | ||||
| 
 | ||||
| public: | ||||
|     MemoryBlockManager(VAddr start_addr, VAddr end_addr); | ||||
| 
 | ||||
|     iterator end() { | ||||
|         return memory_block_tree.end(); | ||||
|     } | ||||
|     const_iterator end() const { | ||||
|         return memory_block_tree.end(); | ||||
|     } | ||||
|     const_iterator cend() const { | ||||
|         return memory_block_tree.cend(); | ||||
|     } | ||||
| 
 | ||||
|     iterator FindIterator(VAddr addr); | ||||
| 
 | ||||
|     VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, | ||||
|                        std::size_t align, std::size_t offset, std::size_t guard_pages); | ||||
| 
 | ||||
|     void Update(VAddr addr, std::size_t num_pages, MemoryState prev_state, | ||||
|                 MemoryPermission prev_perm, MemoryAttribute prev_attribute, MemoryState state, | ||||
|                 MemoryPermission perm, MemoryAttribute attribute); | ||||
| 
 | ||||
|     void Update(VAddr addr, std::size_t num_pages, MemoryState state, | ||||
|                 MemoryPermission perm = MemoryPermission::None, | ||||
|                 MemoryAttribute attribute = MemoryAttribute::None); | ||||
| 
 | ||||
|     using IterateFunc = std::function<void(const MemoryInfo&)>; | ||||
|     void IterateForRange(VAddr start, VAddr end, IterateFunc&& func); | ||||
| 
 | ||||
|     MemoryBlock& FindBlock(VAddr addr) { | ||||
|         return *FindIterator(addr); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     void MergeAdjacent(iterator it, iterator& next_it); | ||||
| 
 | ||||
|     const VAddr start_addr; | ||||
|     const VAddr end_addr; | ||||
| 
 | ||||
|     MemoryBlockTree memory_block_tree; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										73
									
								
								src/core/hle/kernel/memory/memory_layout.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/core/hle/kernel/memory/memory_layout.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| // Copyright 2020 yuzu 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::Memory { | ||||
| 
 | ||||
| class MemoryRegion final { | ||||
|     friend class MemoryLayout; | ||||
| 
 | ||||
| public: | ||||
|     constexpr PAddr StartAddress() const { | ||||
|         return start_address; | ||||
|     } | ||||
| 
 | ||||
|     constexpr PAddr EndAddress() const { | ||||
|         return end_address; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     constexpr MemoryRegion() = default; | ||||
|     constexpr MemoryRegion(PAddr start_address, PAddr end_address) | ||||
|         : start_address{start_address}, end_address{end_address} {} | ||||
| 
 | ||||
|     const PAddr start_address{}; | ||||
|     const PAddr end_address{}; | ||||
| }; | ||||
| 
 | ||||
| class MemoryLayout final { | ||||
| public: | ||||
|     constexpr const MemoryRegion& Application() const { | ||||
|         return application; | ||||
|     } | ||||
| 
 | ||||
|     constexpr const MemoryRegion& Applet() const { | ||||
|         return applet; | ||||
|     } | ||||
| 
 | ||||
|     constexpr const MemoryRegion& System() const { | ||||
|         return system; | ||||
|     } | ||||
| 
 | ||||
|     static constexpr MemoryLayout GetDefaultLayout() { | ||||
|         constexpr std::size_t application_size{0xcd500000}; | ||||
|         constexpr std::size_t applet_size{0x1fb00000}; | ||||
|         constexpr PAddr application_start_address{Core::DramMemoryMap::End - application_size}; | ||||
|         constexpr PAddr application_end_address{Core::DramMemoryMap::End}; | ||||
|         constexpr PAddr applet_start_address{application_start_address - applet_size}; | ||||
|         constexpr PAddr applet_end_address{applet_start_address + applet_size}; | ||||
|         constexpr PAddr system_start_address{Core::DramMemoryMap::SlabHeapEnd}; | ||||
|         constexpr PAddr system_end_address{applet_start_address}; | ||||
|         return {application_start_address, application_end_address, applet_start_address, | ||||
|                 applet_end_address,        system_start_address,    system_end_address}; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     constexpr MemoryLayout(PAddr application_start_address, std::size_t application_size, | ||||
|                            PAddr applet_start_address, std::size_t applet_size, | ||||
|                            PAddr system_start_address, std::size_t system_size) | ||||
|         : application{application_start_address, application_size}, | ||||
|           applet{applet_start_address, applet_size}, system{system_start_address, system_size} {} | ||||
| 
 | ||||
|     const MemoryRegion application; | ||||
|     const MemoryRegion applet; | ||||
|     const MemoryRegion system; | ||||
| 
 | ||||
|     const PAddr start_address{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										176
									
								
								src/core/hle/kernel/memory/memory_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/core/hle/kernel/memory/memory_manager.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #include "common/alignment.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/scope_exit.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/memory/memory_manager.h" | ||||
| #include "core/hle/kernel/memory/page_linked_list.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| std::size_t MemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) { | ||||
|     const auto size{end_address - start_address}; | ||||
| 
 | ||||
|     // Calculate metadata sizes
 | ||||
|     const auto ref_count_size{(size / PageSize) * sizeof(u16)}; | ||||
|     const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)}; | ||||
|     const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)}; | ||||
|     const auto page_heap_size{PageHeap::CalculateMetadataOverheadSize(size)}; | ||||
|     const auto total_metadata_size{manager_size + page_heap_size}; | ||||
|     ASSERT(manager_size <= total_metadata_size); | ||||
|     ASSERT(Common::IsAligned(total_metadata_size, PageSize)); | ||||
| 
 | ||||
|     // Setup region
 | ||||
|     pool = new_pool; | ||||
| 
 | ||||
|     // Initialize the manager's KPageHeap
 | ||||
|     heap.Initialize(start_address, size, page_heap_size); | ||||
| 
 | ||||
|     // Free the memory to the heap
 | ||||
|     heap.Free(start_address, size / PageSize); | ||||
| 
 | ||||
|     // Update the heap's used size
 | ||||
|     heap.UpdateUsedSize(); | ||||
| 
 | ||||
|     return total_metadata_size; | ||||
| } | ||||
| 
 | ||||
| void MemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) { | ||||
|     ASSERT(pool < Pool::Count); | ||||
|     managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address); | ||||
| } | ||||
| 
 | ||||
| VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool, | ||||
|                                         Direction dir) { | ||||
|     // Early return if we're allocating no pages
 | ||||
|     if (num_pages == 0) { | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     // Lock the pool that we're allocating from
 | ||||
|     const auto pool_index{static_cast<std::size_t>(pool)}; | ||||
|     std::lock_guard lock{pool_locks[pool_index]}; | ||||
| 
 | ||||
|     // Choose a heap based on our page size request
 | ||||
|     const s32 heap_index{PageHeap::GetAlignedBlockIndex(num_pages, align_pages)}; | ||||
| 
 | ||||
|     // Loop, trying to iterate from each block
 | ||||
|     // TODO (bunnei): Support multiple managers
 | ||||
|     Impl& chosen_manager{managers[pool_index]}; | ||||
|     VAddr allocated_block{chosen_manager.AllocateBlock(heap_index)}; | ||||
| 
 | ||||
|     // If we failed to allocate, quit now
 | ||||
|     if (!allocated_block) { | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     // If we allocated more than we need, free some
 | ||||
|     const auto allocated_pages{PageHeap::GetBlockNumPages(heap_index)}; | ||||
|     if (allocated_pages > num_pages) { | ||||
|         chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages); | ||||
|     } | ||||
| 
 | ||||
|     return allocated_block; | ||||
| } | ||||
| 
 | ||||
| ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool, | ||||
|                                    Direction dir) { | ||||
|     ASSERT(page_list.GetNumPages() == 0); | ||||
| 
 | ||||
|     // Early return if we're allocating no pages
 | ||||
|     if (num_pages == 0) { | ||||
|         return RESULT_SUCCESS; | ||||
|     } | ||||
| 
 | ||||
|     // Lock the pool that we're allocating from
 | ||||
|     const auto pool_index{static_cast<std::size_t>(pool)}; | ||||
|     std::lock_guard lock{pool_locks[pool_index]}; | ||||
| 
 | ||||
|     // Choose a heap based on our page size request
 | ||||
|     const s32 heap_index{PageHeap::GetBlockIndex(num_pages)}; | ||||
|     if (heap_index < 0) { | ||||
|         return ERR_OUT_OF_MEMORY; | ||||
|     } | ||||
| 
 | ||||
|     // TODO (bunnei): Support multiple managers
 | ||||
|     Impl& chosen_manager{managers[pool_index]}; | ||||
| 
 | ||||
|     // Ensure that we don't leave anything un-freed
 | ||||
|     auto group_guard = detail::ScopeExit([&] { | ||||
|         for (const auto& it : page_list.Nodes()) { | ||||
|             const auto num_pages{std::min( | ||||
|                 it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; | ||||
|             chosen_manager.Free(it.GetAddress(), num_pages); | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     // Keep allocating until we've allocated all our pages
 | ||||
|     for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) { | ||||
|         const auto pages_per_alloc{PageHeap::GetBlockNumPages(index)}; | ||||
| 
 | ||||
|         while (num_pages >= pages_per_alloc) { | ||||
|             // Allocate a block
 | ||||
|             VAddr allocated_block{chosen_manager.AllocateBlock(index)}; | ||||
|             if (!allocated_block) { | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             // Safely add it to our group
 | ||||
|             { | ||||
|                 auto block_guard = detail::ScopeExit( | ||||
|                     [&] { chosen_manager.Free(allocated_block, pages_per_alloc); }); | ||||
| 
 | ||||
|                 if (const ResultCode result{page_list.AddBlock(allocated_block, pages_per_alloc)}; | ||||
|                     result.IsError()) { | ||||
|                     return result; | ||||
|                 } | ||||
| 
 | ||||
|                 block_guard.Cancel(); | ||||
|             } | ||||
| 
 | ||||
|             num_pages -= pages_per_alloc; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Only succeed if we allocated as many pages as we wanted
 | ||||
|     ASSERT(num_pages >= 0); | ||||
|     if (num_pages) { | ||||
|         return ERR_OUT_OF_MEMORY; | ||||
|     } | ||||
| 
 | ||||
|     // We succeeded!
 | ||||
|     group_guard.Cancel(); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool, | ||||
|                                Direction dir) { | ||||
|     // Early return if we're freeing no pages
 | ||||
|     if (!num_pages) { | ||||
|         return RESULT_SUCCESS; | ||||
|     } | ||||
| 
 | ||||
|     // Lock the pool that we're freeing from
 | ||||
|     const auto pool_index{static_cast<std::size_t>(pool)}; | ||||
|     std::lock_guard lock{pool_locks[pool_index]}; | ||||
| 
 | ||||
|     // TODO (bunnei): Support multiple managers
 | ||||
|     Impl& chosen_manager{managers[pool_index]}; | ||||
| 
 | ||||
|     // Free all of the pages
 | ||||
|     for (const auto& it : page_list.Nodes()) { | ||||
|         const auto num_pages{std::min( | ||||
|             it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; | ||||
|         chosen_manager.Free(it.GetAddress(), num_pages); | ||||
|     } | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										97
									
								
								src/core/hle/kernel/memory/memory_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/core/hle/kernel/memory/memory_manager.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/memory/page_heap.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| class PageLinkedList; | ||||
| 
 | ||||
| class MemoryManager final : NonCopyable { | ||||
| public: | ||||
|     enum class Pool : u32 { | ||||
|         Application = 0, | ||||
|         Applet = 1, | ||||
|         System = 2, | ||||
|         SystemNonSecure = 3, | ||||
| 
 | ||||
|         Count, | ||||
| 
 | ||||
|         Shift = 4, | ||||
|         Mask = (0xF << Shift), | ||||
|     }; | ||||
| 
 | ||||
|     enum class Direction : u32 { | ||||
|         FromFront = 0, | ||||
|         FromBack = 1, | ||||
| 
 | ||||
|         Shift = 0, | ||||
|         Mask = (0xF << Shift), | ||||
|     }; | ||||
| 
 | ||||
|     MemoryManager() = default; | ||||
| 
 | ||||
|     constexpr std::size_t GetSize(Pool pool) const { | ||||
|         return managers[static_cast<std::size_t>(pool)].GetSize(); | ||||
|     } | ||||
| 
 | ||||
|     void InitializeManager(Pool pool, u64 start_address, u64 end_address); | ||||
|     VAddr AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool, | ||||
|                              Direction dir = Direction::FromFront); | ||||
|     ResultCode Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool, | ||||
|                         Direction dir = Direction::FromFront); | ||||
|     ResultCode Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool, | ||||
|                     Direction dir = Direction::FromFront); | ||||
| 
 | ||||
|     static constexpr std::size_t MaxManagerCount = 10; | ||||
| 
 | ||||
| private: | ||||
|     class Impl final : NonCopyable { | ||||
|     private: | ||||
|         using RefCount = u16; | ||||
| 
 | ||||
|     private: | ||||
|         PageHeap heap; | ||||
|         Pool pool{}; | ||||
| 
 | ||||
|     public: | ||||
|         Impl() = default; | ||||
| 
 | ||||
|         std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address); | ||||
| 
 | ||||
|         VAddr AllocateBlock(s32 index) { | ||||
|             return heap.AllocateBlock(index); | ||||
|         } | ||||
| 
 | ||||
|         void Free(VAddr addr, std::size_t num_pages) { | ||||
|             heap.Free(addr, num_pages); | ||||
|         } | ||||
| 
 | ||||
|         constexpr std::size_t GetSize() const { | ||||
|             return heap.GetSize(); | ||||
|         } | ||||
| 
 | ||||
|         constexpr VAddr GetAddress() const { | ||||
|             return heap.GetAddress(); | ||||
|         } | ||||
| 
 | ||||
|         constexpr VAddr GetEndAddress() const { | ||||
|             return heap.GetEndAddress(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| private: | ||||
|     std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks; | ||||
|     std::array<Impl, MaxManagerCount> managers; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										18
									
								
								src/core/hle/kernel/memory/memory_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/core/hle/kernel/memory/memory_types.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| constexpr std::size_t PageBits{12}; | ||||
| constexpr std::size_t PageSize{1 << PageBits}; | ||||
| 
 | ||||
| using Page = std::array<u8, PageSize>; | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										119
									
								
								src/core/hle/kernel/memory/page_heap.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/core/hle/kernel/memory/page_heap.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,119 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| // This file references various implementation details from Atmosphère, an open-source firmware for
 | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX.
 | ||||
| 
 | ||||
| #include "core/core.h" | ||||
| #include "core/hle/kernel/memory/page_heap.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| void PageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) { | ||||
|     // Check our assumptions
 | ||||
|     ASSERT(Common::IsAligned((address), PageSize)); | ||||
|     ASSERT(Common::IsAligned(size, PageSize)); | ||||
| 
 | ||||
|     // Set our members
 | ||||
|     heap_address = address; | ||||
|     heap_size = size; | ||||
| 
 | ||||
|     // Setup bitmaps
 | ||||
|     metadata.resize(metadata_size / sizeof(u64)); | ||||
|     u64* cur_bitmap_storage{metadata.data()}; | ||||
|     for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { | ||||
|         const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; | ||||
|         const std::size_t next_block_shift{ | ||||
|             (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; | ||||
|         cur_bitmap_storage = blocks[i].Initialize(heap_address, heap_size, cur_block_shift, | ||||
|                                                   next_block_shift, cur_bitmap_storage); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| VAddr PageHeap::AllocateBlock(s32 index) { | ||||
|     const std::size_t needed_size{blocks[index].GetSize()}; | ||||
| 
 | ||||
|     for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) { | ||||
|         if (const VAddr addr{blocks[i].PopBlock()}; addr) { | ||||
|             if (const std::size_t allocated_size{blocks[i].GetSize()}; | ||||
|                 allocated_size > needed_size) { | ||||
|                 Free(addr + needed_size, (allocated_size - needed_size) / PageSize); | ||||
|             } | ||||
|             return addr; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| void PageHeap::FreeBlock(VAddr block, s32 index) { | ||||
|     do { | ||||
|         block = blocks[index++].PushBlock(block); | ||||
|     } while (block != 0); | ||||
| } | ||||
| 
 | ||||
| void PageHeap::Free(VAddr addr, std::size_t num_pages) { | ||||
|     // Freeing no pages is a no-op
 | ||||
|     if (num_pages == 0) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Find the largest block size that we can free, and free as many as possible
 | ||||
|     s32 big_index{static_cast<s32>(MemoryBlockPageShifts.size()) - 1}; | ||||
|     const VAddr start{addr}; | ||||
|     const VAddr end{(num_pages * PageSize) + addr}; | ||||
|     VAddr before_start{start}; | ||||
|     VAddr before_end{start}; | ||||
|     VAddr after_start{end}; | ||||
|     VAddr after_end{end}; | ||||
|     while (big_index >= 0) { | ||||
|         const std::size_t block_size{blocks[big_index].GetSize()}; | ||||
|         const VAddr big_start{Common::AlignUp((start), block_size)}; | ||||
|         const VAddr big_end{Common::AlignDown((end), block_size)}; | ||||
|         if (big_start < big_end) { | ||||
|             // Free as many big blocks as we can
 | ||||
|             for (auto block{big_start}; block < big_end; block += block_size) { | ||||
|                 FreeBlock(block, big_index); | ||||
|             } | ||||
|             before_end = big_start; | ||||
|             after_start = big_end; | ||||
|             break; | ||||
|         } | ||||
|         big_index--; | ||||
|     } | ||||
|     ASSERT(big_index >= 0); | ||||
| 
 | ||||
|     // Free space before the big blocks
 | ||||
|     for (s32 i{big_index - 1}; i >= 0; i--) { | ||||
|         const std::size_t block_size{blocks[i].GetSize()}; | ||||
|         while (before_start + block_size <= before_end) { | ||||
|             before_end -= block_size; | ||||
|             FreeBlock(before_end, i); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Free space after the big blocks
 | ||||
|     for (s32 i{big_index - 1}; i >= 0; i--) { | ||||
|         const std::size_t block_size{blocks[i].GetSize()}; | ||||
|         while (after_start + block_size <= after_end) { | ||||
|             FreeBlock(after_start, i); | ||||
|             after_start += block_size; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::size_t PageHeap::CalculateMetadataOverheadSize(std::size_t region_size) { | ||||
|     std::size_t overhead_size = 0; | ||||
|     for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { | ||||
|         const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; | ||||
|         const std::size_t next_block_shift{ | ||||
|             (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; | ||||
|         overhead_size += PageHeap::Block::CalculateMetadataOverheadSize( | ||||
|             region_size, cur_block_shift, next_block_shift); | ||||
|     } | ||||
|     return Common::AlignUp(overhead_size, PageSize); | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										370
									
								
								src/core/hle/kernel/memory/page_heap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										370
									
								
								src/core/hle/kernel/memory/page_heap.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,370 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| // This file references various implementation details from Atmosphère, an open-source firmware for
 | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/alignment.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/bit_util.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/memory/memory_types.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| class PageHeap final : NonCopyable { | ||||
| public: | ||||
|     static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) { | ||||
|         const auto target_pages{std::max(num_pages, align_pages)}; | ||||
|         for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) { | ||||
|             if (target_pages <= | ||||
|                 (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { | ||||
|                 return static_cast<s32>(i); | ||||
|             } | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     static constexpr s32 GetBlockIndex(std::size_t num_pages) { | ||||
|         for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) { | ||||
|             if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { | ||||
|                 return i; | ||||
|             } | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     static constexpr std::size_t GetBlockSize(std::size_t index) { | ||||
|         return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index]; | ||||
|     } | ||||
| 
 | ||||
|     static constexpr std::size_t GetBlockNumPages(std::size_t index) { | ||||
|         return GetBlockSize(index) / PageSize; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     static constexpr std::size_t NumMemoryBlockPageShifts{7}; | ||||
|     static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{ | ||||
|         0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E, | ||||
|     }; | ||||
| 
 | ||||
|     class Block final : NonCopyable { | ||||
|     private: | ||||
|         class Bitmap final : NonCopyable { | ||||
|         public: | ||||
|             static constexpr std::size_t MaxDepth{4}; | ||||
| 
 | ||||
|         private: | ||||
|             std::array<u64*, MaxDepth> bit_storages{}; | ||||
|             std::size_t num_bits{}; | ||||
|             std::size_t used_depths{}; | ||||
| 
 | ||||
|         public: | ||||
|             constexpr Bitmap() = default; | ||||
| 
 | ||||
|             constexpr std::size_t GetNumBits() const { | ||||
|                 return num_bits; | ||||
|             } | ||||
|             constexpr s32 GetHighestDepthIndex() const { | ||||
|                 return static_cast<s32>(used_depths) - 1; | ||||
|             } | ||||
| 
 | ||||
|             constexpr u64* Initialize(u64* storage, std::size_t size) { | ||||
|                 //* Initially, everything is un-set
 | ||||
|                 num_bits = 0; | ||||
| 
 | ||||
|                 // Calculate the needed bitmap depth
 | ||||
|                 used_depths = static_cast<std::size_t>(GetRequiredDepth(size)); | ||||
|                 ASSERT(used_depths <= MaxDepth); | ||||
| 
 | ||||
|                 // Set the bitmap pointers
 | ||||
|                 for (s32 depth{GetHighestDepthIndex()}; depth >= 0; depth--) { | ||||
|                     bit_storages[depth] = storage; | ||||
|                     size = Common::AlignUp(size, 64) / 64; | ||||
|                     storage += size; | ||||
|                 } | ||||
| 
 | ||||
|                 return storage; | ||||
|             } | ||||
| 
 | ||||
|             s64 FindFreeBlock() const { | ||||
|                 uintptr_t offset{}; | ||||
|                 s32 depth{}; | ||||
| 
 | ||||
|                 do { | ||||
|                     const u64 v{bit_storages[depth][offset]}; | ||||
|                     if (v == 0) { | ||||
|                         // Non-zero depth indicates that a previous level had a free block
 | ||||
|                         ASSERT(depth == 0); | ||||
|                         return -1; | ||||
|                     } | ||||
|                     offset = offset * 64 + Common::CountTrailingZeroes64(v); | ||||
|                     ++depth; | ||||
|                 } while (depth < static_cast<s32>(used_depths)); | ||||
| 
 | ||||
|                 return static_cast<s64>(offset); | ||||
|             } | ||||
| 
 | ||||
|             constexpr void SetBit(std::size_t offset) { | ||||
|                 SetBit(GetHighestDepthIndex(), offset); | ||||
|                 num_bits++; | ||||
|             } | ||||
| 
 | ||||
|             constexpr void ClearBit(std::size_t offset) { | ||||
|                 ClearBit(GetHighestDepthIndex(), offset); | ||||
|                 num_bits--; | ||||
|             } | ||||
| 
 | ||||
|             constexpr bool ClearRange(std::size_t offset, std::size_t count) { | ||||
|                 const s32 depth{GetHighestDepthIndex()}; | ||||
|                 const auto bit_ind{offset / 64}; | ||||
|                 u64* bits{bit_storages[depth]}; | ||||
|                 if (count < 64) { | ||||
|                     const auto shift{offset % 64}; | ||||
|                     ASSERT(shift + count <= 64); | ||||
|                     // Check that all the bits are set
 | ||||
|                     const u64 mask{((1ULL << count) - 1) << shift}; | ||||
|                     u64 v{bits[bit_ind]}; | ||||
|                     if ((v & mask) != mask) { | ||||
|                         return false; | ||||
|                     } | ||||
| 
 | ||||
|                     // Clear the bits
 | ||||
|                     v &= ~mask; | ||||
|                     bits[bit_ind] = v; | ||||
|                     if (v == 0) { | ||||
|                         ClearBit(depth - 1, bit_ind); | ||||
|                     } | ||||
|                 } else { | ||||
|                     ASSERT(offset % 64 == 0); | ||||
|                     ASSERT(count % 64 == 0); | ||||
|                     // Check that all the bits are set
 | ||||
|                     std::size_t remaining{count}; | ||||
|                     std::size_t i = 0; | ||||
|                     do { | ||||
|                         if (bits[bit_ind + i++] != ~u64(0)) { | ||||
|                             return false; | ||||
|                         } | ||||
|                         remaining -= 64; | ||||
|                     } while (remaining > 0); | ||||
| 
 | ||||
|                     // Clear the bits
 | ||||
|                     remaining = count; | ||||
|                     i = 0; | ||||
|                     do { | ||||
|                         bits[bit_ind + i] = 0; | ||||
|                         ClearBit(depth - 1, bit_ind + i); | ||||
|                         i++; | ||||
|                         remaining -= 64; | ||||
|                     } while (remaining > 0); | ||||
|                 } | ||||
| 
 | ||||
|                 num_bits -= count; | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|         private: | ||||
|             constexpr void SetBit(s32 depth, std::size_t offset) { | ||||
|                 while (depth >= 0) { | ||||
|                     const auto ind{offset / 64}; | ||||
|                     const auto which{offset % 64}; | ||||
|                     const u64 mask{1ULL << which}; | ||||
| 
 | ||||
|                     u64* bit{std::addressof(bit_storages[depth][ind])}; | ||||
|                     const u64 v{*bit}; | ||||
|                     ASSERT((v & mask) == 0); | ||||
|                     *bit = v | mask; | ||||
|                     if (v) { | ||||
|                         break; | ||||
|                     } | ||||
|                     offset = ind; | ||||
|                     depth--; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             constexpr void ClearBit(s32 depth, std::size_t offset) { | ||||
|                 while (depth >= 0) { | ||||
|                     const auto ind{offset / 64}; | ||||
|                     const auto which{offset % 64}; | ||||
|                     const u64 mask{1ULL << which}; | ||||
| 
 | ||||
|                     u64* bit{std::addressof(bit_storages[depth][ind])}; | ||||
|                     u64 v{*bit}; | ||||
|                     ASSERT((v & mask) != 0); | ||||
|                     v &= ~mask; | ||||
|                     *bit = v; | ||||
|                     if (v) { | ||||
|                         break; | ||||
|                     } | ||||
|                     offset = ind; | ||||
|                     depth--; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         private: | ||||
|             static constexpr s32 GetRequiredDepth(std::size_t region_size) { | ||||
|                 s32 depth = 0; | ||||
|                 while (true) { | ||||
|                     region_size /= 64; | ||||
|                     depth++; | ||||
|                     if (region_size == 0) { | ||||
|                         return depth; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         public: | ||||
|             static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size) { | ||||
|                 std::size_t overhead_bits = 0; | ||||
|                 for (s32 depth{GetRequiredDepth(region_size) - 1}; depth >= 0; depth--) { | ||||
|                     region_size = Common::AlignUp(region_size, 64) / 64; | ||||
|                     overhead_bits += region_size; | ||||
|                 } | ||||
|                 return overhead_bits * sizeof(u64); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|     private: | ||||
|         Bitmap bitmap; | ||||
|         VAddr heap_address{}; | ||||
|         uintptr_t end_offset{}; | ||||
|         std::size_t block_shift{}; | ||||
|         std::size_t next_block_shift{}; | ||||
| 
 | ||||
|     public: | ||||
|         constexpr Block() = default; | ||||
| 
 | ||||
|         constexpr std::size_t GetShift() const { | ||||
|             return block_shift; | ||||
|         } | ||||
|         constexpr std::size_t GetNextShift() const { | ||||
|             return next_block_shift; | ||||
|         } | ||||
|         constexpr std::size_t GetSize() const { | ||||
|             return static_cast<std::size_t>(1) << GetShift(); | ||||
|         } | ||||
|         constexpr std::size_t GetNumPages() const { | ||||
|             return GetSize() / PageSize; | ||||
|         } | ||||
|         constexpr std::size_t GetNumFreeBlocks() const { | ||||
|             return bitmap.GetNumBits(); | ||||
|         } | ||||
|         constexpr std::size_t GetNumFreePages() const { | ||||
|             return GetNumFreeBlocks() * GetNumPages(); | ||||
|         } | ||||
| 
 | ||||
|         constexpr u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs, | ||||
|                                   u64* bit_storage) { | ||||
|             // Set shifts
 | ||||
|             block_shift = bs; | ||||
|             next_block_shift = nbs; | ||||
| 
 | ||||
|             // Align up the address
 | ||||
|             VAddr end{addr + size}; | ||||
|             const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift) | ||||
|                                                      : (1ULL << block_shift)}; | ||||
|             addr = Common::AlignDown((addr), align); | ||||
|             end = Common::AlignUp((end), align); | ||||
| 
 | ||||
|             heap_address = addr; | ||||
|             end_offset = (end - addr) / (1ULL << block_shift); | ||||
|             return bitmap.Initialize(bit_storage, end_offset); | ||||
|         } | ||||
| 
 | ||||
|         constexpr VAddr PushBlock(VAddr address) { | ||||
|             // Set the bit for the free block
 | ||||
|             std::size_t offset{(address - heap_address) >> GetShift()}; | ||||
|             bitmap.SetBit(offset); | ||||
| 
 | ||||
|             // If we have a next shift, try to clear the blocks below and return the address
 | ||||
|             if (GetNextShift()) { | ||||
|                 const auto diff{1ULL << (GetNextShift() - GetShift())}; | ||||
|                 offset = Common::AlignDown(offset, diff); | ||||
|                 if (bitmap.ClearRange(offset, diff)) { | ||||
|                     return heap_address + (offset << GetShift()); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // We couldn't coalesce, or we're already as big as possible
 | ||||
|             return 0; | ||||
|         } | ||||
| 
 | ||||
|         VAddr PopBlock() { | ||||
|             // Find a free block
 | ||||
|             const s64 soffset{bitmap.FindFreeBlock()}; | ||||
|             if (soffset < 0) { | ||||
|                 return 0; | ||||
|             } | ||||
|             const auto offset{static_cast<std::size_t>(soffset)}; | ||||
| 
 | ||||
|             // Update our tracking and return it
 | ||||
|             bitmap.ClearBit(offset); | ||||
|             return heap_address + (offset << GetShift()); | ||||
|         } | ||||
| 
 | ||||
|     public: | ||||
|         static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size, | ||||
|                                                                    std::size_t cur_block_shift, | ||||
|                                                                    std::size_t next_block_shift) { | ||||
|             const auto cur_block_size{(1ULL << cur_block_shift)}; | ||||
|             const auto next_block_size{(1ULL << next_block_shift)}; | ||||
|             const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size}; | ||||
|             return Bitmap::CalculateMetadataOverheadSize( | ||||
|                 (align * 2 + Common::AlignUp(region_size, align)) / cur_block_size); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| public: | ||||
|     PageHeap() = default; | ||||
| 
 | ||||
|     constexpr VAddr GetAddress() const { | ||||
|         return heap_address; | ||||
|     } | ||||
|     constexpr std::size_t GetSize() const { | ||||
|         return heap_size; | ||||
|     } | ||||
|     constexpr VAddr GetEndAddress() const { | ||||
|         return GetAddress() + GetSize(); | ||||
|     } | ||||
|     constexpr std::size_t GetPageOffset(VAddr block) const { | ||||
|         return (block - GetAddress()) / PageSize; | ||||
|     } | ||||
| 
 | ||||
|     void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size); | ||||
|     VAddr AllocateBlock(s32 index); | ||||
|     void Free(VAddr addr, std::size_t num_pages); | ||||
| 
 | ||||
|     void UpdateUsedSize() { | ||||
|         used_size = heap_size - (GetNumFreePages() * PageSize); | ||||
|     } | ||||
| 
 | ||||
|     static std::size_t CalculateMetadataOverheadSize(std::size_t region_size); | ||||
| 
 | ||||
| private: | ||||
|     constexpr std::size_t GetNumFreePages() const { | ||||
|         std::size_t num_free{}; | ||||
| 
 | ||||
|         for (const auto& block : blocks) { | ||||
|             num_free += block.GetNumFreePages(); | ||||
|         } | ||||
| 
 | ||||
|         return num_free; | ||||
|     } | ||||
| 
 | ||||
|     void FreeBlock(VAddr block, s32 index); | ||||
| 
 | ||||
|     VAddr heap_address{}; | ||||
|     std::size_t heap_size{}; | ||||
|     std::size_t used_size{}; | ||||
|     std::array<Block, NumMemoryBlockPageShifts> blocks{}; | ||||
|     std::vector<u64> metadata; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										93
									
								
								src/core/hle/kernel/memory/page_linked_list.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/core/hle/kernel/memory/page_linked_list.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <list> | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/memory/memory_types.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| class PageLinkedList final { | ||||
| public: | ||||
|     class Node final { | ||||
|     public: | ||||
|         constexpr Node(u64 addr, std::size_t num_pages) : addr{addr}, num_pages{num_pages} {} | ||||
| 
 | ||||
|         constexpr u64 GetAddress() const { | ||||
|             return addr; | ||||
|         } | ||||
| 
 | ||||
|         constexpr std::size_t GetNumPages() const { | ||||
|             return num_pages; | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         u64 addr{}; | ||||
|         std::size_t num_pages{}; | ||||
|     }; | ||||
| 
 | ||||
| public: | ||||
|     PageLinkedList() = default; | ||||
|     PageLinkedList(u64 address, u64 num_pages) { | ||||
|         ASSERT(AddBlock(address, num_pages).IsSuccess()); | ||||
|     } | ||||
| 
 | ||||
|     constexpr std::list<Node>& Nodes() { | ||||
|         return nodes; | ||||
|     } | ||||
| 
 | ||||
|     constexpr const std::list<Node>& Nodes() const { | ||||
|         return nodes; | ||||
|     } | ||||
| 
 | ||||
|     std::size_t GetNumPages() const { | ||||
|         std::size_t num_pages = 0; | ||||
|         for (const Node& node : nodes) { | ||||
|             num_pages += node.GetNumPages(); | ||||
|         } | ||||
|         return num_pages; | ||||
|     } | ||||
| 
 | ||||
|     bool IsEqual(PageLinkedList& other) const { | ||||
|         auto this_node = nodes.begin(); | ||||
|         auto other_node = other.nodes.begin(); | ||||
|         while (this_node != nodes.end() && other_node != other.nodes.end()) { | ||||
|             if (this_node->GetAddress() != other_node->GetAddress() || | ||||
|                 this_node->GetNumPages() != other_node->GetNumPages()) { | ||||
|                 return false; | ||||
|             } | ||||
|             this_node = std::next(this_node); | ||||
|             other_node = std::next(other_node); | ||||
|         } | ||||
| 
 | ||||
|         return this_node == nodes.end() && other_node == other.nodes.end(); | ||||
|     } | ||||
| 
 | ||||
|     ResultCode AddBlock(u64 address, u64 num_pages) { | ||||
|         if (!num_pages) { | ||||
|             return RESULT_SUCCESS; | ||||
|         } | ||||
|         if (!nodes.empty()) { | ||||
|             const auto node = nodes.back(); | ||||
|             if (node.GetAddress() + node.GetNumPages() * PageSize == address) { | ||||
|                 address = node.GetAddress(); | ||||
|                 num_pages += node.GetNumPages(); | ||||
|                 nodes.pop_back(); | ||||
|             } | ||||
|         } | ||||
|         nodes.push_back({address, num_pages}); | ||||
|         return RESULT_SUCCESS; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::list<Node> nodes; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										1130
									
								
								src/core/hle/kernel/memory/page_table.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1130
									
								
								src/core/hle/kernel/memory/page_table.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										276
									
								
								src/core/hle/kernel/memory/page_table.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								src/core/hle/kernel/memory/page_table.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,276 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <list> | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/page_table.h" | ||||
| #include "core/file_sys/program_metadata.h" | ||||
| #include "core/hle/kernel/memory/memory_block.h" | ||||
| #include "core/hle/kernel/memory/memory_manager.h" | ||||
| 
 | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| class MemoryBlockManager; | ||||
| 
 | ||||
| class PageTable final : NonCopyable { | ||||
| public: | ||||
|     explicit PageTable(Core::System& system); | ||||
| 
 | ||||
|     ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, | ||||
|                                     VAddr code_addr, std::size_t code_size, | ||||
|                                     Memory::MemoryManager::Pool pool); | ||||
|     ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, MemoryState state, | ||||
|                               MemoryPermission perm); | ||||
|     ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | ||||
|     ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | ||||
|     ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); | ||||
|     ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); | ||||
|     ResultCode UnmapMemory(VAddr addr, std::size_t size); | ||||
|     ResultCode Map(VAddr dst_addr, VAddr src_addr, std::size_t size); | ||||
|     ResultCode Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size); | ||||
|     ResultCode MapPages(VAddr addr, PageLinkedList& page_linked_list, MemoryState state, | ||||
|                         MemoryPermission perm); | ||||
|     ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, MemoryPermission perm); | ||||
|     MemoryInfo QueryInfo(VAddr addr); | ||||
|     ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, MemoryPermission perm); | ||||
|     ResultCode ResetTransferMemory(VAddr addr, std::size_t size); | ||||
|     ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, MemoryAttribute mask, | ||||
|                                   MemoryAttribute value); | ||||
|     ResultCode SetHeapCapacity(std::size_t new_heap_capacity); | ||||
|     ResultVal<VAddr> SetHeapSize(std::size_t size); | ||||
|     ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, | ||||
|                                           bool is_map_only, VAddr region_start, | ||||
|                                           std::size_t region_num_pages, MemoryState state, | ||||
|                                           MemoryPermission perm, PAddr map_addr = 0); | ||||
| 
 | ||||
|     Common::PageTable& PageTableImpl() { | ||||
|         return page_table_impl; | ||||
|     } | ||||
| 
 | ||||
|     const Common::PageTable& PageTableImpl() const { | ||||
|         return page_table_impl; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     enum class OperationType : u32 { | ||||
|         Map, | ||||
|         MapGroup, | ||||
|         Unmap, | ||||
|         ChangePermissions, | ||||
|         ChangePermissionsAndRefresh, | ||||
|     }; | ||||
| 
 | ||||
|     static constexpr MemoryAttribute DefaultMemoryIgnoreAttr = | ||||
|         MemoryAttribute::DontCareMask | MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared; | ||||
| 
 | ||||
|     ResultCode InitializeMemoryLayout(VAddr start, VAddr end); | ||||
|     ResultCode MapPages(VAddr addr, const PageLinkedList& page_linked_list, MemoryPermission perm); | ||||
|     void MapPhysicalMemory(PageLinkedList& page_linked_list, VAddr start, VAddr end); | ||||
|     bool IsRegionMapped(VAddr address, u64 size); | ||||
|     bool IsRegionContiguous(VAddr addr, u64 size) const; | ||||
|     void AddRegionToPages(VAddr start, std::size_t num_pages, PageLinkedList& page_linked_list); | ||||
|     MemoryInfo QueryInfoImpl(VAddr addr); | ||||
|     VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages, | ||||
|                                 std::size_t align); | ||||
|     ResultCode Operate(VAddr addr, std::size_t num_pages, const PageLinkedList& page_group, | ||||
|                        OperationType operation); | ||||
|     ResultCode Operate(VAddr addr, std::size_t num_pages, MemoryPermission perm, | ||||
|                        OperationType operation, PAddr map_addr = 0); | ||||
|     constexpr VAddr GetRegionAddress(MemoryState state) const; | ||||
|     constexpr std::size_t GetRegionSize(MemoryState state) const; | ||||
|     constexpr bool CanContain(VAddr addr, std::size_t size, MemoryState state) const; | ||||
| 
 | ||||
|     constexpr ResultCode CheckMemoryState(const MemoryInfo& info, MemoryState state_mask, | ||||
|                                           MemoryState state, MemoryPermission perm_mask, | ||||
|                                           MemoryPermission perm, MemoryAttribute attr_mask, | ||||
|                                           MemoryAttribute attr) const; | ||||
|     ResultCode CheckMemoryState(MemoryState* out_state, MemoryPermission* out_perm, | ||||
|                                 MemoryAttribute* out_attr, VAddr addr, std::size_t size, | ||||
|                                 MemoryState state_mask, MemoryState state, | ||||
|                                 MemoryPermission perm_mask, MemoryPermission perm, | ||||
|                                 MemoryAttribute attr_mask, MemoryAttribute attr, | ||||
|                                 MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr); | ||||
|     ResultCode CheckMemoryState(VAddr addr, std::size_t size, MemoryState state_mask, | ||||
|                                 MemoryState state, MemoryPermission perm_mask, | ||||
|                                 MemoryPermission perm, MemoryAttribute attr_mask, | ||||
|                                 MemoryAttribute attr, | ||||
|                                 MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) { | ||||
|         return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, | ||||
|                                 perm, attr_mask, attr, ignore_attr); | ||||
|     } | ||||
| 
 | ||||
|     std::recursive_mutex page_table_lock; | ||||
|     std::unique_ptr<MemoryBlockManager> block_manager; | ||||
| 
 | ||||
| public: | ||||
|     constexpr VAddr GetAddressSpaceStart() const { | ||||
|         return address_space_start; | ||||
|     } | ||||
|     constexpr VAddr GetAddressSpaceEnd() const { | ||||
|         return address_space_end; | ||||
|     } | ||||
|     constexpr std::size_t GetAddressSpaceSize() const { | ||||
|         return address_space_end - address_space_start; | ||||
|     } | ||||
|     constexpr VAddr GetHeapRegionStart() const { | ||||
|         return heap_region_start; | ||||
|     } | ||||
|     constexpr VAddr GetHeapRegionEnd() const { | ||||
|         return heap_region_end; | ||||
|     } | ||||
|     constexpr std::size_t GetHeapRegionSize() const { | ||||
|         return heap_region_end - heap_region_start; | ||||
|     } | ||||
|     constexpr VAddr GetAliasRegionStart() const { | ||||
|         return alias_region_start; | ||||
|     } | ||||
|     constexpr VAddr GetAliasRegionEnd() const { | ||||
|         return alias_region_end; | ||||
|     } | ||||
|     constexpr std::size_t GetAliasRegionSize() const { | ||||
|         return alias_region_end - alias_region_start; | ||||
|     } | ||||
|     constexpr VAddr GetStackRegionStart() const { | ||||
|         return stack_region_start; | ||||
|     } | ||||
|     constexpr VAddr GetStackRegionEnd() const { | ||||
|         return stack_region_end; | ||||
|     } | ||||
|     constexpr std::size_t GetStackRegionSize() const { | ||||
|         return stack_region_end - stack_region_start; | ||||
|     } | ||||
|     constexpr VAddr GetKernelMapRegionStart() const { | ||||
|         return kernel_map_region_start; | ||||
|     } | ||||
|     constexpr VAddr GetKernelMapRegionEnd() const { | ||||
|         return kernel_map_region_end; | ||||
|     } | ||||
|     constexpr VAddr GetCodeRegionStart() const { | ||||
|         return code_region_start; | ||||
|     } | ||||
|     constexpr VAddr GetCodeRegionEnd() const { | ||||
|         return code_region_end; | ||||
|     } | ||||
|     constexpr VAddr GetAliasCodeRegionStart() const { | ||||
|         return alias_code_region_start; | ||||
|     } | ||||
|     constexpr VAddr GetAliasCodeRegionSize() const { | ||||
|         return alias_code_region_end - alias_code_region_start; | ||||
|     } | ||||
|     constexpr std::size_t GetAddressSpaceWidth() const { | ||||
|         return address_space_width; | ||||
|     } | ||||
|     constexpr std::size_t GetHeapSize() { | ||||
|         return current_heap_addr - heap_region_start; | ||||
|     } | ||||
|     constexpr std::size_t GetTotalHeapSize() { | ||||
|         return GetHeapSize() + physical_memory_usage; | ||||
|     } | ||||
|     constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const { | ||||
|         return address_space_start <= address && address + size - 1 <= address_space_end - 1; | ||||
|     } | ||||
|     constexpr bool IsOutsideAliasRegion(VAddr address, std::size_t size) const { | ||||
|         return alias_region_start > address || address + size - 1 > alias_region_end - 1; | ||||
|     } | ||||
|     constexpr bool IsOutsideStackRegion(VAddr address, std::size_t size) const { | ||||
|         return stack_region_start > address || address + size - 1 > stack_region_end - 1; | ||||
|     } | ||||
|     constexpr bool IsInvalidRegion(VAddr address, std::size_t size) const { | ||||
|         return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1; | ||||
|     } | ||||
|     constexpr bool IsInsideHeapRegion(VAddr address, std::size_t size) const { | ||||
|         return address + size > heap_region_start && heap_region_end > address; | ||||
|     } | ||||
|     constexpr bool IsInsideAliasRegion(VAddr address, std::size_t size) const { | ||||
|         return address + size > alias_region_start && alias_region_end > address; | ||||
|     } | ||||
|     constexpr bool IsOutsideASLRRegion(VAddr address, std::size_t size) const { | ||||
|         if (IsInvalidRegion(address, size)) { | ||||
|             return true; | ||||
|         } | ||||
|         if (IsInsideHeapRegion(address, size)) { | ||||
|             return true; | ||||
|         } | ||||
|         if (IsInsideAliasRegion(address, size)) { | ||||
|             return true; | ||||
|         } | ||||
|         return {}; | ||||
|     } | ||||
|     constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { | ||||
|         return !IsOutsideASLRRegion(address, size); | ||||
|     } | ||||
|     constexpr PAddr GetPhysicalAddr(VAddr addr) { | ||||
|         return page_table_impl.backing_addr[addr >> Memory::PageBits] + addr; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     constexpr bool Contains(VAddr addr) const { | ||||
|         return address_space_start <= addr && addr <= address_space_end - 1; | ||||
|     } | ||||
|     constexpr bool Contains(VAddr addr, std::size_t size) const { | ||||
|         return address_space_start <= addr && addr < addr + size && | ||||
|                addr + size - 1 <= address_space_end - 1; | ||||
|     } | ||||
|     constexpr bool IsKernel() const { | ||||
|         return is_kernel; | ||||
|     } | ||||
|     constexpr bool IsAslrEnabled() const { | ||||
|         return is_aslr_enabled; | ||||
|     } | ||||
| 
 | ||||
|     constexpr std::size_t GetNumGuardPages() const { | ||||
|         return IsKernel() ? 1 : 4; | ||||
|     } | ||||
| 
 | ||||
|     constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { | ||||
|         return (address_space_start <= addr) && | ||||
|                (num_pages <= (address_space_end - address_space_start) / PageSize) && | ||||
|                (addr + num_pages * PageSize - 1 <= address_space_end - 1); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     VAddr address_space_start{}; | ||||
|     VAddr address_space_end{}; | ||||
|     VAddr heap_region_start{}; | ||||
|     VAddr heap_region_end{}; | ||||
|     VAddr current_heap_end{}; | ||||
|     VAddr alias_region_start{}; | ||||
|     VAddr alias_region_end{}; | ||||
|     VAddr stack_region_start{}; | ||||
|     VAddr stack_region_end{}; | ||||
|     VAddr kernel_map_region_start{}; | ||||
|     VAddr kernel_map_region_end{}; | ||||
|     VAddr code_region_start{}; | ||||
|     VAddr code_region_end{}; | ||||
|     VAddr alias_code_region_start{}; | ||||
|     VAddr alias_code_region_end{}; | ||||
|     VAddr current_heap_addr{}; | ||||
| 
 | ||||
|     std::size_t heap_capacity{}; | ||||
|     std::size_t physical_memory_usage{}; | ||||
|     std::size_t max_heap_size{}; | ||||
|     std::size_t max_physical_memory_size{}; | ||||
|     std::size_t address_space_width{}; | ||||
| 
 | ||||
|     bool is_kernel{}; | ||||
|     bool is_aslr_enabled{}; | ||||
| 
 | ||||
|     MemoryManager::Pool memory_pool{MemoryManager::Pool::Application}; | ||||
| 
 | ||||
|     Common::PageTable page_table_impl; | ||||
| 
 | ||||
|     Core::System& system; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										164
									
								
								src/core/hle/kernel/memory/slab_heap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								src/core/hle/kernel/memory/slab_heap.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,164 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| // This file references various implementation details from Atmosphère, an open-source firmware for
 | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <atomic> | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Kernel::Memory { | ||||
| 
 | ||||
| namespace impl { | ||||
| 
 | ||||
| class SlabHeapImpl final : NonCopyable { | ||||
| public: | ||||
|     struct Node { | ||||
|         Node* next{}; | ||||
|     }; | ||||
| 
 | ||||
|     constexpr SlabHeapImpl() = default; | ||||
| 
 | ||||
|     void Initialize(std::size_t size) { | ||||
|         ASSERT(head == nullptr); | ||||
|         obj_size = size; | ||||
|     } | ||||
| 
 | ||||
|     constexpr std::size_t GetObjectSize() const { | ||||
|         return obj_size; | ||||
|     } | ||||
| 
 | ||||
|     Node* GetHead() const { | ||||
|         return head; | ||||
|     } | ||||
| 
 | ||||
|     void* Allocate() { | ||||
|         Node* ret = head.load(); | ||||
| 
 | ||||
|         do { | ||||
|             if (ret == nullptr) { | ||||
|                 break; | ||||
|             } | ||||
|         } while (!head.compare_exchange_weak(ret, ret->next)); | ||||
| 
 | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     void Free(void* obj) { | ||||
|         Node* node = reinterpret_cast<Node*>(obj); | ||||
| 
 | ||||
|         Node* cur_head = head.load(); | ||||
|         do { | ||||
|             node->next = cur_head; | ||||
|         } while (!head.compare_exchange_weak(cur_head, node)); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::atomic<Node*> head{}; | ||||
|     std::size_t obj_size{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace impl
 | ||||
| 
 | ||||
| class SlabHeapBase : NonCopyable { | ||||
| public: | ||||
|     constexpr SlabHeapBase() = default; | ||||
| 
 | ||||
|     constexpr bool Contains(uintptr_t addr) const { | ||||
|         return start <= addr && addr < end; | ||||
|     } | ||||
| 
 | ||||
|     constexpr std::size_t GetSlabHeapSize() const { | ||||
|         return (end - start) / GetObjectSize(); | ||||
|     } | ||||
| 
 | ||||
|     constexpr std::size_t GetObjectSize() const { | ||||
|         return impl.GetObjectSize(); | ||||
|     } | ||||
| 
 | ||||
|     constexpr uintptr_t GetSlabHeapAddress() const { | ||||
|         return start; | ||||
|     } | ||||
| 
 | ||||
|     std::size_t GetObjectIndexImpl(const void* obj) const { | ||||
|         return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize(); | ||||
|     } | ||||
| 
 | ||||
|     std::size_t GetPeakIndex() const { | ||||
|         return GetObjectIndexImpl(reinterpret_cast<const void*>(peak)); | ||||
|     } | ||||
| 
 | ||||
|     void* AllocateImpl() { | ||||
|         return impl.Allocate(); | ||||
|     } | ||||
| 
 | ||||
|     void FreeImpl(void* obj) { | ||||
|         // Don't allow freeing an object that wasn't allocated from this heap
 | ||||
|         ASSERT(Contains(reinterpret_cast<uintptr_t>(obj))); | ||||
|         impl.Free(obj); | ||||
|     } | ||||
| 
 | ||||
|     void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) { | ||||
|         // Ensure we don't initialize a slab using null memory
 | ||||
|         ASSERT(memory != nullptr); | ||||
| 
 | ||||
|         // Initialize the base allocator
 | ||||
|         impl.Initialize(obj_size); | ||||
| 
 | ||||
|         // Set our tracking variables
 | ||||
|         const std::size_t num_obj = (memory_size / obj_size); | ||||
|         start = reinterpret_cast<uintptr_t>(memory); | ||||
|         end = start + num_obj * obj_size; | ||||
|         peak = start; | ||||
| 
 | ||||
|         // Free the objects
 | ||||
|         u8* cur = reinterpret_cast<u8*>(end); | ||||
| 
 | ||||
|         for (std::size_t i{}; i < num_obj; i++) { | ||||
|             cur -= obj_size; | ||||
|             impl.Free(cur); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     using Impl = impl::SlabHeapImpl; | ||||
| 
 | ||||
|     Impl impl; | ||||
|     uintptr_t peak{}; | ||||
|     uintptr_t start{}; | ||||
|     uintptr_t end{}; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| class SlabHeap final : public SlabHeapBase { | ||||
| public: | ||||
|     constexpr SlabHeap() : SlabHeapBase() {} | ||||
| 
 | ||||
|     void Initialize(void* memory, std::size_t memory_size) { | ||||
|         InitializeImpl(sizeof(T), memory, memory_size); | ||||
|     } | ||||
| 
 | ||||
|     T* Allocate() { | ||||
|         T* obj = reinterpret_cast<T*>(AllocateImpl()); | ||||
|         if (obj != nullptr) { | ||||
|             new (obj) T(); | ||||
|         } | ||||
|         return obj; | ||||
|     } | ||||
| 
 | ||||
|     void Free(T* obj) { | ||||
|         FreeImpl(obj); | ||||
|     } | ||||
| 
 | ||||
|     constexpr std::size_t GetObjectIndex(const T* obj) const { | ||||
|         return GetObjectIndexImpl(obj); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel::Memory
 | ||||
							
								
								
									
										41
									
								
								src/core/hle/kernel/memory/system_control.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/core/hle/kernel/memory/system_control.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| // Copyright 2020 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <random> | ||||
| 
 | ||||
| #include "core/hle/kernel/memory/system_control.h" | ||||
| 
 | ||||
| namespace Kernel::Memory::SystemControl { | ||||
| 
 | ||||
| u64 GenerateRandomU64ForInit() { | ||||
|     static std::random_device device; | ||||
|     static std::mt19937 gen(device()); | ||||
|     static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); | ||||
|     return distribution(gen); | ||||
| } | ||||
| 
 | ||||
| template <typename F> | ||||
| u64 GenerateUniformRange(u64 min, u64 max, F f) { | ||||
|     /* Handle the case where the difference is too large to represent. */ | ||||
|     if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) { | ||||
|         return f(); | ||||
|     } | ||||
| 
 | ||||
|     /* Iterate until we get a value in range. */ | ||||
|     const u64 range_size = ((max + 1) - min); | ||||
|     const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size; | ||||
|     while (true) { | ||||
|         if (const u64 rnd = f(); rnd < effective_max) { | ||||
|             return min + (rnd % range_size); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| u64 GenerateRandomRange(u64 min, u64 max) { | ||||
|     return GenerateUniformRange(min, max, GenerateRandomU64ForInit); | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel::Memory::SystemControl
 | ||||
							
								
								
									
										18
									
								
								src/core/hle/kernel/memory/system_control.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/core/hle/kernel/memory/system_control.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| // Copyright 2020 yuzu 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::Memory::SystemControl { | ||||
| 
 | ||||
| u64 GenerateRandomU64ForInit(); | ||||
| 
 | ||||
| template <typename F> | ||||
| u64 GenerateUniformRange(u64 min, u64 max, F f); | ||||
| 
 | ||||
| u64 GenerateRandomRange(u64 min, u64 max); | ||||
| 
 | ||||
| } // namespace Kernel::Memory::SystemControl
 | ||||
| @ -4,6 +4,8 @@ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/alignment.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
|  | ||||
| @ -10,15 +10,18 @@ | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/core.h" | ||||
| #include "core/device_memory.h" | ||||
| #include "core/file_sys/program_metadata.h" | ||||
| #include "core/hle/kernel/code_set.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory/memory_block_manager.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/memory/slab_heap.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/kernel/scheduler.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| @ -31,10 +34,8 @@ namespace { | ||||
|  * @param kernel The kernel instance to create the main thread under. | ||||
|  * @param priority The priority to give the main thread | ||||
|  */ | ||||
| void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) { | ||||
|     const auto& vm_manager = owner_process.VMManager(); | ||||
|     const VAddr entry_point = vm_manager.GetCodeRegionBaseAddress(); | ||||
|     const VAddr stack_top = vm_manager.GetTLSIORegionEndAddress(); | ||||
| void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, VAddr stack_top) { | ||||
|     const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); | ||||
|     auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, | ||||
|                                      owner_process.GetIdealCore(), stack_top, owner_process); | ||||
| 
 | ||||
| @ -42,6 +43,8 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) { | ||||
| 
 | ||||
|     // Register 1 must be a handle to the main thread
 | ||||
|     const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); | ||||
|     thread->GetContext32().cpu_registers[0] = 0; | ||||
|     thread->GetContext64().cpu_registers[0] = 0; | ||||
|     thread->GetContext32().cpu_registers[1] = thread_handle; | ||||
|     thread->GetContext64().cpu_registers[1] = thread_handle; | ||||
| 
 | ||||
| @ -57,7 +60,8 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) { | ||||
| // (whichever page happens to have an available slot).
 | ||||
| class TLSPage { | ||||
| public: | ||||
|     static constexpr std::size_t num_slot_entries = Memory::PAGE_SIZE / Memory::TLS_ENTRY_SIZE; | ||||
|     static constexpr std::size_t num_slot_entries = | ||||
|         Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE; | ||||
| 
 | ||||
|     explicit TLSPage(VAddr address) : base_address{address} {} | ||||
| 
 | ||||
| @ -76,7 +80,7 @@ public: | ||||
|             } | ||||
| 
 | ||||
|             is_slot_used[i] = true; | ||||
|             return base_address + (i * Memory::TLS_ENTRY_SIZE); | ||||
|             return base_address + (i * Core::Memory::TLS_ENTRY_SIZE); | ||||
|         } | ||||
| 
 | ||||
|         return std::nullopt; | ||||
| @ -86,15 +90,15 @@ public: | ||||
|         // Ensure that all given addresses are consistent with how TLS pages
 | ||||
|         // are intended to be used when releasing slots.
 | ||||
|         ASSERT(IsWithinPage(address)); | ||||
|         ASSERT((address % Memory::TLS_ENTRY_SIZE) == 0); | ||||
|         ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0); | ||||
| 
 | ||||
|         const std::size_t index = (address - base_address) / Memory::TLS_ENTRY_SIZE; | ||||
|         const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE; | ||||
|         is_slot_used[index] = false; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     bool IsWithinPage(VAddr address) const { | ||||
|         return base_address <= address && address < base_address + Memory::PAGE_SIZE; | ||||
|         return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     VAddr base_address; | ||||
| @ -106,7 +110,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name, | ||||
| 
 | ||||
|     std::shared_ptr<Process> process = std::make_shared<Process>(system); | ||||
|     process->name = std::move(name); | ||||
|     process->resource_limit = kernel.GetSystemResourceLimit(); | ||||
|     process->resource_limit = ResourceLimit::Create(kernel); | ||||
|     process->status = ProcessStatus::Created; | ||||
|     process->program_id = 0; | ||||
|     process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() | ||||
| @ -127,7 +131,14 @@ std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const { | ||||
| } | ||||
| 
 | ||||
| u64 Process::GetTotalPhysicalMemoryAvailable() const { | ||||
|     return vm_manager.GetTotalPhysicalMemoryAvailable(); | ||||
|     const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) + | ||||
|                        page_table->GetTotalHeapSize() + image_size + main_thread_stack_size}; | ||||
| 
 | ||||
|     if (capacity < memory_usage_capacity) { | ||||
|         return capacity; | ||||
|     } | ||||
| 
 | ||||
|     return memory_usage_capacity; | ||||
| } | ||||
| 
 | ||||
| u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const { | ||||
| @ -135,8 +146,7 @@ u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const { | ||||
| } | ||||
| 
 | ||||
| u64 Process::GetTotalPhysicalMemoryUsed() const { | ||||
|     return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size + | ||||
|            GetSystemResourceUsage(); | ||||
|     return image_size + main_thread_stack_size + page_table->GetTotalHeapSize(); | ||||
| } | ||||
| 
 | ||||
| u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { | ||||
| @ -209,33 +219,82 @@ ResultCode Process::ClearSignalState() { | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { | ||||
| ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, | ||||
|                                      std::size_t code_size) { | ||||
|     program_id = metadata.GetTitleID(); | ||||
|     ideal_core = metadata.GetMainThreadCore(); | ||||
|     is_64bit_process = metadata.Is64BitProgram(); | ||||
|     system_resource_size = metadata.GetSystemResourceSize(); | ||||
|     image_size = code_size; | ||||
| 
 | ||||
|     vm_manager.Reset(metadata.GetAddressSpaceType()); | ||||
| 
 | ||||
|     const auto& caps = metadata.GetKernelCapabilities(); | ||||
|     const auto capability_init_result = | ||||
|         capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager); | ||||
|     if (capability_init_result.IsError()) { | ||||
|         return capability_init_result; | ||||
|     // Initialize proces address space
 | ||||
|     if (const ResultCode result{ | ||||
|             page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000, | ||||
|                                              code_size, Memory::MemoryManager::Pool::Application)}; | ||||
|         result.IsError()) { | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     // Map process code region
 | ||||
|     if (const ResultCode result{page_table->MapProcessCode( | ||||
|             page_table->GetCodeRegionStart(), code_size / Memory::PageSize, | ||||
|             Memory::MemoryState::Code, Memory::MemoryPermission::None)}; | ||||
|         result.IsError()) { | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     // Initialize process capabilities
 | ||||
|     const auto& caps{metadata.GetKernelCapabilities()}; | ||||
|     if (const ResultCode result{ | ||||
|             capabilities.InitializeForUserProcess(caps.data(), caps.size(), *page_table)}; | ||||
|         result.IsError()) { | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     // Set memory usage capacity
 | ||||
|     switch (metadata.GetAddressSpaceType()) { | ||||
|     case FileSys::ProgramAddressSpaceType::Is32Bit: | ||||
|     case FileSys::ProgramAddressSpaceType::Is36Bit: | ||||
|     case FileSys::ProgramAddressSpaceType::Is39Bit: | ||||
|         memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart(); | ||||
|         break; | ||||
| 
 | ||||
|     case FileSys::ProgramAddressSpaceType::Is32BitNoMap: | ||||
|         memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart() + | ||||
|                                 page_table->GetAliasRegionEnd() - page_table->GetAliasRegionStart(); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
| 
 | ||||
|     // Set initial resource limits
 | ||||
|     resource_limit->SetLimitValue( | ||||
|         ResourceType::PhysicalMemory, | ||||
|         kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application)); | ||||
|     resource_limit->SetLimitValue(ResourceType::Threads, 608); | ||||
|     resource_limit->SetLimitValue(ResourceType::Events, 700); | ||||
|     resource_limit->SetLimitValue(ResourceType::TransferMemory, 128); | ||||
|     resource_limit->SetLimitValue(ResourceType::Sessions, 894); | ||||
|     ASSERT(resource_limit->Reserve(ResourceType::PhysicalMemory, code_size)); | ||||
| 
 | ||||
|     // Create TLS region
 | ||||
|     tls_region_address = CreateTLSRegion(); | ||||
| 
 | ||||
|     return handle_table.SetSize(capabilities.GetHandleTableSize()); | ||||
| } | ||||
| 
 | ||||
| void Process::Run(s32 main_thread_priority, u64 stack_size) { | ||||
|     AllocateMainThreadStack(stack_size); | ||||
|     tls_region_address = CreateTLSRegion(); | ||||
| 
 | ||||
|     vm_manager.LogLayout(); | ||||
|     const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size}; | ||||
|     ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError()); | ||||
| 
 | ||||
|     ChangeStatus(ProcessStatus::Running); | ||||
| 
 | ||||
|     SetupMainThread(*this, kernel, main_thread_priority); | ||||
|     SetupMainThread(*this, kernel, main_thread_priority, main_thread_stack_top); | ||||
|     resource_limit->Reserve(ResourceType::Threads, 1); | ||||
|     resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); | ||||
| } | ||||
| 
 | ||||
| void Process::PrepareForTermination() { | ||||
| @ -279,32 +338,37 @@ static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) { | ||||
| } | ||||
| 
 | ||||
| VAddr Process::CreateTLSRegion() { | ||||
|     auto tls_page_iter = FindTLSPageWithAvailableSlots(tls_pages); | ||||
| 
 | ||||
|     if (tls_page_iter == tls_pages.cend()) { | ||||
|         const auto region_address = | ||||
|             vm_manager.FindFreeRegion(vm_manager.GetTLSIORegionBaseAddress(), | ||||
|                                       vm_manager.GetTLSIORegionEndAddress(), Memory::PAGE_SIZE); | ||||
|         ASSERT(region_address.Succeeded()); | ||||
| 
 | ||||
|         const auto map_result = vm_manager.MapMemoryBlock( | ||||
|             *region_address, std::make_shared<PhysicalMemory>(Memory::PAGE_SIZE), 0, | ||||
|             Memory::PAGE_SIZE, MemoryState::ThreadLocal); | ||||
|         ASSERT(map_result.Succeeded()); | ||||
| 
 | ||||
|         tls_pages.emplace_back(*region_address); | ||||
| 
 | ||||
|         const auto reserve_result = tls_pages.back().ReserveSlot(); | ||||
|         ASSERT(reserve_result.has_value()); | ||||
| 
 | ||||
|         return *reserve_result; | ||||
|     if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; | ||||
|         tls_page_iter != tls_pages.cend()) { | ||||
|         return *tls_page_iter->ReserveSlot(); | ||||
|     } | ||||
| 
 | ||||
|     return *tls_page_iter->ReserveSlot(); | ||||
|     Memory::Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; | ||||
|     ASSERT(tls_page_ptr); | ||||
| 
 | ||||
|     const VAddr start{page_table->GetKernelMapRegionStart()}; | ||||
|     const VAddr size{page_table->GetKernelMapRegionEnd() - start}; | ||||
|     const PAddr tls_map_addr{system.DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; | ||||
|     const VAddr tls_page_addr{ | ||||
|         page_table | ||||
|             ->AllocateAndMapMemory(1, Memory::PageSize, true, start, size / Memory::PageSize, | ||||
|                                    Memory::MemoryState::ThreadLocal, | ||||
|                                    Memory::MemoryPermission::ReadAndWrite, tls_map_addr) | ||||
|             .ValueOr(0)}; | ||||
| 
 | ||||
|     ASSERT(tls_page_addr); | ||||
| 
 | ||||
|     std::memset(tls_page_ptr, 0, Memory::PageSize); | ||||
|     tls_pages.emplace_back(tls_page_addr); | ||||
| 
 | ||||
|     const auto reserve_result{tls_pages.back().ReserveSlot()}; | ||||
|     ASSERT(reserve_result.has_value()); | ||||
| 
 | ||||
|     return *reserve_result; | ||||
| } | ||||
| 
 | ||||
| void Process::FreeTLSRegion(VAddr tls_address) { | ||||
|     const VAddr aligned_address = Common::AlignDown(tls_address, Memory::PAGE_SIZE); | ||||
|     const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE); | ||||
|     auto iter = | ||||
|         std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) { | ||||
|             return page.GetBaseAddress() == aligned_address; | ||||
| @ -317,28 +381,22 @@ void Process::FreeTLSRegion(VAddr tls_address) { | ||||
|     iter->ReleaseSlot(tls_address); | ||||
| } | ||||
| 
 | ||||
| void Process::LoadModule(CodeSet module_, VAddr base_addr) { | ||||
|     code_memory_size += module_.memory.size(); | ||||
| 
 | ||||
|     const auto memory = std::make_shared<PhysicalMemory>(std::move(module_.memory)); | ||||
| 
 | ||||
|     const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions, | ||||
|                                 MemoryState memory_state) { | ||||
|         const auto vma = vm_manager | ||||
|                              .MapMemoryBlock(segment.addr + base_addr, memory, segment.offset, | ||||
|                                              segment.size, memory_state) | ||||
|                              .Unwrap(); | ||||
|         vm_manager.Reprotect(vma, permissions); | ||||
| void Process::LoadModule(CodeSet code_set, VAddr base_addr) { | ||||
|     const auto ReprotectSegment = [&](const CodeSet::Segment& segment, | ||||
|                                       Memory::MemoryPermission permission) { | ||||
|         page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission); | ||||
|     }; | ||||
| 
 | ||||
|     // Map CodeSet segments
 | ||||
|     MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code); | ||||
|     MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData); | ||||
|     MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData); | ||||
|     system.Memory().WriteBlock(*this, base_addr, code_set.memory.data(), code_set.memory.size()); | ||||
| 
 | ||||
|     ReprotectSegment(code_set.CodeSegment(), Memory::MemoryPermission::ReadAndExecute); | ||||
|     ReprotectSegment(code_set.RODataSegment(), Memory::MemoryPermission::Read); | ||||
|     ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite); | ||||
| } | ||||
| 
 | ||||
| Process::Process(Core::System& system) | ||||
|     : SynchronizationObject{system.Kernel()}, vm_manager{system}, | ||||
|     : SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>( | ||||
|                                                   system)}, | ||||
|       address_arbiter{system}, mutex{system}, system{system} {} | ||||
| 
 | ||||
| Process::~Process() = default; | ||||
| @ -361,16 +419,24 @@ void Process::ChangeStatus(ProcessStatus new_status) { | ||||
|     Signal(); | ||||
| } | ||||
| 
 | ||||
| void Process::AllocateMainThreadStack(u64 stack_size) { | ||||
|     // The kernel always ensures that the given stack size is page aligned.
 | ||||
|     main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE); | ||||
| ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) { | ||||
|     ASSERT(stack_size); | ||||
| 
 | ||||
|     // Allocate and map the main thread stack
 | ||||
|     const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size; | ||||
|     vm_manager | ||||
|         .MapMemoryBlock(mapping_address, std::make_shared<PhysicalMemory>(main_thread_stack_size), | ||||
|                         0, main_thread_stack_size, MemoryState::Stack) | ||||
|         .Unwrap(); | ||||
|     // The kernel always ensures that the given stack size is page aligned.
 | ||||
|     main_thread_stack_size = Common::AlignUp(stack_size, Memory::PageSize); | ||||
| 
 | ||||
|     const VAddr start{page_table->GetStackRegionStart()}; | ||||
|     const std::size_t size{page_table->GetStackRegionEnd() - start}; | ||||
| 
 | ||||
|     CASCADE_RESULT(main_thread_stack_top, | ||||
|                    page_table->AllocateAndMapMemory( | ||||
|                        main_thread_stack_size / Memory::PageSize, Memory::PageSize, false, start, | ||||
|                        size / Memory::PageSize, Memory::MemoryState::Stack, | ||||
|                        Memory::MemoryPermission::ReadAndWrite)); | ||||
| 
 | ||||
|     main_thread_stack_top += main_thread_stack_size; | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -16,7 +16,6 @@ | ||||
| #include "core/hle/kernel/mutex.h" | ||||
| #include "core/hle/kernel/process_capability.h" | ||||
| #include "core/hle/kernel/synchronization_object.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace Core { | ||||
| @ -36,6 +35,10 @@ class TLSPage; | ||||
| 
 | ||||
| struct CodeSet; | ||||
| 
 | ||||
| namespace Memory { | ||||
| class PageTable; | ||||
| } | ||||
| 
 | ||||
| enum class MemoryRegion : u16 { | ||||
|     APPLICATION = 1, | ||||
|     SYSTEM = 2, | ||||
| @ -100,14 +103,14 @@ public: | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     /// Gets a reference to the process' memory manager.
 | ||||
|     Kernel::VMManager& VMManager() { | ||||
|         return vm_manager; | ||||
|     /// Gets a reference to the process' page table.
 | ||||
|     Memory::PageTable& PageTable() { | ||||
|         return *page_table; | ||||
|     } | ||||
| 
 | ||||
|     /// Gets a const reference to the process' memory manager.
 | ||||
|     const Kernel::VMManager& VMManager() const { | ||||
|         return vm_manager; | ||||
|     /// Gets const a reference to the process' page table.
 | ||||
|     const Memory::PageTable& PageTable() const { | ||||
|         return *page_table; | ||||
|     } | ||||
| 
 | ||||
|     /// Gets a reference to the process' handle table.
 | ||||
| @ -273,7 +276,7 @@ public: | ||||
|      * @returns RESULT_SUCCESS if all relevant metadata was able to be | ||||
|      *          loaded and parsed. Otherwise, an error code is returned. | ||||
|      */ | ||||
|     ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata); | ||||
|     ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Starts the main application thread for this process. | ||||
| @ -289,7 +292,7 @@ public: | ||||
|      */ | ||||
|     void PrepareForTermination(); | ||||
| 
 | ||||
|     void LoadModule(CodeSet module_, VAddr base_addr); | ||||
|     void LoadModule(CodeSet code_set, VAddr base_addr); | ||||
| 
 | ||||
|     ///////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
|     // Thread-local storage management
 | ||||
| @ -313,16 +316,10 @@ private: | ||||
|     void ChangeStatus(ProcessStatus new_status); | ||||
| 
 | ||||
|     /// Allocates the main thread stack for the process, given the stack size in bytes.
 | ||||
|     void AllocateMainThreadStack(u64 stack_size); | ||||
|     ResultCode AllocateMainThreadStack(std::size_t stack_size); | ||||
| 
 | ||||
|     /// Memory manager for this process.
 | ||||
|     Kernel::VMManager vm_manager; | ||||
| 
 | ||||
|     /// Size of the main thread's stack in bytes.
 | ||||
|     u64 main_thread_stack_size = 0; | ||||
| 
 | ||||
|     /// Size of the loaded code memory in bytes.
 | ||||
|     u64 code_memory_size = 0; | ||||
|     /// Memory manager for this process
 | ||||
|     std::unique_ptr<Memory::PageTable> page_table; | ||||
| 
 | ||||
|     /// Current status of the process
 | ||||
|     ProcessStatus status{}; | ||||
| @ -390,6 +387,18 @@ private: | ||||
| 
 | ||||
|     /// Name of this process
 | ||||
|     std::string name; | ||||
| 
 | ||||
|     /// Address of the top of the main thread's stack
 | ||||
|     VAddr main_thread_stack_top{}; | ||||
| 
 | ||||
|     /// Size of the main thread's stack
 | ||||
|     std::size_t main_thread_stack_size{}; | ||||
| 
 | ||||
|     /// Memory usage capacity for the process
 | ||||
|     std::size_t memory_usage_capacity{}; | ||||
| 
 | ||||
|     /// Process total image size
 | ||||
|     std::size_t image_size{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -5,8 +5,8 @@ | ||||
| #include "common/bit_util.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process_capability.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| namespace { | ||||
| @ -66,7 +66,7 @@ u32 GetFlagBitOffset(CapabilityType type) { | ||||
| 
 | ||||
| ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities, | ||||
|                                                            std::size_t num_capabilities, | ||||
|                                                            VMManager& vm_manager) { | ||||
|                                                            Memory::PageTable& page_table) { | ||||
|     Clear(); | ||||
| 
 | ||||
|     // Allow all cores and priorities.
 | ||||
| @ -74,15 +74,15 @@ ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabiliti | ||||
|     priority_mask = 0xFFFFFFFFFFFFFFFF; | ||||
|     kernel_version = PackedKernelVersion; | ||||
| 
 | ||||
|     return ParseCapabilities(capabilities, num_capabilities, vm_manager); | ||||
|     return ParseCapabilities(capabilities, num_capabilities, page_table); | ||||
| } | ||||
| 
 | ||||
| ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities, | ||||
|                                                          std::size_t num_capabilities, | ||||
|                                                          VMManager& vm_manager) { | ||||
|                                                          Memory::PageTable& page_table) { | ||||
|     Clear(); | ||||
| 
 | ||||
|     return ParseCapabilities(capabilities, num_capabilities, vm_manager); | ||||
|     return ParseCapabilities(capabilities, num_capabilities, page_table); | ||||
| } | ||||
| 
 | ||||
| void ProcessCapabilities::InitializeForMetadatalessProcess() { | ||||
| @ -105,7 +105,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() { | ||||
| 
 | ||||
| ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities, | ||||
|                                                   std::size_t num_capabilities, | ||||
|                                                   VMManager& vm_manager) { | ||||
|                                                   Memory::PageTable& page_table) { | ||||
|     u32 set_flags = 0; | ||||
|     u32 set_svc_bits = 0; | ||||
| 
 | ||||
| @ -127,13 +127,13 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities, | ||||
|                 return ERR_INVALID_COMBINATION; | ||||
|             } | ||||
| 
 | ||||
|             const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager); | ||||
|             const auto result = HandleMapPhysicalFlags(descriptor, size_flags, page_table); | ||||
|             if (result.IsError()) { | ||||
|                 return result; | ||||
|             } | ||||
|         } else { | ||||
|             const auto result = | ||||
|                 ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager); | ||||
|                 ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, page_table); | ||||
|             if (result.IsError()) { | ||||
|                 return result; | ||||
|             } | ||||
| @ -144,7 +144,7 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities, | ||||
| } | ||||
| 
 | ||||
| ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, | ||||
|                                                           u32 flag, VMManager& vm_manager) { | ||||
|                                                           u32 flag, Memory::PageTable& page_table) { | ||||
|     const auto type = GetCapabilityType(flag); | ||||
| 
 | ||||
|     if (type == CapabilityType::Unset) { | ||||
| @ -172,7 +172,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s | ||||
|     case CapabilityType::Syscall: | ||||
|         return HandleSyscallFlags(set_svc_bits, flag); | ||||
|     case CapabilityType::MapIO: | ||||
|         return HandleMapIOFlags(flag, vm_manager); | ||||
|         return HandleMapIOFlags(flag, page_table); | ||||
|     case CapabilityType::Interrupt: | ||||
|         return HandleInterruptFlags(flag); | ||||
|     case CapabilityType::ProgramType: | ||||
| @ -269,12 +269,12 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) | ||||
| } | ||||
| 
 | ||||
| ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, | ||||
|                                                        VMManager& vm_manager) { | ||||
|                                                        Memory::PageTable& page_table) { | ||||
|     // TODO(Lioncache): Implement once the memory manager can handle this.
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) { | ||||
| ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, Memory::PageTable& page_table) { | ||||
|     // TODO(Lioncache): Implement once the memory manager can handle this.
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| @ -12,7 +12,9 @@ union ResultCode; | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class VMManager; | ||||
| namespace Memory { | ||||
| class PageTable; | ||||
| } | ||||
| 
 | ||||
| /// The possible types of programs that may be indicated
 | ||||
| /// by the program type capability descriptor.
 | ||||
| @ -81,27 +83,27 @@ public: | ||||
|     ///
 | ||||
|     /// @param capabilities     The capabilities to parse
 | ||||
|     /// @param num_capabilities The number of capabilities to parse.
 | ||||
|     /// @param vm_manager       The memory manager to use for handling any mapping-related
 | ||||
|     /// @param page_table       The memory manager to use for handling any mapping-related
 | ||||
|     ///                         operations (such as mapping IO memory, etc).
 | ||||
|     ///
 | ||||
|     /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
 | ||||
|     ///          otherwise, an error code upon failure.
 | ||||
|     ///
 | ||||
|     ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, | ||||
|                                           VMManager& vm_manager); | ||||
|                                           Memory::PageTable& page_table); | ||||
| 
 | ||||
|     /// Initializes this process capabilities instance for a userland process.
 | ||||
|     ///
 | ||||
|     /// @param capabilities     The capabilities to parse.
 | ||||
|     /// @param num_capabilities The total number of capabilities to parse.
 | ||||
|     /// @param vm_manager       The memory manager to use for handling any mapping-related
 | ||||
|     /// @param page_table       The memory manager to use for handling any mapping-related
 | ||||
|     ///                         operations (such as mapping IO memory, etc).
 | ||||
|     ///
 | ||||
|     /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
 | ||||
|     ///          otherwise, an error code upon failure.
 | ||||
|     ///
 | ||||
|     ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, | ||||
|                                         VMManager& vm_manager); | ||||
|                                         Memory::PageTable& page_table); | ||||
| 
 | ||||
|     /// Initializes this process capabilities instance for a process that does not
 | ||||
|     /// have any metadata to parse.
 | ||||
| @ -181,13 +183,13 @@ private: | ||||
|     ///
 | ||||
|     /// @param capabilities     The sequence of capability descriptors to parse.
 | ||||
|     /// @param num_capabilities The number of descriptors within the given sequence.
 | ||||
|     /// @param vm_manager       The memory manager that will perform any memory
 | ||||
|     /// @param page_table       The memory manager that will perform any memory
 | ||||
|     ///                         mapping if necessary.
 | ||||
|     ///
 | ||||
|     /// @return RESULT_SUCCESS if no errors occur, otherwise an error code.
 | ||||
|     ///
 | ||||
|     ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, | ||||
|                                  VMManager& vm_manager); | ||||
|                                  Memory::PageTable& page_table); | ||||
| 
 | ||||
|     /// Attempts to parse a capability descriptor that is only represented by a
 | ||||
|     /// single flag set.
 | ||||
| @ -196,13 +198,13 @@ private: | ||||
|     ///                     flags being initialized more than once when they shouldn't be.
 | ||||
|     /// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
 | ||||
|     /// @param flag         The flag to attempt to parse.
 | ||||
|     /// @param vm_manager   The memory manager that will perform any memory
 | ||||
|     /// @param page_table   The memory manager that will perform any memory
 | ||||
|     ///                     mapping if necessary.
 | ||||
|     ///
 | ||||
|     /// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.
 | ||||
|     ///
 | ||||
|     ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, | ||||
|                                          VMManager& vm_manager); | ||||
|                                          Memory::PageTable& page_table); | ||||
| 
 | ||||
|     /// Clears the internal state of this process capability instance. Necessary,
 | ||||
|     /// to have a sane starting point due to us allowing running executables without
 | ||||
| @ -226,10 +228,10 @@ private: | ||||
|     ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags); | ||||
| 
 | ||||
|     /// Handles flags related to mapping physical memory pages.
 | ||||
|     ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager); | ||||
|     ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, Memory::PageTable& page_table); | ||||
| 
 | ||||
|     /// Handles flags related to mapping IO pages.
 | ||||
|     ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager); | ||||
|     ResultCode HandleMapIOFlags(u32 flags, Memory::PageTable& page_table); | ||||
| 
 | ||||
|     /// Handles flags related to the interrupt capability flags.
 | ||||
|     ResultCode HandleInterruptFlags(u32 flags); | ||||
|  | ||||
| @ -16,26 +16,60 @@ constexpr std::size_t ResourceTypeToIndex(ResourceType type) { | ||||
| ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {} | ||||
| ResourceLimit::~ResourceLimit() = default; | ||||
| 
 | ||||
| bool ResourceLimit::Reserve(ResourceType resource, s64 amount) { | ||||
|     return Reserve(resource, amount, 10000000000); | ||||
| } | ||||
| 
 | ||||
| bool ResourceLimit::Reserve(ResourceType resource, s64 amount, u64 timeout) { | ||||
|     const std::size_t index{ResourceTypeToIndex(resource)}; | ||||
| 
 | ||||
|     s64 new_value = current[index] + amount; | ||||
|     while (new_value > limit[index] && available[index] + amount <= limit[index]) { | ||||
|         // TODO(bunnei): This is wrong for multicore, we should wait the calling thread for timeout
 | ||||
|         new_value = current[index] + amount; | ||||
| 
 | ||||
|         if (timeout >= 0) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (new_value <= limit[index]) { | ||||
|         current[index] = new_value; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void ResourceLimit::Release(ResourceType resource, u64 amount) { | ||||
|     Release(resource, amount, amount); | ||||
| } | ||||
| 
 | ||||
| void ResourceLimit::Release(ResourceType resource, u64 used_amount, u64 available_amount) { | ||||
|     const std::size_t index{ResourceTypeToIndex(resource)}; | ||||
| 
 | ||||
|     current[index] -= used_amount; | ||||
|     available[index] -= available_amount; | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) { | ||||
|     return std::make_shared<ResourceLimit>(kernel); | ||||
| } | ||||
| 
 | ||||
| s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { | ||||
|     return values.at(ResourceTypeToIndex(resource)); | ||||
|     return limit.at(ResourceTypeToIndex(resource)) - current.at(ResourceTypeToIndex(resource)); | ||||
| } | ||||
| 
 | ||||
| s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { | ||||
|     return limits.at(ResourceTypeToIndex(resource)); | ||||
|     return limit.at(ResourceTypeToIndex(resource)); | ||||
| } | ||||
| 
 | ||||
| ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) { | ||||
|     const auto index = ResourceTypeToIndex(resource); | ||||
| 
 | ||||
|     if (value < values[index]) { | ||||
|     const std::size_t index{ResourceTypeToIndex(resource)}; | ||||
|     if (current[index] <= value) { | ||||
|         limit[index] = value; | ||||
|         return RESULT_SUCCESS; | ||||
|     } else { | ||||
|         return ERR_INVALID_STATE; | ||||
|     } | ||||
| 
 | ||||
|     values[index] = value; | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -51,6 +51,11 @@ public: | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     bool Reserve(ResourceType resource, s64 amount); | ||||
|     bool Reserve(ResourceType resource, s64 amount, u64 timeout); | ||||
|     void Release(ResourceType resource, u64 amount); | ||||
|     void Release(ResourceType resource, u64 used_amount, u64 available_amount); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets the current value for the specified resource. | ||||
|      * @param resource Requested resource type | ||||
| @ -91,10 +96,9 @@ private: | ||||
|     using ResourceArray = | ||||
|         std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>; | ||||
| 
 | ||||
|     /// Maximum values a resource type may reach.
 | ||||
|     ResourceArray limits{}; | ||||
|     /// Current resource limit values.
 | ||||
|     ResourceArray values{}; | ||||
|     ResourceArray limit{}; | ||||
|     ResourceArray current{}; | ||||
|     ResourceArray available{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -134,7 +134,8 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) { | ||||
| ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, | ||||
|                                            Core::Memory::Memory& memory) { | ||||
|     u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))}; | ||||
|     std::shared_ptr<Kernel::HLERequestContext> context{ | ||||
|         std::make_shared<Kernel::HLERequestContext>(SharedFrom(this), std::move(thread))}; | ||||
| @ -178,7 +179,7 @@ ResultCode ServerSession::CompleteSyncRequest() { | ||||
| } | ||||
| 
 | ||||
| ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | ||||
|                                             Memory::Memory& memory) { | ||||
|                                             Core::Memory::Memory& memory) { | ||||
|     Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {}); | ||||
|     return QueueSyncRequest(std::move(thread), memory); | ||||
| } | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
| #include "core/hle/kernel/synchronization_object.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
| @ -92,7 +92,7 @@ public: | ||||
|      * | ||||
|      * @returns ResultCode from the operation. | ||||
|      */ | ||||
|     ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory); | ||||
|     ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); | ||||
| 
 | ||||
|     bool ShouldWait(const Thread* thread) const override; | ||||
| 
 | ||||
| @ -126,7 +126,7 @@ public: | ||||
| 
 | ||||
| private: | ||||
|     /// Queues a sync request from the emulated application.
 | ||||
|     ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory); | ||||
|     ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); | ||||
| 
 | ||||
|     /// Completes a sync request from the emulated application.
 | ||||
|     ResultCode CompleteSyncRequest(); | ||||
|  | ||||
| @ -2,149 +2,56 @@ | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {} | ||||
| SharedMemory::SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory) | ||||
|     : Object{kernel}, device_memory{device_memory} {} | ||||
| 
 | ||||
| SharedMemory::~SharedMemory() = default; | ||||
| 
 | ||||
| std::shared_ptr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_process, | ||||
|                                                    u64 size, MemoryPermission permissions, | ||||
|                                                    MemoryPermission other_permissions, | ||||
|                                                    VAddr address, MemoryRegion region, | ||||
|                                                    std::string name) { | ||||
|     std::shared_ptr<SharedMemory> shared_memory = std::make_shared<SharedMemory>(kernel); | ||||
| std::shared_ptr<SharedMemory> SharedMemory::Create( | ||||
|     KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process, | ||||
|     Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission, | ||||
|     Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size, | ||||
|     std::string name) { | ||||
| 
 | ||||
|     std::shared_ptr<SharedMemory> shared_memory{ | ||||
|         std::make_shared<SharedMemory>(kernel, device_memory)}; | ||||
| 
 | ||||
|     shared_memory->owner_process = owner_process; | ||||
|     shared_memory->name = std::move(name); | ||||
|     shared_memory->page_list = std::move(page_list); | ||||
|     shared_memory->owner_permission = owner_permission; | ||||
|     shared_memory->user_permission = user_permission; | ||||
|     shared_memory->physical_address = physical_address; | ||||
|     shared_memory->size = size; | ||||
|     shared_memory->permissions = permissions; | ||||
|     shared_memory->other_permissions = other_permissions; | ||||
| 
 | ||||
|     if (address == 0) { | ||||
|         shared_memory->backing_block = std::make_shared<Kernel::PhysicalMemory>(size); | ||||
|         shared_memory->backing_block_offset = 0; | ||||
| 
 | ||||
|         // Refresh the address mappings for the current process.
 | ||||
|         if (kernel.CurrentProcess() != nullptr) { | ||||
|             kernel.CurrentProcess()->VMManager().RefreshMemoryBlockMappings( | ||||
|                 shared_memory->backing_block.get()); | ||||
|         } | ||||
|     } else { | ||||
|         const auto& vm_manager = shared_memory->owner_process->VMManager(); | ||||
| 
 | ||||
|         // The memory is already available and mapped in the owner process.
 | ||||
|         const auto vma = vm_manager.FindVMA(address); | ||||
|         ASSERT_MSG(vm_manager.IsValidHandle(vma), "Invalid memory address"); | ||||
|         ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); | ||||
| 
 | ||||
|         // The returned VMA might be a bigger one encompassing the desired address.
 | ||||
|         const auto vma_offset = address - vma->first; | ||||
|         ASSERT_MSG(vma_offset + size <= vma->second.size, | ||||
|                    "Shared memory exceeds bounds of mapped block"); | ||||
| 
 | ||||
|         shared_memory->backing_block = vma->second.backing_block; | ||||
|         shared_memory->backing_block_offset = vma->second.offset + vma_offset; | ||||
|     } | ||||
| 
 | ||||
|     shared_memory->base_address = address; | ||||
|     shared_memory->name = name; | ||||
| 
 | ||||
|     return shared_memory; | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<SharedMemory> SharedMemory::CreateForApplet( | ||||
|     KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset, | ||||
|     u64 size, MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { | ||||
|     std::shared_ptr<SharedMemory> shared_memory = std::make_shared<SharedMemory>(kernel); | ||||
| ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size, | ||||
|                              Memory::MemoryPermission permission) { | ||||
|     const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize}; | ||||
| 
 | ||||
|     shared_memory->owner_process = nullptr; | ||||
|     shared_memory->name = std::move(name); | ||||
|     shared_memory->size = size; | ||||
|     shared_memory->permissions = permissions; | ||||
|     shared_memory->other_permissions = other_permissions; | ||||
|     shared_memory->backing_block = std::move(heap_block); | ||||
|     shared_memory->backing_block_offset = offset; | ||||
|     shared_memory->base_address = | ||||
|         kernel.CurrentProcess()->VMManager().GetHeapRegionBaseAddress() + offset; | ||||
| 
 | ||||
|     return shared_memory; | ||||
| } | ||||
| 
 | ||||
| ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermission permissions, | ||||
|                              MemoryPermission other_permissions) { | ||||
|     const MemoryPermission own_other_permissions = | ||||
|         &target_process == owner_process ? this->permissions : this->other_permissions; | ||||
| 
 | ||||
|     // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
 | ||||
|     if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { | ||||
|         return ERR_INVALID_MEMORY_PERMISSIONS; | ||||
|     if (page_list.GetNumPages() != page_count) { | ||||
|         UNIMPLEMENTED_MSG("Page count does not match"); | ||||
|     } | ||||
| 
 | ||||
|     // Error out if the requested permissions don't match what the creator process allows.
 | ||||
|     if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { | ||||
|         LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", | ||||
|                   GetObjectId(), address, name); | ||||
|         return ERR_INVALID_MEMORY_PERMISSIONS; | ||||
|     Memory::MemoryPermission expected = | ||||
|         &target_process == owner_process ? owner_permission : user_permission; | ||||
| 
 | ||||
|     if (permission != expected) { | ||||
|         UNIMPLEMENTED_MSG("Permission does not match"); | ||||
|     } | ||||
| 
 | ||||
|     // Error out if the provided permissions are not compatible with what the creator process needs.
 | ||||
|     if (other_permissions != MemoryPermission::DontCare && | ||||
|         static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) { | ||||
|         LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", | ||||
|                   GetObjectId(), address, name); | ||||
|         return ERR_INVALID_MEMORY_PERMISSIONS; | ||||
|     } | ||||
| 
 | ||||
|     VAddr target_address = address; | ||||
| 
 | ||||
|     // Map the memory block into the target process
 | ||||
|     auto result = target_process.VMManager().MapMemoryBlock( | ||||
|         target_address, backing_block, backing_block_offset, size, MemoryState::Shared); | ||||
|     if (result.Failed()) { | ||||
|         LOG_ERROR( | ||||
|             Kernel, | ||||
|             "cannot map id={}, target_address=0x{:X} name={}, error mapping to virtual memory", | ||||
|             GetObjectId(), target_address, name); | ||||
|         return result.Code(); | ||||
|     } | ||||
| 
 | ||||
|     return target_process.VMManager().ReprotectRange(target_address, size, | ||||
|                                                      ConvertPermissions(permissions)); | ||||
| } | ||||
| 
 | ||||
| ResultCode SharedMemory::Unmap(Process& target_process, VAddr address, u64 unmap_size) { | ||||
|     if (unmap_size != size) { | ||||
|         LOG_ERROR(Kernel, | ||||
|                   "Invalid size passed to Unmap. Size must be equal to the size of the " | ||||
|                   "memory managed. Shared memory size=0x{:016X}, Unmap size=0x{:016X}", | ||||
|                   size, unmap_size); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     // TODO(Subv): Verify what happens if the application tries to unmap an address that is not
 | ||||
|     // mapped to a SharedMemory.
 | ||||
|     return target_process.VMManager().UnmapRange(address, size); | ||||
| } | ||||
| 
 | ||||
| VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { | ||||
|     u32 masked_permissions = | ||||
|         static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute); | ||||
|     return static_cast<VMAPermission>(masked_permissions); | ||||
| } | ||||
| 
 | ||||
| u8* SharedMemory::GetPointer(std::size_t offset) { | ||||
|     return backing_block->data() + backing_block_offset + offset; | ||||
| } | ||||
| 
 | ||||
| const u8* SharedMemory::GetPointer(std::size_t offset) const { | ||||
|     return backing_block->data() + backing_block_offset + offset; | ||||
|     return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared, | ||||
|                                                permission); | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -8,8 +8,10 @@ | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "core/device_memory.h" | ||||
| #include "core/hle/kernel/memory/memory_block.h" | ||||
| #include "core/hle/kernel/memory/page_linked_list.h" | ||||
| #include "core/hle/kernel/object.h" | ||||
| #include "core/hle/kernel/physical_memory.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| @ -17,63 +19,21 @@ namespace Kernel { | ||||
| 
 | ||||
| class KernelCore; | ||||
| 
 | ||||
| /// Permissions for mapped shared memory blocks
 | ||||
| enum class MemoryPermission : u32 { | ||||
|     None = 0, | ||||
|     Read = (1u << 0), | ||||
|     Write = (1u << 1), | ||||
|     ReadWrite = (Read | Write), | ||||
|     Execute = (1u << 2), | ||||
|     ReadExecute = (Read | Execute), | ||||
|     WriteExecute = (Write | Execute), | ||||
|     ReadWriteExecute = (Read | Write | Execute), | ||||
|     DontCare = (1u << 28) | ||||
| }; | ||||
| 
 | ||||
| class SharedMemory final : public Object { | ||||
| public: | ||||
|     explicit SharedMemory(KernelCore& kernel); | ||||
|     explicit SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory); | ||||
|     ~SharedMemory() override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Creates a shared memory object. | ||||
|      * @param kernel The kernel instance to create a shared memory instance under. | ||||
|      * @param owner_process Process that created this shared memory object. | ||||
|      * @param size Size of the memory block. Must be page-aligned. | ||||
|      * @param permissions Permission restrictions applied to the process which created the block. | ||||
|      * @param other_permissions Permission restrictions applied to other processes mapping the | ||||
|      * block. | ||||
|      * @param address The address from which to map the Shared Memory. | ||||
|      * @param region If the address is 0, the shared memory will be allocated in this region of the | ||||
|      * linear heap. | ||||
|      * @param name Optional object name, used for debugging purposes. | ||||
|      */ | ||||
|     static std::shared_ptr<SharedMemory> Create(KernelCore& kernel, Process* owner_process, | ||||
|                                                 u64 size, MemoryPermission permissions, | ||||
|                                                 MemoryPermission other_permissions, | ||||
|                                                 VAddr address = 0, | ||||
|                                                 MemoryRegion region = MemoryRegion::BASE, | ||||
|                                                 std::string name = "Unknown"); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Creates a shared memory object from a block of memory managed by an HLE applet. | ||||
|      * @param kernel The kernel instance to create a shared memory instance under. | ||||
|      * @param heap_block Heap block of the HLE applet. | ||||
|      * @param offset The offset into the heap block that the SharedMemory will map. | ||||
|      * @param size Size of the memory block. Must be page-aligned. | ||||
|      * @param permissions Permission restrictions applied to the process which created the block. | ||||
|      * @param other_permissions Permission restrictions applied to other processes mapping the | ||||
|      * block. | ||||
|      * @param name Optional object name, used for debugging purposes. | ||||
|      */ | ||||
|     static std::shared_ptr<SharedMemory> CreateForApplet( | ||||
|         KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset, | ||||
|         u64 size, MemoryPermission permissions, MemoryPermission other_permissions, | ||||
|         std::string name = "Unknown Applet"); | ||||
|     static std::shared_ptr<SharedMemory> Create( | ||||
|         KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process, | ||||
|         Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission, | ||||
|         Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size, | ||||
|         std::string name); | ||||
| 
 | ||||
|     std::string GetTypeName() const override { | ||||
|         return "SharedMemory"; | ||||
|     } | ||||
| 
 | ||||
|     std::string GetName() const override { | ||||
|         return name; | ||||
|     } | ||||
| @ -83,71 +43,42 @@ public: | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     /// Gets the size of the underlying memory block in bytes.
 | ||||
|     u64 GetSize() const { | ||||
|         return size; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Converts the specified MemoryPermission into the equivalent VMAPermission. | ||||
|      * @param permission The MemoryPermission to convert. | ||||
|      */ | ||||
|     static VMAPermission ConvertPermissions(MemoryPermission permission); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Maps a shared memory block to an address in the target process' address space | ||||
|      * @param target_process Process on which to map the memory block. | ||||
|      * @param target_process Process on which to map the memory block | ||||
|      * @param address Address in system memory to map shared memory block to | ||||
|      * @param size Size of the shared memory block to map | ||||
|      * @param permissions Memory block map permissions (specified by SVC field) | ||||
|      * @param other_permissions Memory block map other permissions (specified by SVC field) | ||||
|      */ | ||||
|     ResultCode Map(Process& target_process, VAddr address, MemoryPermission permissions, | ||||
|                    MemoryPermission other_permissions); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Unmaps a shared memory block from the specified address in system memory | ||||
|      * | ||||
|      * @param target_process Process from which to unmap the memory block. | ||||
|      * @param address        Address in system memory where the shared memory block is mapped. | ||||
|      * @param unmap_size     The amount of bytes to unmap from this shared memory instance. | ||||
|      * | ||||
|      * @return Result code of the unmap operation | ||||
|      * | ||||
|      * @pre The given size to unmap must be the same size as the amount of memory managed by | ||||
|      *      the SharedMemory instance itself, otherwise ERR_INVALID_SIZE will be returned. | ||||
|      */ | ||||
|     ResultCode Unmap(Process& target_process, VAddr address, u64 unmap_size); | ||||
|     ResultCode Map(Process& target_process, VAddr address, std::size_t size, | ||||
|                    Memory::MemoryPermission permission); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets a pointer to the shared memory block | ||||
|      * @param offset Offset from the start of the shared memory block to get pointer | ||||
|      * @return A pointer to the shared memory block from the specified offset | ||||
|      */ | ||||
|     u8* GetPointer(std::size_t offset = 0); | ||||
|     u8* GetPointer(std::size_t offset = 0) { | ||||
|         return device_memory.GetPointer(physical_address + offset); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets a constant pointer to the shared memory block | ||||
|      * Gets a pointer to the shared memory block | ||||
|      * @param offset Offset from the start of the shared memory block to get pointer | ||||
|      * @return A constant pointer to the shared memory block from the specified offset | ||||
|      * @return A pointer to the shared memory block from the specified offset | ||||
|      */ | ||||
|     const u8* GetPointer(std::size_t offset = 0) const; | ||||
|     const u8* GetPointer(std::size_t offset = 0) const { | ||||
|         return device_memory.GetPointer(physical_address + offset); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     /// Backing memory for this shared memory block.
 | ||||
|     std::shared_ptr<PhysicalMemory> backing_block; | ||||
|     /// Offset into the backing block for this shared memory.
 | ||||
|     std::size_t backing_block_offset = 0; | ||||
|     /// Size of the memory block. Page-aligned.
 | ||||
|     u64 size = 0; | ||||
|     /// Permission restrictions applied to the process which created the block.
 | ||||
|     MemoryPermission permissions{}; | ||||
|     /// Permission restrictions applied to other processes mapping the block.
 | ||||
|     MemoryPermission other_permissions{}; | ||||
|     /// Process that created this shared memory block.
 | ||||
|     Process* owner_process; | ||||
|     /// Address of shared memory block in the owner process if specified.
 | ||||
|     VAddr base_address = 0; | ||||
|     /// Name of shared memory object.
 | ||||
|     Core::DeviceMemory& device_memory; | ||||
|     Process* owner_process{}; | ||||
|     Memory::PageLinkedList page_list; | ||||
|     Memory::MemoryPermission owner_permission{}; | ||||
|     Memory::MemoryPermission user_permission{}; | ||||
|     PAddr physical_address{}; | ||||
|     std::size_t size{}; | ||||
|     std::string name; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -24,6 +24,8 @@ | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory/memory_block.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/readable_event.h" | ||||
| @ -31,6 +33,7 @@ | ||||
| #include "core/hle/kernel/scheduler.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/kernel/svc.h" | ||||
| #include "core/hle/kernel/svc_types.h" | ||||
| #include "core/hle/kernel/svc_wrap.h" | ||||
| #include "core/hle/kernel/synchronization.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| @ -42,7 +45,7 @@ | ||||
| #include "core/memory.h" | ||||
| #include "core/reporter.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| namespace Kernel::Svc { | ||||
| namespace { | ||||
| 
 | ||||
| // Checks if address + size is greater than the given address
 | ||||
| @ -58,8 +61,8 @@ constexpr u64 MAIN_MEMORY_SIZE = 0x200000000; | ||||
| // Helper function that performs the common sanity checks for svcMapMemory
 | ||||
| // and svcUnmapMemory. This is doable, as both functions perform their sanitizing
 | ||||
| // in the same order.
 | ||||
| ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr, | ||||
|                                       u64 size) { | ||||
| ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr dst_addr, | ||||
|                                       VAddr src_addr, u64 size) { | ||||
|     if (!Common::Is4KBAligned(dst_addr)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
| @ -93,36 +96,33 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     if (!vm_manager.IsWithinAddressSpace(src_addr, size)) { | ||||
|     if (!manager.IsInsideAddressSpace(src_addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", | ||||
|                   src_addr, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     if (!vm_manager.IsWithinStackRegion(dst_addr, size)) { | ||||
|     if (manager.IsOutsideStackRegion(dst_addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}", | ||||
|                   dst_addr, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     const VAddr dst_end_address = dst_addr + size; | ||||
|     if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() && | ||||
|         vm_manager.GetHeapRegionEndAddress() > dst_addr) { | ||||
|     if (manager.IsInsideHeapRegion(dst_addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Destination does not fit within the heap region, addr=0x{:016X}, " | ||||
|                   "size=0x{:016X}, end_addr=0x{:016X}", | ||||
|                   dst_addr, size, dst_end_address); | ||||
|                   "size=0x{:016X}", | ||||
|                   dst_addr, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     if (dst_end_address > vm_manager.GetMapRegionBaseAddress() && | ||||
|         vm_manager.GetMapRegionEndAddress() > dst_addr) { | ||||
|     if (manager.IsInsideAliasRegion(dst_addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Destination does not fit within the map region, addr=0x{:016X}, " | ||||
|                   "size=0x{:016X}, end_addr=0x{:016X}", | ||||
|                   dst_addr, size, dst_end_address); | ||||
|                   "size=0x{:016X}", | ||||
|                   dst_addr, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
| @ -177,13 +177,10 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_s | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); | ||||
|     const auto alloc_result = vm_manager.SetHeapSize(heap_size); | ||||
|     if (alloc_result.Failed()) { | ||||
|         return alloc_result.Code(); | ||||
|     } | ||||
|     auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | ||||
| 
 | ||||
|     CASCADE_RESULT(*heap_addr, page_table.SetHeapSize(heap_size)); | ||||
| 
 | ||||
|     *heap_addr = *alloc_result; | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| @ -194,63 +191,6 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) { | ||||
|     LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot); | ||||
| 
 | ||||
|     if (!Common::Is4KBAligned(addr)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
| 
 | ||||
|     if (size == 0) { | ||||
|         LOG_ERROR(Kernel_SVC, "Size is 0"); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     if (!Common::Is4KBAligned(size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     if (!IsValidAddressRange(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}", | ||||
|                   addr, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     const auto permission = static_cast<MemoryPermission>(prot); | ||||
|     if (permission != MemoryPermission::None && permission != MemoryPermission::Read && | ||||
|         permission != MemoryPermission::ReadWrite) { | ||||
|         LOG_ERROR(Kernel_SVC, "Invalid memory permission specified, Got memory permission=0x{:08X}", | ||||
|                   static_cast<u32>(permission)); | ||||
|         return ERR_INVALID_MEMORY_PERMISSIONS; | ||||
|     } | ||||
| 
 | ||||
|     auto* const current_process = system.Kernel().CurrentProcess(); | ||||
|     auto& vm_manager = current_process->VMManager(); | ||||
| 
 | ||||
|     if (!vm_manager.IsWithinAddressSpace(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, | ||||
|                   size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     const VMManager::VMAHandle iter = vm_manager.FindVMA(addr); | ||||
|     if (!vm_manager.IsValidHandle(iter)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented."); | ||||
|     // TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
 | ||||
|     // make sense to allow changing permissions on kernel memory itself, etc).
 | ||||
| 
 | ||||
|     const auto converted_permissions = SharedMemory::ConvertPermissions(permission); | ||||
| 
 | ||||
|     return vm_manager.ReprotectRange(addr, size, converted_permissions); | ||||
| } | ||||
| 
 | ||||
| static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, | ||||
|                                      u32 attribute) { | ||||
|     LOG_DEBUG(Kernel_SVC, | ||||
| @ -274,30 +214,19 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     const auto mem_attribute = static_cast<MemoryAttribute>(attribute); | ||||
|     const auto mem_mask = static_cast<MemoryAttribute>(mask); | ||||
|     const auto attribute_with_mask = mem_attribute | mem_mask; | ||||
| 
 | ||||
|     if (attribute_with_mask != mem_mask) { | ||||
|     const auto attributes{static_cast<Memory::MemoryAttribute>(mask | attribute)}; | ||||
|     if (attributes != static_cast<Memory::MemoryAttribute>(mask) || | ||||
|         (attributes | Memory::MemoryAttribute::Uncached) != Memory::MemoryAttribute::Uncached) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}", | ||||
|                   attribute, mask); | ||||
|         return ERR_INVALID_COMBINATION; | ||||
|     } | ||||
| 
 | ||||
|     if ((attribute_with_mask | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) { | ||||
|         LOG_ERROR(Kernel_SVC, "Specified attribute isn't equal to MemoryAttributeUncached (8)."); | ||||
|         return ERR_INVALID_COMBINATION; | ||||
|     } | ||||
|     auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | ||||
| 
 | ||||
|     auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); | ||||
|     if (!vm_manager.IsWithinAddressSpace(address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Given address (0x{:016X}) is outside the bounds of the address space.", address); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     return vm_manager.SetMemoryAttribute(address, size, mem_mask, mem_attribute); | ||||
|     return page_table.SetMemoryAttribute(address, size, static_cast<Memory::MemoryAttribute>(mask), | ||||
|                                          static_cast<Memory::MemoryAttribute>(attribute)); | ||||
| } | ||||
| 
 | ||||
| /// Maps a memory range into a different range.
 | ||||
| @ -305,14 +234,14 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr | ||||
|     LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | ||||
|               src_addr, size); | ||||
| 
 | ||||
|     auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); | ||||
|     const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); | ||||
|     auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | ||||
| 
 | ||||
|     if (result.IsError()) { | ||||
|     if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; | ||||
|         result.IsError()) { | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     return vm_manager.MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack); | ||||
|     return page_table.Map(dst_addr, src_addr, size); | ||||
| } | ||||
| 
 | ||||
| /// Unmaps a region that was previously mapped with svcMapMemory
 | ||||
| @ -320,21 +249,14 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad | ||||
|     LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | ||||
|               src_addr, size); | ||||
| 
 | ||||
|     auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); | ||||
|     const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); | ||||
|     auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; | ||||
| 
 | ||||
|     if (result.IsError()) { | ||||
|     if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; | ||||
|         result.IsError()) { | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     const auto unmap_res = vm_manager.UnmapRange(dst_addr, size); | ||||
| 
 | ||||
|     // Reprotect the source mapping on success
 | ||||
|     if (unmap_res.IsSuccess()) { | ||||
|         ASSERT(vm_manager.ReprotectRange(src_addr, size, VMAPermission::ReadWrite).IsSuccess()); | ||||
|     } | ||||
| 
 | ||||
|     return unmap_res; | ||||
|     return page_table.Unmap(dst_addr, src_addr, size); | ||||
| } | ||||
| 
 | ||||
| /// Connect to an OS service given the port name, returns the handle to the port to out
 | ||||
| @ -367,6 +289,8 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, | ||||
|         return ERR_NOT_FOUND; | ||||
|     } | ||||
| 
 | ||||
|     ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Sessions, 1)); | ||||
| 
 | ||||
|     auto client_port = it->second; | ||||
| 
 | ||||
|     std::shared_ptr<ClientSession> client_session; | ||||
| @ -538,7 +462,7 @@ static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_hand | ||||
|               "requesting_current_thread_handle=0x{:08X}", | ||||
|               holding_thread_handle, mutex_addr, requesting_thread_handle); | ||||
| 
 | ||||
|     if (Memory::IsKernelVirtualAddress(mutex_addr)) { | ||||
|     if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", | ||||
|                   mutex_addr); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
| @ -558,7 +482,7 @@ static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_hand | ||||
| static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { | ||||
|     LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); | ||||
| 
 | ||||
|     if (Memory::IsKernelVirtualAddress(mutex_addr)) { | ||||
|     if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", | ||||
|                   mutex_addr); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
| @ -683,7 +607,6 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | ||||
|         auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); | ||||
|         const auto thread_processor_id = current_thread->GetProcessorID(); | ||||
|         system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); | ||||
|         ASSERT(false); | ||||
| 
 | ||||
|         system.Kernel().CurrentProcess()->PrepareForTermination(); | ||||
| 
 | ||||
| @ -785,35 +708,35 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha | ||||
|             return RESULT_SUCCESS; | ||||
| 
 | ||||
|         case GetInfoType::MapRegionBaseAddr: | ||||
|             *result = process->VMManager().GetMapRegionBaseAddress(); | ||||
|             *result = process->PageTable().GetAliasRegionStart(); | ||||
|             return RESULT_SUCCESS; | ||||
| 
 | ||||
|         case GetInfoType::MapRegionSize: | ||||
|             *result = process->VMManager().GetMapRegionSize(); | ||||
|             *result = process->PageTable().GetAliasRegionSize(); | ||||
|             return RESULT_SUCCESS; | ||||
| 
 | ||||
|         case GetInfoType::HeapRegionBaseAddr: | ||||
|             *result = process->VMManager().GetHeapRegionBaseAddress(); | ||||
|             *result = process->PageTable().GetHeapRegionStart(); | ||||
|             return RESULT_SUCCESS; | ||||
| 
 | ||||
|         case GetInfoType::HeapRegionSize: | ||||
|             *result = process->VMManager().GetHeapRegionSize(); | ||||
|             *result = process->PageTable().GetHeapRegionSize(); | ||||
|             return RESULT_SUCCESS; | ||||
| 
 | ||||
|         case GetInfoType::ASLRRegionBaseAddr: | ||||
|             *result = process->VMManager().GetASLRRegionBaseAddress(); | ||||
|             *result = process->PageTable().GetAliasCodeRegionStart(); | ||||
|             return RESULT_SUCCESS; | ||||
| 
 | ||||
|         case GetInfoType::ASLRRegionSize: | ||||
|             *result = process->VMManager().GetASLRRegionSize(); | ||||
|             *result = process->PageTable().GetAliasCodeRegionSize(); | ||||
|             return RESULT_SUCCESS; | ||||
| 
 | ||||
|         case GetInfoType::StackRegionBaseAddr: | ||||
|             *result = process->VMManager().GetStackRegionBaseAddress(); | ||||
|             *result = process->PageTable().GetStackRegionStart(); | ||||
|             return RESULT_SUCCESS; | ||||
| 
 | ||||
|         case GetInfoType::StackRegionSize: | ||||
|             *result = process->VMManager().GetStackRegionSize(); | ||||
|             *result = process->PageTable().GetStackRegionSize(); | ||||
|             return RESULT_SUCCESS; | ||||
| 
 | ||||
|         case GetInfoType::TotalPhysicalMemoryAvailable: | ||||
| @ -987,20 +910,29 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     Process* const current_process = system.Kernel().CurrentProcess(); | ||||
|     auto& vm_manager = current_process->VMManager(); | ||||
|     Process* const current_process{system.Kernel().CurrentProcess()}; | ||||
|     auto& page_table{current_process->PageTable()}; | ||||
| 
 | ||||
|     if (current_process->GetSystemResourceSize() == 0) { | ||||
|         LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); | ||||
|         return ERR_INVALID_STATE; | ||||
|     } | ||||
| 
 | ||||
|     if (!vm_manager.IsWithinMapRegion(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Range not within map region"); | ||||
|     if (!page_table.IsInsideAddressSpace(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, | ||||
|                   size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     return vm_manager.MapPhysicalMemory(addr, size); | ||||
|     if (page_table.IsOutsideAliasRegion(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, | ||||
|                   size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     return page_table.MapPhysicalMemory(addr, size); | ||||
| } | ||||
| 
 | ||||
| /// Unmaps memory previously mapped via MapPhysicalMemory
 | ||||
| @ -1027,20 +959,29 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     Process* const current_process = system.Kernel().CurrentProcess(); | ||||
|     auto& vm_manager = current_process->VMManager(); | ||||
|     Process* const current_process{system.Kernel().CurrentProcess()}; | ||||
|     auto& page_table{current_process->PageTable()}; | ||||
| 
 | ||||
|     if (current_process->GetSystemResourceSize() == 0) { | ||||
|         LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); | ||||
|         return ERR_INVALID_STATE; | ||||
|     } | ||||
| 
 | ||||
|     if (!vm_manager.IsWithinMapRegion(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Range not within map region"); | ||||
|     if (!page_table.IsInsideAddressSpace(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, | ||||
|                   size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     return vm_manager.UnmapPhysicalMemory(addr, size); | ||||
|     if (page_table.IsOutsideAliasRegion(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, | ||||
|                   size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     return page_table.UnmapPhysicalMemory(addr, size); | ||||
| } | ||||
| 
 | ||||
| /// Sets the thread activity
 | ||||
| @ -1197,74 +1138,49 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     const auto permissions_type = static_cast<MemoryPermission>(permissions); | ||||
|     if (permissions_type != MemoryPermission::Read && | ||||
|         permissions_type != MemoryPermission::ReadWrite) { | ||||
|     const auto permission_type = static_cast<Memory::MemoryPermission>(permissions); | ||||
|     if ((permission_type | Memory::MemoryPermission::Write) != | ||||
|         Memory::MemoryPermission::ReadAndWrite) { | ||||
|         LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}", | ||||
|                   permissions); | ||||
|         return ERR_INVALID_MEMORY_PERMISSIONS; | ||||
|     } | ||||
| 
 | ||||
|     auto* const current_process = system.Kernel().CurrentProcess(); | ||||
|     auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); | ||||
|     auto* const current_process{system.Kernel().CurrentProcess()}; | ||||
|     auto& page_table{current_process->PageTable()}; | ||||
| 
 | ||||
|     if (page_table.IsInvalidRegion(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Addr does not fit within the valid region, addr=0x{:016X}, " | ||||
|                   "size=0x{:016X}", | ||||
|                   addr, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     if (page_table.IsInsideHeapRegion(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Addr does not fit within the heap region, addr=0x{:016X}, " | ||||
|                   "size=0x{:016X}", | ||||
|                   addr, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     if (page_table.IsInsideAliasRegion(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Address does not fit within the map region, addr=0x{:016X}, " | ||||
|                   "size=0x{:016X}", | ||||
|                   addr, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     auto shared_memory{current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle)}; | ||||
|     if (!shared_memory) { | ||||
|         LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", | ||||
|                   shared_memory_handle); | ||||
|         return ERR_INVALID_HANDLE; | ||||
|     } | ||||
| 
 | ||||
|     const auto& vm_manager = current_process->VMManager(); | ||||
|     if (!vm_manager.IsWithinASLRRegion(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Region is not within the ASLR region. addr=0x{:016X}, size={:016X}", | ||||
|                   addr, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare); | ||||
| } | ||||
| 
 | ||||
| static ResultCode UnmapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, | ||||
|                                     u64 size) { | ||||
|     LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", | ||||
|                 shared_memory_handle, addr, size); | ||||
| 
 | ||||
|     if (!Common::Is4KBAligned(addr)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
| 
 | ||||
|     if (size == 0) { | ||||
|         LOG_ERROR(Kernel_SVC, "Size is 0"); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     if (!Common::Is4KBAligned(size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     if (!IsValidAddressRange(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}", | ||||
|                   addr, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     auto* const current_process = system.Kernel().CurrentProcess(); | ||||
|     auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); | ||||
|     if (!shared_memory) { | ||||
|         LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", | ||||
|                   shared_memory_handle); | ||||
|         return ERR_INVALID_HANDLE; | ||||
|     } | ||||
| 
 | ||||
|     const auto& vm_manager = current_process->VMManager(); | ||||
|     if (!vm_manager.IsWithinASLRRegion(addr, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Region is not within the ASLR region. addr=0x{:016X}, size={:016X}", | ||||
|                   addr, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     return shared_memory->Unmap(*current_process, addr, size); | ||||
|     return shared_memory->Map(*current_process, addr, size, permission_type); | ||||
| } | ||||
| 
 | ||||
| static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, | ||||
| @ -1279,18 +1195,17 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add | ||||
|         return ERR_INVALID_HANDLE; | ||||
|     } | ||||
| 
 | ||||
|     auto& memory = system.Memory(); | ||||
|     const auto& vm_manager = process->VMManager(); | ||||
|     const MemoryInfo memory_info = vm_manager.QueryMemory(address); | ||||
|     auto& memory{system.Memory()}; | ||||
|     const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; | ||||
| 
 | ||||
|     memory.Write64(memory_info_address, memory_info.base_address); | ||||
|     memory.Write64(memory_info_address + 8, memory_info.size); | ||||
|     memory.Write32(memory_info_address + 16, memory_info.state); | ||||
|     memory.Write32(memory_info_address + 20, memory_info.attributes); | ||||
|     memory.Write32(memory_info_address + 24, memory_info.permission); | ||||
|     memory.Write32(memory_info_address + 32, memory_info.ipc_ref_count); | ||||
|     memory.Write32(memory_info_address + 28, memory_info.device_ref_count); | ||||
|     memory.Write32(memory_info_address + 36, 0); | ||||
|     memory.Write64(memory_info_address + 0x00, memory_info.addr); | ||||
|     memory.Write64(memory_info_address + 0x08, memory_info.size); | ||||
|     memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); | ||||
|     memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr)); | ||||
|     memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm)); | ||||
|     memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount); | ||||
|     memory.Write32(memory_info_address + 0x20, memory_info.device_refcount); | ||||
|     memory.Write32(memory_info_address + 0x24, 0); | ||||
| 
 | ||||
|     // Page info appears to be currently unused by the kernel and is always set to zero.
 | ||||
|     memory.Write32(page_info_address, 0); | ||||
| @ -1314,142 +1229,6 @@ static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address, | ||||
|     return QueryMemory(system, memory_info_address, page_info_address, query_address); | ||||
| } | ||||
| 
 | ||||
| static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | ||||
|                                        u64 src_address, u64 size) { | ||||
|     LOG_DEBUG(Kernel_SVC, | ||||
|               "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " | ||||
|               "src_address=0x{:016X}, size=0x{:016X}", | ||||
|               process_handle, dst_address, src_address, size); | ||||
| 
 | ||||
|     if (!Common::Is4KBAligned(src_address)) { | ||||
|         LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", | ||||
|                   src_address); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
| 
 | ||||
|     if (!Common::Is4KBAligned(dst_address)) { | ||||
|         LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", | ||||
|                   dst_address); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
| 
 | ||||
|     if (size == 0 || !Common::Is4KBAligned(size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     if (!IsValidAddressRange(dst_address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Destination address range overflows the address space (dst_address=0x{:016X}, " | ||||
|                   "size=0x{:016X}).", | ||||
|                   dst_address, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     if (!IsValidAddressRange(src_address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Source address range overflows the address space (src_address=0x{:016X}, " | ||||
|                   "size=0x{:016X}).", | ||||
|                   src_address, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||||
|     auto process = handle_table.Get<Process>(process_handle); | ||||
|     if (!process) { | ||||
|         LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", | ||||
|                   process_handle); | ||||
|         return ERR_INVALID_HANDLE; | ||||
|     } | ||||
| 
 | ||||
|     auto& vm_manager = process->VMManager(); | ||||
|     if (!vm_manager.IsWithinAddressSpace(src_address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Source address range is not within the address space (src_address=0x{:016X}, " | ||||
|                   "size=0x{:016X}).", | ||||
|                   src_address, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " | ||||
|                   "size=0x{:016X}).", | ||||
|                   dst_address, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     return vm_manager.MapCodeMemory(dst_address, src_address, size); | ||||
| } | ||||
| 
 | ||||
| static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, | ||||
|                                          u64 dst_address, u64 src_address, u64 size) { | ||||
|     LOG_DEBUG(Kernel_SVC, | ||||
|               "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " | ||||
|               "size=0x{:016X}", | ||||
|               process_handle, dst_address, src_address, size); | ||||
| 
 | ||||
|     if (!Common::Is4KBAligned(dst_address)) { | ||||
|         LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", | ||||
|                   dst_address); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
| 
 | ||||
|     if (!Common::Is4KBAligned(src_address)) { | ||||
|         LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", | ||||
|                   src_address); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
| 
 | ||||
|     if (size == 0 || Common::Is4KBAligned(size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     if (!IsValidAddressRange(dst_address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Destination address range overflows the address space (dst_address=0x{:016X}, " | ||||
|                   "size=0x{:016X}).", | ||||
|                   dst_address, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     if (!IsValidAddressRange(src_address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Source address range overflows the address space (src_address=0x{:016X}, " | ||||
|                   "size=0x{:016X}).", | ||||
|                   src_address, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | ||||
|     auto process = handle_table.Get<Process>(process_handle); | ||||
|     if (!process) { | ||||
|         LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", | ||||
|                   process_handle); | ||||
|         return ERR_INVALID_HANDLE; | ||||
|     } | ||||
| 
 | ||||
|     auto& vm_manager = process->VMManager(); | ||||
|     if (!vm_manager.IsWithinAddressSpace(src_address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Source address range is not within the address space (src_address=0x{:016X}, " | ||||
|                   "size=0x{:016X}).", | ||||
|                   src_address, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " | ||||
|                   "size=0x{:016X}).", | ||||
|                   dst_address, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     return vm_manager.UnmapCodeMemory(dst_address, src_address, size); | ||||
| } | ||||
| 
 | ||||
| /// Exits the current process
 | ||||
| static void ExitProcess(Core::System& system) { | ||||
|     auto* current_process = system.Kernel().CurrentProcess(); | ||||
| @ -1506,6 +1285,9 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e | ||||
|     } | ||||
| 
 | ||||
|     auto& kernel = system.Kernel(); | ||||
| 
 | ||||
|     ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); | ||||
| 
 | ||||
|     CASCADE_RESULT(std::shared_ptr<Thread> thread, | ||||
|                    Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top, | ||||
|                                   *current_process)); | ||||
| @ -1610,7 +1392,7 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add | ||||
|         "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", | ||||
|         mutex_addr, condition_variable_addr, thread_handle, nano_seconds); | ||||
| 
 | ||||
|     if (Memory::IsKernelVirtualAddress(mutex_addr)) { | ||||
|     if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { | ||||
|         LOG_ERROR( | ||||
|             Kernel_SVC, | ||||
|             "Given mutex address must not be within the kernel address space. address=0x{:016X}", | ||||
| @ -1741,7 +1523,7 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, | ||||
|               type, value, timeout); | ||||
| 
 | ||||
|     // If the passed address is a kernel virtual address, return invalid memory state.
 | ||||
|     if (Memory::IsKernelVirtualAddress(address)) { | ||||
|     if (Core::Memory::IsKernelVirtualAddress(address)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| @ -1769,7 +1551,7 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, | ||||
|               address, type, value, num_to_wake); | ||||
| 
 | ||||
|     // If the passed address is a kernel virtual address, return invalid memory state.
 | ||||
|     if (Memory::IsKernelVirtualAddress(address)) { | ||||
|     if (Core::Memory::IsKernelVirtualAddress(address)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| @ -1865,9 +1647,9 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     const auto perms = static_cast<MemoryPermission>(permissions); | ||||
|     if (perms != MemoryPermission::None && perms != MemoryPermission::Read && | ||||
|         perms != MemoryPermission::ReadWrite) { | ||||
|     const auto perms{static_cast<Memory::MemoryPermission>(permissions)}; | ||||
|     if (perms > Memory::MemoryPermission::ReadAndWrite || | ||||
|         perms == Memory::MemoryPermission::Write) { | ||||
|         LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})", | ||||
|                   permissions); | ||||
|         return ERR_INVALID_MEMORY_PERMISSIONS; | ||||
| @ -1890,111 +1672,6 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static ResultCode MapTransferMemory(Core::System& system, Handle handle, VAddr address, u64 size, | ||||
|                                     u32 permission_raw) { | ||||
|     LOG_DEBUG(Kernel_SVC, | ||||
|               "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}", | ||||
|               handle, address, size, permission_raw); | ||||
| 
 | ||||
|     if (!Common::Is4KBAligned(address)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).", | ||||
|                   address); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
| 
 | ||||
|     if (size == 0 || !Common::Is4KBAligned(size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).", | ||||
|                   size); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     if (!IsValidAddressRange(address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Given address and size overflows the 64-bit range (address=0x{:016X}, " | ||||
|                   "size=0x{:016X}).", | ||||
|                   address, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     const auto permissions = static_cast<MemoryPermission>(permission_raw); | ||||
|     if (permissions != MemoryPermission::None && permissions != MemoryPermission::Read && | ||||
|         permissions != MemoryPermission::ReadWrite) { | ||||
|         LOG_ERROR(Kernel_SVC, "Invalid transfer memory permissions given (permissions=0x{:08X}).", | ||||
|                   permission_raw); | ||||
|         return ERR_INVALID_STATE; | ||||
|     } | ||||
| 
 | ||||
|     const auto& kernel = system.Kernel(); | ||||
|     const auto* const current_process = kernel.CurrentProcess(); | ||||
|     const auto& handle_table = current_process->GetHandleTable(); | ||||
| 
 | ||||
|     auto transfer_memory = handle_table.Get<TransferMemory>(handle); | ||||
|     if (!transfer_memory) { | ||||
|         LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).", | ||||
|                   handle); | ||||
|         return ERR_INVALID_HANDLE; | ||||
|     } | ||||
| 
 | ||||
|     if (!current_process->VMManager().IsWithinASLRRegion(address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Given address and size don't fully fit within the ASLR region " | ||||
|                   "(address=0x{:016X}, size=0x{:016X}).", | ||||
|                   address, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     return transfer_memory->MapMemory(address, size, permissions); | ||||
| } | ||||
| 
 | ||||
| static ResultCode UnmapTransferMemory(Core::System& system, Handle handle, VAddr address, | ||||
|                                       u64 size) { | ||||
|     LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle, | ||||
|               address, size); | ||||
| 
 | ||||
|     if (!Common::Is4KBAligned(address)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).", | ||||
|                   address); | ||||
|         return ERR_INVALID_ADDRESS; | ||||
|     } | ||||
| 
 | ||||
|     if (size == 0 || !Common::Is4KBAligned(size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).", | ||||
|                   size); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     if (!IsValidAddressRange(address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Given address and size overflows the 64-bit range (address=0x{:016X}, " | ||||
|                   "size=0x{:016X}).", | ||||
|                   address, size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
| 
 | ||||
|     const auto& kernel = system.Kernel(); | ||||
|     const auto* const current_process = kernel.CurrentProcess(); | ||||
|     const auto& handle_table = current_process->GetHandleTable(); | ||||
| 
 | ||||
|     auto transfer_memory = handle_table.Get<TransferMemory>(handle); | ||||
|     if (!transfer_memory) { | ||||
|         LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).", | ||||
|                   handle); | ||||
|         return ERR_INVALID_HANDLE; | ||||
|     } | ||||
| 
 | ||||
|     if (!current_process->VMManager().IsWithinASLRRegion(address, size)) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Given address and size don't fully fit within the ASLR region " | ||||
|                   "(address=0x{:016X}, size=0x{:016X}).", | ||||
|                   address, size); | ||||
|         return ERR_INVALID_MEMORY_RANGE; | ||||
|     } | ||||
| 
 | ||||
|     return transfer_memory->UnmapMemory(address, size); | ||||
| } | ||||
| 
 | ||||
| static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, | ||||
|                                     u64* mask) { | ||||
|     LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); | ||||
| @ -2073,52 +1750,6 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static ResultCode CreateSharedMemory(Core::System& system, Handle* handle, u64 size, | ||||
|                                      u32 local_permissions, u32 remote_permissions) { | ||||
|     LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, | ||||
|               local_permissions, remote_permissions); | ||||
|     if (size == 0) { | ||||
|         LOG_ERROR(Kernel_SVC, "Size is 0"); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
|     if (!Common::Is4KBAligned(size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     if (size >= MAIN_MEMORY_SIZE) { | ||||
|         LOG_ERROR(Kernel_SVC, "Size is not less than 8GB, 0x{:016X}", size); | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     const auto local_perms = static_cast<MemoryPermission>(local_permissions); | ||||
|     if (local_perms != MemoryPermission::Read && local_perms != MemoryPermission::ReadWrite) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Invalid local memory permissions, expected Read or ReadWrite but got " | ||||
|                   "local_permissions={}", | ||||
|                   static_cast<u32>(local_permissions)); | ||||
|         return ERR_INVALID_MEMORY_PERMISSIONS; | ||||
|     } | ||||
| 
 | ||||
|     const auto remote_perms = static_cast<MemoryPermission>(remote_permissions); | ||||
|     if (remote_perms != MemoryPermission::Read && remote_perms != MemoryPermission::ReadWrite && | ||||
|         remote_perms != MemoryPermission::DontCare) { | ||||
|         LOG_ERROR(Kernel_SVC, | ||||
|                   "Invalid remote memory permissions, expected Read, ReadWrite or DontCare but got " | ||||
|                   "remote_permissions={}", | ||||
|                   static_cast<u32>(remote_permissions)); | ||||
|         return ERR_INVALID_MEMORY_PERMISSIONS; | ||||
|     } | ||||
| 
 | ||||
|     auto& kernel = system.Kernel(); | ||||
|     auto process = kernel.CurrentProcess(); | ||||
|     auto& handle_table = process->GetHandleTable(); | ||||
|     auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms); | ||||
| 
 | ||||
|     CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { | ||||
|     LOG_DEBUG(Kernel_SVC, "called"); | ||||
| 
 | ||||
| @ -2305,11 +1936,10 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, | ||||
|     } | ||||
| 
 | ||||
|     const auto& kernel = system.Kernel(); | ||||
|     const auto& vm_manager = kernel.CurrentProcess()->VMManager(); | ||||
|     const auto total_copy_size = out_process_ids_size * sizeof(u64); | ||||
| 
 | ||||
|     if (out_process_ids_size > 0 && | ||||
|         !vm_manager.IsWithinAddressSpace(out_process_ids, total_copy_size)) { | ||||
|     if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace( | ||||
|                                         out_process_ids, total_copy_size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", | ||||
|                   out_process_ids, out_process_ids + total_copy_size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
| @ -2345,11 +1975,10 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd | ||||
|     } | ||||
| 
 | ||||
|     const auto* const current_process = system.Kernel().CurrentProcess(); | ||||
|     const auto& vm_manager = current_process->VMManager(); | ||||
|     const auto total_copy_size = out_thread_ids_size * sizeof(u64); | ||||
| 
 | ||||
|     if (out_thread_ids_size > 0 && | ||||
|         !vm_manager.IsWithinAddressSpace(out_thread_ids, total_copy_size)) { | ||||
|         !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", | ||||
|                   out_thread_ids, out_thread_ids + total_copy_size); | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
| @ -2510,7 +2139,7 @@ static const FunctionDef SVC_Table_32[] = { | ||||
| static const FunctionDef SVC_Table_64[] = { | ||||
|     {0x00, nullptr, "Unknown"}, | ||||
|     {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"}, | ||||
|     {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"}, | ||||
|     {0x02, nullptr, "SetMemoryPermission"}, | ||||
|     {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"}, | ||||
|     {0x04, SvcWrap64<MapMemory>, "MapMemory"}, | ||||
|     {0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"}, | ||||
| @ -2528,7 +2157,7 @@ static const FunctionDef SVC_Table_64[] = { | ||||
|     {0x11, SvcWrap64<SignalEvent>, "SignalEvent"}, | ||||
|     {0x12, SvcWrap64<ClearEvent>, "ClearEvent"}, | ||||
|     {0x13, SvcWrap64<MapSharedMemory>, "MapSharedMemory"}, | ||||
|     {0x14, SvcWrap64<UnmapSharedMemory>, "UnmapSharedMemory"}, | ||||
|     {0x14, nullptr, "UnmapSharedMemory"}, | ||||
|     {0x15, SvcWrap64<CreateTransferMemory>, "CreateTransferMemory"}, | ||||
|     {0x16, SvcWrap64<CloseHandle>, "CloseHandle"}, | ||||
|     {0x17, SvcWrap64<ResetSignal>, "ResetSignal"}, | ||||
| @ -2588,9 +2217,9 @@ static const FunctionDef SVC_Table_64[] = { | ||||
|     {0x4D, nullptr, "SleepSystem"}, | ||||
|     {0x4E, nullptr, "ReadWriteRegister"}, | ||||
|     {0x4F, nullptr, "SetProcessActivity"}, | ||||
|     {0x50, SvcWrap64<CreateSharedMemory>, "CreateSharedMemory"}, | ||||
|     {0x51, SvcWrap64<MapTransferMemory>, "MapTransferMemory"}, | ||||
|     {0x52, SvcWrap64<UnmapTransferMemory>, "UnmapTransferMemory"}, | ||||
|     {0x50, nullptr, "CreateSharedMemory"}, | ||||
|     {0x51, nullptr, "MapTransferMemory"}, | ||||
|     {0x52, nullptr, "UnmapTransferMemory"}, | ||||
|     {0x53, nullptr, "CreateInterruptEvent"}, | ||||
|     {0x54, nullptr, "QueryPhysicalAddress"}, | ||||
|     {0x55, nullptr, "QueryIoMapping"}, | ||||
| @ -2627,8 +2256,8 @@ static const FunctionDef SVC_Table_64[] = { | ||||
|     {0x74, nullptr, "MapProcessMemory"}, | ||||
|     {0x75, nullptr, "UnmapProcessMemory"}, | ||||
|     {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, | ||||
|     {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, | ||||
|     {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, | ||||
|     {0x77, nullptr, "MapProcessCodeMemory"}, | ||||
|     {0x78, nullptr, "UnmapProcessCodeMemory"}, | ||||
|     {0x79, nullptr, "CreateProcess"}, | ||||
|     {0x7A, nullptr, "StartProcess"}, | ||||
|     {0x7B, nullptr, "TerminateProcess"}, | ||||
| @ -2656,7 +2285,7 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) { | ||||
| 
 | ||||
| MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); | ||||
| 
 | ||||
| void CallSVC(Core::System& system, u32 immediate) { | ||||
| void Call(Core::System& system, u32 immediate) { | ||||
|     MICROPROFILE_SCOPE(Kernel_SVC); | ||||
| 
 | ||||
|     // Lock the global kernel mutex when we enter the kernel HLE.
 | ||||
| @ -2675,4 +2304,4 @@ void CallSVC(Core::System& system, u32 immediate) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
| } // namespace Kernel::Svc
 | ||||
|  | ||||
| @ -10,8 +10,8 @@ namespace Core { | ||||
| class System; | ||||
| } | ||||
| 
 | ||||
| namespace Kernel { | ||||
| namespace Kernel::Svc { | ||||
| 
 | ||||
| void CallSVC(Core::System& system, u32 immediate); | ||||
| void Call(Core::System& system, u32 immediate); | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
| } // namespace Kernel::Svc
 | ||||
|  | ||||
							
								
								
									
										68
									
								
								src/core/hle/kernel/svc_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/core/hle/kernel/svc_types.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| // Copyright 2020 yuzu emulator team
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Kernel::Svc { | ||||
| 
 | ||||
| enum class MemoryState : u32 { | ||||
|     Free = 0x00, | ||||
|     Io = 0x01, | ||||
|     Static = 0x02, | ||||
|     Code = 0x03, | ||||
|     CodeData = 0x04, | ||||
|     Normal = 0x05, | ||||
|     Shared = 0x06, | ||||
|     Alias = 0x07, | ||||
|     AliasCode = 0x08, | ||||
|     AliasCodeData = 0x09, | ||||
|     Ipc = 0x0A, | ||||
|     Stack = 0x0B, | ||||
|     ThreadLocal = 0x0C, | ||||
|     Transfered = 0x0D, | ||||
|     SharedTransfered = 0x0E, | ||||
|     SharedCode = 0x0F, | ||||
|     Inaccessible = 0x10, | ||||
|     NonSecureIpc = 0x11, | ||||
|     NonDeviceIpc = 0x12, | ||||
|     Kernel = 0x13, | ||||
|     GeneratedCode = 0x14, | ||||
|     CodeOut = 0x15, | ||||
| }; | ||||
| DECLARE_ENUM_FLAG_OPERATORS(MemoryState); | ||||
| 
 | ||||
| enum class MemoryAttribute : u32 { | ||||
|     Locked = (1 << 0), | ||||
|     IpcLocked = (1 << 1), | ||||
|     DeviceShared = (1 << 2), | ||||
|     Uncached = (1 << 3), | ||||
| }; | ||||
| DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute); | ||||
| 
 | ||||
| enum class MemoryPermission : u32 { | ||||
|     None = (0 << 0), | ||||
|     Read = (1 << 0), | ||||
|     Write = (1 << 1), | ||||
|     Execute = (1 << 2), | ||||
|     ReadWrite = Read | Write, | ||||
|     ReadExecute = Read | Execute, | ||||
|     DontCare = (1 << 28), | ||||
| }; | ||||
| DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); | ||||
| 
 | ||||
| struct MemoryInfo { | ||||
|     u64 addr{}; | ||||
|     u64 size{}; | ||||
|     MemoryState state{}; | ||||
|     MemoryAttribute attr{}; | ||||
|     MemoryPermission perm{}; | ||||
|     u32 ipc_refcount{}; | ||||
|     u32 device_refcount{}; | ||||
|     u32 padding{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel::Svc
 | ||||
| @ -2,17 +2,16 @@ | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/kernel/transfer_memory.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| TransferMemory::TransferMemory(KernelCore& kernel, Memory::Memory& memory) | ||||
| TransferMemory::TransferMemory(KernelCore& kernel, Core::Memory::Memory& memory) | ||||
|     : Object{kernel}, memory{memory} {} | ||||
| 
 | ||||
| TransferMemory::~TransferMemory() { | ||||
| @ -20,14 +19,15 @@ TransferMemory::~TransferMemory() { | ||||
|     Reset(); | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, Memory::Memory& memory, | ||||
|                                                        VAddr base_address, u64 size, | ||||
|                                                        MemoryPermission permissions) { | ||||
| std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, | ||||
|                                                        Core::Memory::Memory& memory, | ||||
|                                                        VAddr base_address, std::size_t size, | ||||
|                                                        Memory::MemoryPermission permissions) { | ||||
|     std::shared_ptr<TransferMemory> transfer_memory{ | ||||
|         std::make_shared<TransferMemory>(kernel, memory)}; | ||||
| 
 | ||||
|     transfer_memory->base_address = base_address; | ||||
|     transfer_memory->memory_size = size; | ||||
|     transfer_memory->size = size; | ||||
|     transfer_memory->owner_permissions = permissions; | ||||
|     transfer_memory->owner_process = kernel.CurrentProcess(); | ||||
| 
 | ||||
| @ -38,98 +38,12 @@ const u8* TransferMemory::GetPointer() const { | ||||
|     return memory.GetPointer(base_address); | ||||
| } | ||||
| 
 | ||||
| u64 TransferMemory::GetSize() const { | ||||
|     return memory_size; | ||||
| } | ||||
| 
 | ||||
| ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission permissions) { | ||||
|     if (memory_size != size) { | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     if (owner_permissions != permissions) { | ||||
|         return ERR_INVALID_STATE; | ||||
|     } | ||||
| 
 | ||||
|     if (is_mapped) { | ||||
|         return ERR_INVALID_STATE; | ||||
|     } | ||||
| 
 | ||||
|     backing_block = std::make_shared<PhysicalMemory>(size); | ||||
| 
 | ||||
|     const auto map_state = owner_permissions == MemoryPermission::None | ||||
|                                ? MemoryState::TransferMemoryIsolated | ||||
|                                : MemoryState::TransferMemory; | ||||
|     auto& vm_manager = owner_process->VMManager(); | ||||
|     const auto map_result = vm_manager.MapMemoryBlock(address, backing_block, 0, size, map_state); | ||||
|     if (map_result.Failed()) { | ||||
|         return map_result.Code(); | ||||
|     } | ||||
| 
 | ||||
|     is_mapped = true; | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode TransferMemory::Reserve() { | ||||
|     auto& vm_manager{owner_process->VMManager()}; | ||||
|     const auto check_range_result{vm_manager.CheckRangeState( | ||||
|         base_address, memory_size, MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, | ||||
|         MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::All, | ||||
|         VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, | ||||
|         MemoryAttribute::IpcAndDeviceMapped)}; | ||||
| 
 | ||||
|     if (check_range_result.Failed()) { | ||||
|         return check_range_result.Code(); | ||||
|     } | ||||
| 
 | ||||
|     auto [state_, permissions_, attribute] = *check_range_result; | ||||
| 
 | ||||
|     if (const auto result{vm_manager.ReprotectRange( | ||||
|             base_address, memory_size, SharedMemory::ConvertPermissions(owner_permissions))}; | ||||
|         result.IsError()) { | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask, | ||||
|                                          attribute | MemoryAttribute::Locked); | ||||
|     return owner_process->PageTable().ReserveTransferMemory(base_address, size, owner_permissions); | ||||
| } | ||||
| 
 | ||||
| ResultCode TransferMemory::Reset() { | ||||
|     auto& vm_manager{owner_process->VMManager()}; | ||||
|     if (const auto result{vm_manager.CheckRangeState( | ||||
|             base_address, memory_size, | ||||
|             MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, | ||||
|             MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::None, | ||||
|             VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, | ||||
|             MemoryAttribute::IpcAndDeviceMapped)}; | ||||
|         result.Failed()) { | ||||
|         return result.Code(); | ||||
|     } | ||||
| 
 | ||||
|     if (const auto result{ | ||||
|             vm_manager.ReprotectRange(base_address, memory_size, VMAPermission::ReadWrite)}; | ||||
|         result.IsError()) { | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask, | ||||
|                                          MemoryAttribute::None); | ||||
| } | ||||
| 
 | ||||
| ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) { | ||||
|     if (memory_size != size) { | ||||
|         return ERR_INVALID_SIZE; | ||||
|     } | ||||
| 
 | ||||
|     auto& vm_manager = owner_process->VMManager(); | ||||
|     const auto result = vm_manager.UnmapRange(address, size); | ||||
| 
 | ||||
|     if (result.IsError()) { | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     is_mapped = false; | ||||
|     return RESULT_SUCCESS; | ||||
|     return owner_process->PageTable().ResetTransferMemory(base_address, size); | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -6,12 +6,13 @@ | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "core/hle/kernel/memory/memory_block.h" | ||||
| #include "core/hle/kernel/object.h" | ||||
| #include "core/hle/kernel/physical_memory.h" | ||||
| 
 | ||||
| union ResultCode; | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
| @ -20,8 +21,6 @@ namespace Kernel { | ||||
| class KernelCore; | ||||
| class Process; | ||||
| 
 | ||||
| enum class MemoryPermission : u32; | ||||
| 
 | ||||
| /// Defines the interface for transfer memory objects.
 | ||||
| ///
 | ||||
| /// Transfer memory is typically used for the purpose of
 | ||||
| @ -30,14 +29,14 @@ enum class MemoryPermission : u32; | ||||
| ///
 | ||||
| class TransferMemory final : public Object { | ||||
| public: | ||||
|     explicit TransferMemory(KernelCore& kernel, Memory::Memory& memory); | ||||
|     explicit TransferMemory(KernelCore& kernel, Core::Memory::Memory& memory); | ||||
|     ~TransferMemory() override; | ||||
| 
 | ||||
|     static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; | ||||
| 
 | ||||
|     static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Memory::Memory& memory, | ||||
|                                                   VAddr base_address, u64 size, | ||||
|                                                   MemoryPermission permissions); | ||||
|     static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Core::Memory::Memory& memory, | ||||
|                                                   VAddr base_address, std::size_t size, | ||||
|                                                   Memory::MemoryPermission permissions); | ||||
| 
 | ||||
|     TransferMemory(const TransferMemory&) = delete; | ||||
|     TransferMemory& operator=(const TransferMemory&) = delete; | ||||
| @ -61,29 +60,9 @@ public: | ||||
|     const u8* GetPointer() const; | ||||
| 
 | ||||
|     /// Gets the size of the memory backing this instance in bytes.
 | ||||
|     u64 GetSize() const; | ||||
| 
 | ||||
|     /// Attempts to map transfer memory with the given range and memory permissions.
 | ||||
|     ///
 | ||||
|     /// @param address     The base address to being mapping memory at.
 | ||||
|     /// @param size        The size of the memory to map, in bytes.
 | ||||
|     /// @param permissions The memory permissions to check against when mapping memory.
 | ||||
|     ///
 | ||||
|     /// @pre The given address, size, and memory permissions must all match
 | ||||
|     ///      the same values that were given when creating the transfer memory
 | ||||
|     ///      instance.
 | ||||
|     ///
 | ||||
|     ResultCode MapMemory(VAddr address, u64 size, MemoryPermission permissions); | ||||
| 
 | ||||
|     /// Unmaps the transfer memory with the given range
 | ||||
|     ///
 | ||||
|     /// @param address The base address to begin unmapping memory at.
 | ||||
|     /// @param size    The size of the memory to unmap, in bytes.
 | ||||
|     ///
 | ||||
|     /// @pre The given address and size must be the same as the ones used
 | ||||
|     ///      to create the transfer memory instance.
 | ||||
|     ///
 | ||||
|     ResultCode UnmapMemory(VAddr address, u64 size); | ||||
|     constexpr std::size_t GetSize() const { | ||||
|         return size; | ||||
|     } | ||||
| 
 | ||||
|     /// Reserves the region to be used for the transfer memory, called after the transfer memory is
 | ||||
|     /// created.
 | ||||
| @ -94,25 +73,19 @@ public: | ||||
|     ResultCode Reset(); | ||||
| 
 | ||||
| private: | ||||
|     /// Memory block backing this instance.
 | ||||
|     std::shared_ptr<PhysicalMemory> backing_block; | ||||
| 
 | ||||
|     /// The base address for the memory managed by this instance.
 | ||||
|     VAddr base_address = 0; | ||||
|     VAddr base_address{}; | ||||
| 
 | ||||
|     /// Size of the memory, in bytes, that this instance manages.
 | ||||
|     u64 memory_size = 0; | ||||
|     std::size_t size{}; | ||||
| 
 | ||||
|     /// The memory permissions that are applied to this instance.
 | ||||
|     MemoryPermission owner_permissions{}; | ||||
|     Memory::MemoryPermission owner_permissions{}; | ||||
| 
 | ||||
|     /// The process that this transfer memory instance was created under.
 | ||||
|     Process* owner_process = nullptr; | ||||
|     Process* owner_process{}; | ||||
| 
 | ||||
|     /// Whether or not this transfer memory instance has mapped memory.
 | ||||
|     bool is_mapped = false; | ||||
| 
 | ||||
|     Memory::Memory& memory; | ||||
|     Core::Memory::Memory& memory; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,796 +0,0 @@ | ||||
| // Copyright 2015 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <tuple> | ||||
| #include <vector> | ||||
| #include "common/common_types.h" | ||||
| #include "common/memory_hook.h" | ||||
| #include "common/page_table.h" | ||||
| #include "core/hle/kernel/physical_memory.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
| 
 | ||||
| namespace FileSys { | ||||
| enum class ProgramAddressSpaceType : u8; | ||||
| } | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| enum class VMAType : u8 { | ||||
|     /// VMA represents an unmapped region of the address space.
 | ||||
|     Free, | ||||
|     /// VMA is backed by a ref-counted allocate memory block.
 | ||||
|     AllocatedMemoryBlock, | ||||
|     /// VMA is backed by a raw, unmanaged pointer.
 | ||||
|     BackingMemory, | ||||
|     /// VMA is mapped to MMIO registers at a fixed PAddr.
 | ||||
|     MMIO, | ||||
|     // TODO(yuriks): Implement MemoryAlias to support MAP/UNMAP
 | ||||
| }; | ||||
| 
 | ||||
| /// Permissions for mapped memory blocks
 | ||||
| enum class VMAPermission : u8 { | ||||
|     None = 0, | ||||
|     Read = 1, | ||||
|     Write = 2, | ||||
|     Execute = 4, | ||||
| 
 | ||||
|     ReadWrite = Read | Write, | ||||
|     ReadExecute = Read | Execute, | ||||
|     WriteExecute = Write | Execute, | ||||
|     ReadWriteExecute = Read | Write | Execute, | ||||
| 
 | ||||
|     // Used as a wildcard when checking permissions across memory ranges
 | ||||
|     All = 0xFF, | ||||
| }; | ||||
| 
 | ||||
| constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { | ||||
|     return static_cast<VMAPermission>(u32(lhs) | u32(rhs)); | ||||
| } | ||||
| 
 | ||||
| constexpr VMAPermission operator&(VMAPermission lhs, VMAPermission rhs) { | ||||
|     return static_cast<VMAPermission>(u32(lhs) & u32(rhs)); | ||||
| } | ||||
| 
 | ||||
| constexpr VMAPermission operator^(VMAPermission lhs, VMAPermission rhs) { | ||||
|     return static_cast<VMAPermission>(u32(lhs) ^ u32(rhs)); | ||||
| } | ||||
| 
 | ||||
| constexpr VMAPermission operator~(VMAPermission permission) { | ||||
|     return static_cast<VMAPermission>(~u32(permission)); | ||||
| } | ||||
| 
 | ||||
| constexpr VMAPermission& operator|=(VMAPermission& lhs, VMAPermission rhs) { | ||||
|     lhs = lhs | rhs; | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| constexpr VMAPermission& operator&=(VMAPermission& lhs, VMAPermission rhs) { | ||||
|     lhs = lhs & rhs; | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| constexpr VMAPermission& operator^=(VMAPermission& lhs, VMAPermission rhs) { | ||||
|     lhs = lhs ^ rhs; | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| /// Attribute flags that can be applied to a VMA
 | ||||
| enum class MemoryAttribute : u32 { | ||||
|     Mask = 0xFF, | ||||
| 
 | ||||
|     /// No particular qualities
 | ||||
|     None = 0, | ||||
|     /// Memory locked/borrowed for use. e.g. This would be used by transfer memory.
 | ||||
|     Locked = 1, | ||||
|     /// Memory locked for use by IPC-related internals.
 | ||||
|     LockedForIPC = 2, | ||||
|     /// Mapped as part of the device address space.
 | ||||
|     DeviceMapped = 4, | ||||
|     /// Uncached memory
 | ||||
|     Uncached = 8, | ||||
| 
 | ||||
|     IpcAndDeviceMapped = LockedForIPC | DeviceMapped, | ||||
| }; | ||||
| 
 | ||||
| constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) { | ||||
|     return static_cast<MemoryAttribute>(u32(lhs) | u32(rhs)); | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryAttribute operator&(MemoryAttribute lhs, MemoryAttribute rhs) { | ||||
|     return static_cast<MemoryAttribute>(u32(lhs) & u32(rhs)); | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryAttribute operator^(MemoryAttribute lhs, MemoryAttribute rhs) { | ||||
|     return static_cast<MemoryAttribute>(u32(lhs) ^ u32(rhs)); | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryAttribute operator~(MemoryAttribute attribute) { | ||||
|     return static_cast<MemoryAttribute>(~u32(attribute)); | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryAttribute& operator|=(MemoryAttribute& lhs, MemoryAttribute rhs) { | ||||
|     lhs = lhs | rhs; | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryAttribute& operator&=(MemoryAttribute& lhs, MemoryAttribute rhs) { | ||||
|     lhs = lhs & rhs; | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryAttribute& operator^=(MemoryAttribute& lhs, MemoryAttribute rhs) { | ||||
|     lhs = lhs ^ rhs; | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| constexpr u32 ToSvcMemoryAttribute(MemoryAttribute attribute) { | ||||
|     return static_cast<u32>(attribute & MemoryAttribute::Mask); | ||||
| } | ||||
| 
 | ||||
| // clang-format off
 | ||||
| /// Represents memory states and any relevant flags, as used by the kernel.
 | ||||
| /// svcQueryMemory interprets these by masking away all but the first eight
 | ||||
| /// bits when storing memory state into a MemoryInfo instance.
 | ||||
| enum class MemoryState : u32 { | ||||
|     Mask                            = 0xFF, | ||||
|     FlagProtect                     = 1U << 8, | ||||
|     FlagDebug                       = 1U << 9, | ||||
|     FlagIPC0                        = 1U << 10, | ||||
|     FlagIPC3                        = 1U << 11, | ||||
|     FlagIPC1                        = 1U << 12, | ||||
|     FlagMapped                      = 1U << 13, | ||||
|     FlagCode                        = 1U << 14, | ||||
|     FlagAlias                       = 1U << 15, | ||||
|     FlagModule                      = 1U << 16, | ||||
|     FlagTransfer                    = 1U << 17, | ||||
|     FlagQueryPhysicalAddressAllowed = 1U << 18, | ||||
|     FlagSharedDevice                = 1U << 19, | ||||
|     FlagSharedDeviceAligned         = 1U << 20, | ||||
|     FlagIPCBuffer                   = 1U << 21, | ||||
|     FlagMemoryPoolAllocated         = 1U << 22, | ||||
|     FlagMapProcess                  = 1U << 23, | ||||
|     FlagUncached                    = 1U << 24, | ||||
|     FlagCodeMemory                  = 1U << 25, | ||||
| 
 | ||||
|     // Wildcard used in range checking to indicate all states.
 | ||||
|     All                             = 0xFFFFFFFF, | ||||
| 
 | ||||
|     // Convenience flag sets to reduce repetition
 | ||||
|     IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, | ||||
| 
 | ||||
|     CodeFlags = FlagDebug | IPCFlags | FlagMapped | FlagCode | FlagQueryPhysicalAddressAllowed | | ||||
|                 FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||||
| 
 | ||||
|     DataFlags = FlagProtect | IPCFlags | FlagMapped | FlagAlias | FlagTransfer | | ||||
|                 FlagQueryPhysicalAddressAllowed | FlagSharedDevice | FlagSharedDeviceAligned | | ||||
|                 FlagMemoryPoolAllocated | FlagIPCBuffer | FlagUncached, | ||||
| 
 | ||||
|     Unmapped               = 0x00, | ||||
|     Io                     = 0x01 | FlagMapped, | ||||
|     Normal                 = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed, | ||||
|     Code                   = 0x03 | CodeFlags  | FlagMapProcess, | ||||
|     CodeData               = 0x04 | DataFlags  | FlagMapProcess | FlagCodeMemory, | ||||
|     Heap                   = 0x05 | DataFlags  | FlagCodeMemory, | ||||
|     Shared                 = 0x06 | FlagMapped | FlagMemoryPoolAllocated, | ||||
|     ModuleCode             = 0x08 | CodeFlags  | FlagModule | FlagMapProcess, | ||||
|     ModuleCodeData         = 0x09 | DataFlags  | FlagModule | FlagMapProcess | FlagCodeMemory, | ||||
| 
 | ||||
|     IpcBuffer0             = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated | | ||||
|                                     IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned, | ||||
| 
 | ||||
|     Stack                  = 0x0B | FlagMapped | IPCFlags | FlagQueryPhysicalAddressAllowed | | ||||
|                                     FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||||
| 
 | ||||
|     ThreadLocal            = 0x0C | FlagMapped | FlagMemoryPoolAllocated, | ||||
| 
 | ||||
|     TransferMemoryIsolated = 0x0D | IPCFlags | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||||
|                                     FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated | | ||||
|                                     FlagUncached, | ||||
| 
 | ||||
|     TransferMemory         = 0x0E | FlagIPC3   | FlagIPC1   | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||||
|                                     FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||||
| 
 | ||||
|     ProcessMemory          = 0x0F | FlagIPC3   | FlagIPC1   | FlagMapped | FlagMemoryPoolAllocated, | ||||
| 
 | ||||
|     // Used to signify an inaccessible or invalid memory region with memory queries
 | ||||
|     Inaccessible           = 0x10, | ||||
| 
 | ||||
|     IpcBuffer1             = 0x11 | FlagIPC3   | FlagIPC1   | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||||
|                                     FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||||
| 
 | ||||
|     IpcBuffer3             = 0x12 | FlagIPC3   | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||||
|                                     FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||||
| 
 | ||||
|     KernelStack            = 0x13 | FlagMapped, | ||||
| }; | ||||
| // clang-format on
 | ||||
| 
 | ||||
| constexpr MemoryState operator|(MemoryState lhs, MemoryState rhs) { | ||||
|     return static_cast<MemoryState>(u32(lhs) | u32(rhs)); | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryState operator&(MemoryState lhs, MemoryState rhs) { | ||||
|     return static_cast<MemoryState>(u32(lhs) & u32(rhs)); | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryState operator^(MemoryState lhs, MemoryState rhs) { | ||||
|     return static_cast<MemoryState>(u32(lhs) ^ u32(rhs)); | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryState operator~(MemoryState lhs) { | ||||
|     return static_cast<MemoryState>(~u32(lhs)); | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryState& operator|=(MemoryState& lhs, MemoryState rhs) { | ||||
|     lhs = lhs | rhs; | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryState& operator&=(MemoryState& lhs, MemoryState rhs) { | ||||
|     lhs = lhs & rhs; | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| constexpr MemoryState& operator^=(MemoryState& lhs, MemoryState rhs) { | ||||
|     lhs = lhs ^ rhs; | ||||
|     return lhs; | ||||
| } | ||||
| 
 | ||||
| constexpr u32 ToSvcMemoryState(MemoryState state) { | ||||
|     return static_cast<u32>(state & MemoryState::Mask); | ||||
| } | ||||
| 
 | ||||
| struct MemoryInfo { | ||||
|     u64 base_address; | ||||
|     u64 size; | ||||
|     u32 state; | ||||
|     u32 attributes; | ||||
|     u32 permission; | ||||
|     u32 ipc_ref_count; | ||||
|     u32 device_ref_count; | ||||
| }; | ||||
| static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size."); | ||||
| 
 | ||||
| struct PageInfo { | ||||
|     u32 flags; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space | ||||
|  * with homogeneous attributes across its extents. In this particular implementation each VMA is | ||||
|  * also backed by a single host memory allocation. | ||||
|  */ | ||||
| struct VirtualMemoryArea { | ||||
|     /// Gets the starting (base) address of this VMA.
 | ||||
|     VAddr StartAddress() const { | ||||
|         return base; | ||||
|     } | ||||
| 
 | ||||
|     /// Gets the ending address of this VMA.
 | ||||
|     VAddr EndAddress() const { | ||||
|         return base + size - 1; | ||||
|     } | ||||
| 
 | ||||
|     /// Virtual base address of the region.
 | ||||
|     VAddr base = 0; | ||||
|     /// Size of the region.
 | ||||
|     u64 size = 0; | ||||
| 
 | ||||
|     VMAType type = VMAType::Free; | ||||
|     VMAPermission permissions = VMAPermission::None; | ||||
|     MemoryState state = MemoryState::Unmapped; | ||||
|     MemoryAttribute attribute = MemoryAttribute::None; | ||||
| 
 | ||||
|     // Settings for type = AllocatedMemoryBlock
 | ||||
|     /// Memory block backing this VMA.
 | ||||
|     std::shared_ptr<PhysicalMemory> backing_block = nullptr; | ||||
|     /// Offset into the backing_memory the mapping starts from.
 | ||||
|     std::size_t offset = 0; | ||||
| 
 | ||||
|     // Settings for type = BackingMemory
 | ||||
|     /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed.
 | ||||
|     u8* backing_memory = nullptr; | ||||
| 
 | ||||
|     // Settings for type = MMIO
 | ||||
|     /// Physical address of the register area this VMA maps to.
 | ||||
|     PAddr paddr = 0; | ||||
|     Common::MemoryHookPointer mmio_handler = nullptr; | ||||
| 
 | ||||
|     /// Tests if this area can be merged to the right with `next`.
 | ||||
|     bool CanBeMergedWith(const VirtualMemoryArea& next) const; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Manages a process' virtual addressing space. This class maintains a list of allocated and free | ||||
|  * regions in the address space, along with their attributes, and allows kernel clients to | ||||
|  * manipulate it, adjusting the page table to match. | ||||
|  * | ||||
|  * This is similar in idea and purpose to the VM manager present in operating system kernels, with | ||||
|  * the main difference being that it doesn't have to support swapping or memory mapping of files. | ||||
|  * The implementation is also simplified by not having to allocate page frames. See these articles | ||||
|  * about the Linux kernel for an explantion of the concept and implementation: | ||||
|  *  - http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/
 | ||||
|  *  - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
 | ||||
|  */ | ||||
| class VMManager final { | ||||
|     using VMAMap = std::map<VAddr, VirtualMemoryArea>; | ||||
| 
 | ||||
| public: | ||||
|     using VMAHandle = VMAMap::const_iterator; | ||||
| 
 | ||||
|     explicit VMManager(Core::System& system); | ||||
|     ~VMManager(); | ||||
| 
 | ||||
|     /// Clears the address space map, re-initializing with a single free area.
 | ||||
|     void Reset(FileSys::ProgramAddressSpaceType type); | ||||
| 
 | ||||
|     /// Finds the VMA in which the given address is included in, or `vma_map.end()`.
 | ||||
|     VMAHandle FindVMA(VAddr target) const; | ||||
| 
 | ||||
|     /// Indicates whether or not the given handle is within the VMA map.
 | ||||
|     bool IsValidHandle(VMAHandle handle) const; | ||||
| 
 | ||||
|     // TODO(yuriks): Should these functions actually return the handle?
 | ||||
| 
 | ||||
|     /**
 | ||||
|      * Maps part of a ref-counted block of memory at a given address. | ||||
|      * | ||||
|      * @param target The guest address to start the mapping at. | ||||
|      * @param block The block to be mapped. | ||||
|      * @param offset Offset into `block` to map from. | ||||
|      * @param size Size of the mapping. | ||||
|      * @param state MemoryState tag to attach to the VMA. | ||||
|      */ | ||||
|     ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<PhysicalMemory> block, | ||||
|                                         std::size_t offset, u64 size, MemoryState state, | ||||
|                                         VMAPermission perm = VMAPermission::ReadWrite); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Maps an unmanaged host memory pointer at a given address. | ||||
|      * | ||||
|      * @param target The guest address to start the mapping at. | ||||
|      * @param memory The memory to be mapped. | ||||
|      * @param size Size of the mapping. | ||||
|      * @param state MemoryState tag to attach to the VMA. | ||||
|      */ | ||||
|     ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Finds the first free memory region of the given size within | ||||
|      * the user-addressable ASLR memory region. | ||||
|      * | ||||
|      * @param size The size of the desired region in bytes. | ||||
|      * | ||||
|      * @returns If successful, the base address of the free region with | ||||
|      *          the given size. | ||||
|      */ | ||||
|     ResultVal<VAddr> FindFreeRegion(u64 size) const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Finds the first free address range that can hold a region of the desired size | ||||
|      * | ||||
|      * @param begin The starting address of the range. | ||||
|      *              This is treated as an inclusive beginning address. | ||||
|      * | ||||
|      * @param end   The ending address of the range. | ||||
|      *              This is treated as an exclusive ending address. | ||||
|      * | ||||
|      * @param size  The size of the free region to attempt to locate, | ||||
|      *              in bytes. | ||||
|      * | ||||
|      * @returns If successful, the base address of the free region with | ||||
|      *          the given size. | ||||
|      * | ||||
|      * @returns If unsuccessful, a result containing an error code. | ||||
|      * | ||||
|      * @pre The starting address must be less than the ending address. | ||||
|      * @pre The size must not exceed the address range itself. | ||||
|      */ | ||||
|     ResultVal<VAddr> FindFreeRegion(VAddr begin, VAddr end, u64 size) const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Maps a memory-mapped IO region at a given address. | ||||
|      * | ||||
|      * @param target The guest address to start the mapping at. | ||||
|      * @param paddr The physical address where the registers are present. | ||||
|      * @param size Size of the mapping. | ||||
|      * @param state MemoryState tag to attach to the VMA. | ||||
|      * @param mmio_handler The handler that will implement read and write for this MMIO region. | ||||
|      */ | ||||
|     ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, | ||||
|                                  Common::MemoryHookPointer mmio_handler); | ||||
| 
 | ||||
|     /// Unmaps a range of addresses, splitting VMAs as necessary.
 | ||||
|     ResultCode UnmapRange(VAddr target, u64 size); | ||||
| 
 | ||||
|     /// Changes the permissions of the given VMA.
 | ||||
|     VMAHandle Reprotect(VMAHandle vma, VMAPermission new_perms); | ||||
| 
 | ||||
|     /// Changes the permissions of a range of addresses, splitting VMAs as necessary.
 | ||||
|     ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); | ||||
| 
 | ||||
|     ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state); | ||||
| 
 | ||||
|     /// Attempts to allocate a heap with the given size.
 | ||||
|     ///
 | ||||
|     /// @param size The size of the heap to allocate in bytes.
 | ||||
|     ///
 | ||||
|     /// @note If a heap is currently allocated, and this is called
 | ||||
|     ///       with a size that is equal to the size of the current heap,
 | ||||
|     ///       then this function will do nothing and return the current
 | ||||
|     ///       heap's starting address, as there's no need to perform
 | ||||
|     ///       any additional heap allocation work.
 | ||||
|     ///
 | ||||
|     /// @note If a heap is currently allocated, and this is called
 | ||||
|     ///       with a size less than the current heap's size, then
 | ||||
|     ///       this function will attempt to shrink the heap.
 | ||||
|     ///
 | ||||
|     /// @note If a heap is currently allocated, and this is called
 | ||||
|     ///       with a size larger than the current heap's size, then
 | ||||
|     ///       this function will attempt to extend the size of the heap.
 | ||||
|     ///
 | ||||
|     /// @returns A result indicating either success or failure.
 | ||||
|     ///          <p>
 | ||||
|     ///          If successful, this function will return a result
 | ||||
|     ///          containing the starting address to the allocated heap.
 | ||||
|     ///          <p>
 | ||||
|     ///          If unsuccessful, this function will return a result
 | ||||
|     ///          containing an error code.
 | ||||
|     ///
 | ||||
|     /// @pre The given size must lie within the allowable heap
 | ||||
|     ///      memory region managed by this VMManager instance.
 | ||||
|     ///      Failure to abide by this will result in ERR_OUT_OF_MEMORY
 | ||||
|     ///      being returned as the result.
 | ||||
|     ///
 | ||||
|     ResultVal<VAddr> SetHeapSize(u64 size); | ||||
| 
 | ||||
|     /// Maps memory at a given address.
 | ||||
|     ///
 | ||||
|     /// @param target The virtual address to map memory at.
 | ||||
|     /// @param size   The amount of memory to map.
 | ||||
|     ///
 | ||||
|     /// @note The destination address must lie within the Map region.
 | ||||
|     ///
 | ||||
|     /// @note This function requires that SystemResourceSize be non-zero,
 | ||||
|     ///       however, this is just because if it were not then the
 | ||||
|     ///       resulting page tables could be exploited on hardware by
 | ||||
|     ///       a malicious program. SystemResource usage does not need
 | ||||
|     ///       to be explicitly checked or updated here.
 | ||||
|     ResultCode MapPhysicalMemory(VAddr target, u64 size); | ||||
| 
 | ||||
|     /// Unmaps memory at a given address.
 | ||||
|     ///
 | ||||
|     /// @param target The virtual address to unmap memory at.
 | ||||
|     /// @param size   The amount of memory to unmap.
 | ||||
|     ///
 | ||||
|     /// @note The destination address must lie within the Map region.
 | ||||
|     ///
 | ||||
|     /// @note This function requires that SystemResourceSize be non-zero,
 | ||||
|     ///       however, this is just because if it were not then the
 | ||||
|     ///       resulting page tables could be exploited on hardware by
 | ||||
|     ///       a malicious program. SystemResource usage does not need
 | ||||
|     ///       to be explicitly checked or updated here.
 | ||||
|     ResultCode UnmapPhysicalMemory(VAddr target, u64 size); | ||||
| 
 | ||||
|     /// Maps a region of memory as code memory.
 | ||||
|     ///
 | ||||
|     /// @param dst_address The base address of the region to create the aliasing memory region.
 | ||||
|     /// @param src_address The base address of the region to be aliased.
 | ||||
|     /// @param size        The total amount of memory to map in bytes.
 | ||||
|     ///
 | ||||
|     /// @pre Both memory regions lie within the actual addressable address space.
 | ||||
|     ///
 | ||||
|     /// @post After this function finishes execution, assuming success, then the address range
 | ||||
|     ///       [dst_address, dst_address+size) will alias the memory region,
 | ||||
|     ///       [src_address, src_address+size).
 | ||||
|     ///       <p>
 | ||||
|     ///       What this also entails is as follows:
 | ||||
|     ///          1. The aliased region gains the Locked memory attribute.
 | ||||
|     ///          2. The aliased region becomes read-only.
 | ||||
|     ///          3. The aliasing region becomes read-only.
 | ||||
|     ///          4. The aliasing region is created with a memory state of MemoryState::CodeModule.
 | ||||
|     ///
 | ||||
|     ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); | ||||
| 
 | ||||
|     /// Unmaps a region of memory designated as code module memory.
 | ||||
|     ///
 | ||||
|     /// @param dst_address The base address of the memory region aliasing the source memory region.
 | ||||
|     /// @param src_address The base address of the memory region being aliased.
 | ||||
|     /// @param size        The size of the memory region to unmap in bytes.
 | ||||
|     ///
 | ||||
|     /// @pre Both memory ranges lie within the actual addressable address space.
 | ||||
|     ///
 | ||||
|     /// @pre The memory region being unmapped has been previously been mapped
 | ||||
|     ///      by a call to MapCodeMemory.
 | ||||
|     ///
 | ||||
|     /// @post After execution of the function, if successful. the aliasing memory region
 | ||||
|     ///       will be unmapped and the aliased region will have various traits about it
 | ||||
|     ///       restored to what they were prior to the original mapping call preceding
 | ||||
|     ///       this function call.
 | ||||
|     ///       <p>
 | ||||
|     ///       What this also entails is as follows:
 | ||||
|     ///           1. The state of the memory region will now indicate a general heap region.
 | ||||
|     ///           2. All memory attributes for the memory region are cleared.
 | ||||
|     ///           3. Memory permissions for the region are restored to user read/write.
 | ||||
|     ///
 | ||||
|     ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); | ||||
| 
 | ||||
|     /// Queries the memory manager for information about the given address.
 | ||||
|     ///
 | ||||
|     /// @param address The address to query the memory manager about for information.
 | ||||
|     ///
 | ||||
|     /// @return A MemoryInfo instance containing information about the given address.
 | ||||
|     ///
 | ||||
|     MemoryInfo QueryMemory(VAddr address) const; | ||||
| 
 | ||||
|     /// Sets an attribute across the given address range.
 | ||||
|     ///
 | ||||
|     /// @param address   The starting address
 | ||||
|     /// @param size      The size of the range to set the attribute on.
 | ||||
|     /// @param mask      The attribute mask
 | ||||
|     /// @param attribute The attribute to set across the given address range
 | ||||
|     ///
 | ||||
|     /// @returns RESULT_SUCCESS if successful
 | ||||
|     /// @returns ERR_INVALID_ADDRESS_STATE if the attribute could not be set.
 | ||||
|     ///
 | ||||
|     ResultCode SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, | ||||
|                                   MemoryAttribute attribute); | ||||
| 
 | ||||
|     /**
 | ||||
|      * 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 PhysicalMemory* block); | ||||
| 
 | ||||
|     /// Dumps the address space layout to the log, for debugging
 | ||||
|     void LogLayout() const; | ||||
| 
 | ||||
|     /// Gets the total memory usage, used by svcGetInfo
 | ||||
|     u64 GetTotalPhysicalMemoryAvailable() const; | ||||
| 
 | ||||
|     /// Gets the address space base address
 | ||||
|     VAddr GetAddressSpaceBaseAddress() const; | ||||
| 
 | ||||
|     /// Gets the address space end address
 | ||||
|     VAddr GetAddressSpaceEndAddress() const; | ||||
| 
 | ||||
|     /// Gets the total address space address size in bytes
 | ||||
|     u64 GetAddressSpaceSize() const; | ||||
| 
 | ||||
|     /// Gets the address space width in bits.
 | ||||
|     u64 GetAddressSpaceWidth() const; | ||||
| 
 | ||||
|     /// Determines whether or not the given address range lies within the address space.
 | ||||
|     bool IsWithinAddressSpace(VAddr address, u64 size) const; | ||||
| 
 | ||||
|     /// Gets the base address of the ASLR region.
 | ||||
|     VAddr GetASLRRegionBaseAddress() const; | ||||
| 
 | ||||
|     /// Gets the end address of the ASLR region.
 | ||||
|     VAddr GetASLRRegionEndAddress() const; | ||||
| 
 | ||||
|     /// Gets the size of the ASLR region
 | ||||
|     u64 GetASLRRegionSize() const; | ||||
| 
 | ||||
|     /// Determines whether or not the specified address range is within the ASLR region.
 | ||||
|     bool IsWithinASLRRegion(VAddr address, u64 size) const; | ||||
| 
 | ||||
|     /// Gets the base address of the code region.
 | ||||
|     VAddr GetCodeRegionBaseAddress() const; | ||||
| 
 | ||||
|     /// Gets the end address of the code region.
 | ||||
|     VAddr GetCodeRegionEndAddress() const; | ||||
| 
 | ||||
|     /// Gets the total size of the code region in bytes.
 | ||||
|     u64 GetCodeRegionSize() const; | ||||
| 
 | ||||
|     /// Determines whether or not the specified range is within the code region.
 | ||||
|     bool IsWithinCodeRegion(VAddr address, u64 size) const; | ||||
| 
 | ||||
|     /// Gets the base address of the heap region.
 | ||||
|     VAddr GetHeapRegionBaseAddress() const; | ||||
| 
 | ||||
|     /// Gets the end address of the heap region;
 | ||||
|     VAddr GetHeapRegionEndAddress() const; | ||||
| 
 | ||||
|     /// Gets the total size of the heap region in bytes.
 | ||||
|     u64 GetHeapRegionSize() const; | ||||
| 
 | ||||
|     /// Gets the total size of the current heap in bytes.
 | ||||
|     ///
 | ||||
|     /// @note This is the current allocated heap size, not the size
 | ||||
|     ///       of the region it's allowed to exist within.
 | ||||
|     ///
 | ||||
|     u64 GetCurrentHeapSize() const; | ||||
| 
 | ||||
|     /// Determines whether or not the specified range is within the heap region.
 | ||||
|     bool IsWithinHeapRegion(VAddr address, u64 size) const; | ||||
| 
 | ||||
|     /// Gets the base address of the map region.
 | ||||
|     VAddr GetMapRegionBaseAddress() const; | ||||
| 
 | ||||
|     /// Gets the end address of the map region.
 | ||||
|     VAddr GetMapRegionEndAddress() const; | ||||
| 
 | ||||
|     /// Gets the total size of the map region in bytes.
 | ||||
|     u64 GetMapRegionSize() const; | ||||
| 
 | ||||
|     /// Determines whether or not the specified range is within the map region.
 | ||||
|     bool IsWithinMapRegion(VAddr address, u64 size) const; | ||||
| 
 | ||||
|     /// Gets the base address of the stack region.
 | ||||
|     VAddr GetStackRegionBaseAddress() const; | ||||
| 
 | ||||
|     /// Gets the end address of the stack region.
 | ||||
|     VAddr GetStackRegionEndAddress() const; | ||||
| 
 | ||||
|     /// Gets the total size of the stack region in bytes.
 | ||||
|     u64 GetStackRegionSize() const; | ||||
| 
 | ||||
|     /// Determines whether or not the given address range is within the stack region
 | ||||
|     bool IsWithinStackRegion(VAddr address, u64 size) const; | ||||
| 
 | ||||
|     /// Gets the base address of the TLS IO region.
 | ||||
|     VAddr GetTLSIORegionBaseAddress() const; | ||||
| 
 | ||||
|     /// Gets the end address of the TLS IO region.
 | ||||
|     VAddr GetTLSIORegionEndAddress() const; | ||||
| 
 | ||||
|     /// Gets the total size of the TLS IO region in bytes.
 | ||||
|     u64 GetTLSIORegionSize() const; | ||||
| 
 | ||||
|     /// Determines if the given address range is within the TLS IO region.
 | ||||
|     bool IsWithinTLSIORegion(VAddr address, u64 size) const; | ||||
| 
 | ||||
|     /// Each VMManager has its own page table, which is set as the main one when the owning process
 | ||||
|     /// is scheduled.
 | ||||
|     Common::PageTable page_table{Memory::PAGE_BITS}; | ||||
| 
 | ||||
|     using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>; | ||||
| 
 | ||||
|     /// Checks if an address range adheres to the specified states provided.
 | ||||
|     ///
 | ||||
|     /// @param address         The starting address of the address range.
 | ||||
|     /// @param size            The size of the address range.
 | ||||
|     /// @param state_mask      The memory state mask.
 | ||||
|     /// @param state           The state to compare the individual VMA states against,
 | ||||
|     ///                        which is done in the form of: (vma.state & state_mask) != state.
 | ||||
|     /// @param permission_mask The memory permissions mask.
 | ||||
|     /// @param permissions     The permission to compare the individual VMA permissions against,
 | ||||
|     ///                        which is done in the form of:
 | ||||
|     ///                        (vma.permission & permission_mask) != permission.
 | ||||
|     /// @param attribute_mask  The memory attribute mask.
 | ||||
|     /// @param attribute       The memory attributes to compare the individual VMA attributes
 | ||||
|     ///                        against, which is done in the form of:
 | ||||
|     ///                        (vma.attributes & attribute_mask) != attribute.
 | ||||
|     /// @param ignore_mask     The memory attributes to ignore during the check.
 | ||||
|     ///
 | ||||
|     /// @returns If successful, returns a tuple containing the memory attributes
 | ||||
|     ///          (with ignored bits specified by ignore_mask unset), memory permissions, and
 | ||||
|     ///          memory state across the memory range.
 | ||||
|     /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
 | ||||
|     ///
 | ||||
|     CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state, | ||||
|                                  VMAPermission permission_mask, VMAPermission permissions, | ||||
|                                  MemoryAttribute attribute_mask, MemoryAttribute attribute, | ||||
|                                  MemoryAttribute ignore_mask) const; | ||||
| 
 | ||||
| private: | ||||
|     using VMAIter = VMAMap::iterator; | ||||
| 
 | ||||
|     /// 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, u64 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, u64 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. | ||||
|      */ | ||||
|     VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Checks for and merges the specified VMA with adjacent ones if possible. | ||||
|      * @returns the merged VMA or the original if no merging was possible. | ||||
|      */ | ||||
|     VMAIter MergeAdjacent(VMAIter vma); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Merges two adjacent VMAs. | ||||
|      */ | ||||
|     void MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right); | ||||
| 
 | ||||
|     /// Updates the pages corresponding to this VMA so they match the VMA's attributes.
 | ||||
|     void UpdatePageTableForVMA(const VirtualMemoryArea& vma); | ||||
| 
 | ||||
|     /// Initializes memory region ranges to adhere to a given address space type.
 | ||||
|     void InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type); | ||||
| 
 | ||||
|     /// Clears the underlying map and page table.
 | ||||
|     void Clear(); | ||||
| 
 | ||||
|     /// Clears out the VMA map, unmapping any previously mapped ranges.
 | ||||
|     void ClearVMAMap(); | ||||
| 
 | ||||
|     /// Clears out the page table
 | ||||
|     void ClearPageTable(); | ||||
| 
 | ||||
|     /// Gets the amount of memory currently mapped (state != Unmapped) in a range.
 | ||||
|     ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; | ||||
| 
 | ||||
|     /// Gets the amount of memory unmappable by UnmapPhysicalMemory in a range.
 | ||||
|     ResultVal<std::size_t> SizeOfUnmappablePhysicalMemoryInRange(VAddr address, | ||||
|                                                                  std::size_t size) const; | ||||
| 
 | ||||
|     /**
 | ||||
|      * A map covering the entirety of the managed address space, keyed by the `base` field of each | ||||
|      * VMA. It must always be modified by splitting or merging VMAs, so that the invariant | ||||
|      * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be | ||||
|      * merged when possible so that no two similar and adjacent regions exist that have not been | ||||
|      * merged. | ||||
|      */ | ||||
|     VMAMap vma_map; | ||||
| 
 | ||||
|     u32 address_space_width = 0; | ||||
|     VAddr address_space_base = 0; | ||||
|     VAddr address_space_end = 0; | ||||
| 
 | ||||
|     VAddr aslr_region_base = 0; | ||||
|     VAddr aslr_region_end = 0; | ||||
| 
 | ||||
|     VAddr code_region_base = 0; | ||||
|     VAddr code_region_end = 0; | ||||
| 
 | ||||
|     VAddr heap_region_base = 0; | ||||
|     VAddr heap_region_end = 0; | ||||
| 
 | ||||
|     VAddr map_region_base = 0; | ||||
|     VAddr map_region_end = 0; | ||||
| 
 | ||||
|     VAddr stack_region_base = 0; | ||||
|     VAddr stack_region_end = 0; | ||||
| 
 | ||||
|     VAddr tls_io_region_base = 0; | ||||
|     VAddr tls_io_region_end = 0; | ||||
| 
 | ||||
|     // 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<PhysicalMemory> heap_memory; | ||||
| 
 | ||||
|     // The end of the currently allocated heap. This is not an inclusive
 | ||||
|     // end of the range. This is essentially 'base_address + current_size'.
 | ||||
|     VAddr heap_end = 0; | ||||
| 
 | ||||
|     // The current amount of memory mapped via MapPhysicalMemory.
 | ||||
|     // This is used here (and in Nintendo's kernel) only for debugging, and does not impact
 | ||||
|     // any behavior.
 | ||||
|     u64 physical_memory_mapped = 0; | ||||
| 
 | ||||
|     Core::System& system; | ||||
| }; | ||||
| } // namespace Kernel
 | ||||
| @ -210,7 +210,7 @@ private: | ||||
| 
 | ||||
|     /// This is the event handle used to check if the audio buffer was released
 | ||||
|     Kernel::EventPair buffer_event; | ||||
|     Memory::Memory& main_memory; | ||||
|     Core::Memory::Memory& main_memory; | ||||
| }; | ||||
| 
 | ||||
| AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} { | ||||
|  | ||||
| @ -14,6 +14,7 @@ | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/readable_event.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/kernel/writable_event.h" | ||||
| @ -53,9 +54,7 @@ IAppletResource::IAppletResource(Core::System& system) | ||||
|     RegisterHandlers(functions); | ||||
| 
 | ||||
|     auto& kernel = system.Kernel(); | ||||
|     shared_mem = Kernel::SharedMemory::Create( | ||||
|         kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, | ||||
|         Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); | ||||
|     shared_mem = SharedFrom(&kernel.GetHidSharedMem()); | ||||
| 
 | ||||
|     MakeController<Controller_DebugPad>(HidController::DebugPad); | ||||
|     MakeController<Controller_Touchscreen>(HidController::Touchscreen); | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/service/hid/irs.h" | ||||
| 
 | ||||
| @ -38,9 +39,8 @@ IRS::IRS(Core::System& system) : ServiceFramework{"irs"}, system(system) { | ||||
|     RegisterHandlers(functions); | ||||
| 
 | ||||
|     auto& kernel = system.Kernel(); | ||||
|     shared_mem = Kernel::SharedMemory::Create( | ||||
|         kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite, | ||||
|         Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory"); | ||||
| 
 | ||||
|     shared_mem = SharedFrom(&kernel.GetIrsSharedMem()); | ||||
| } | ||||
| 
 | ||||
| void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { | ||||
|  | ||||
| @ -8,14 +8,21 @@ | ||||
| 
 | ||||
| #include "common/alignment.h" | ||||
| #include "common/hex_util.h" | ||||
| #include "common/scope_exit.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/memory/system_control.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/service/ldr/ldr.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/loader/nro.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Service::LDR { | ||||
| 
 | ||||
| constexpr ResultCode ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2}; | ||||
| 
 | ||||
| constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51}; | ||||
| constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52}; | ||||
| constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53}; | ||||
| @ -29,7 +36,61 @@ constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84}; | ||||
| constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85}; | ||||
| constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; | ||||
| 
 | ||||
| constexpr u64 MAXIMUM_LOADED_RO = 0x40; | ||||
| constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; | ||||
| constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; | ||||
| 
 | ||||
| struct NRRHeader { | ||||
|     u32_le magic; | ||||
|     INSERT_PADDING_BYTES(12); | ||||
|     u64_le title_id_mask; | ||||
|     u64_le title_id_pattern; | ||||
|     INSERT_PADDING_BYTES(16); | ||||
|     std::array<u8, 0x100> modulus; | ||||
|     std::array<u8, 0x100> signature_1; | ||||
|     std::array<u8, 0x100> signature_2; | ||||
|     u64_le title_id; | ||||
|     u32_le size; | ||||
|     INSERT_PADDING_BYTES(4); | ||||
|     u32_le hash_offset; | ||||
|     u32_le hash_count; | ||||
|     INSERT_PADDING_BYTES(8); | ||||
| }; | ||||
| static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); | ||||
| 
 | ||||
| struct NROHeader { | ||||
|     INSERT_PADDING_WORDS(1); | ||||
|     u32_le mod_offset; | ||||
|     INSERT_PADDING_WORDS(2); | ||||
|     u32_le magic; | ||||
|     u32_le version; | ||||
|     u32_le nro_size; | ||||
|     u32_le flags; | ||||
|     u32_le text_offset; | ||||
|     u32_le text_size; | ||||
|     u32_le ro_offset; | ||||
|     u32_le ro_size; | ||||
|     u32_le rw_offset; | ||||
|     u32_le rw_size; | ||||
|     u32_le bss_size; | ||||
|     INSERT_PADDING_WORDS(1); | ||||
|     std::array<u8, 0x20> build_id; | ||||
|     INSERT_PADDING_BYTES(0x20); | ||||
| }; | ||||
| static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); | ||||
| 
 | ||||
| using SHA256Hash = std::array<u8, 0x20>; | ||||
| 
 | ||||
| struct NROInfo { | ||||
|     SHA256Hash hash{}; | ||||
|     VAddr nro_address{}; | ||||
|     std::size_t nro_size{}; | ||||
|     VAddr bss_address{}; | ||||
|     std::size_t bss_size{}; | ||||
|     std::size_t text_size{}; | ||||
|     std::size_t ro_size{}; | ||||
|     std::size_t data_size{}; | ||||
|     VAddr src_addr{}; | ||||
| }; | ||||
| 
 | ||||
| class DebugMonitor final : public ServiceFramework<DebugMonitor> { | ||||
| public: | ||||
| @ -84,7 +145,7 @@ public: | ||||
|             {0, &RelocatableObject::LoadNro, "LoadNro"}, | ||||
|             {1, &RelocatableObject::UnloadNro, "UnloadNro"}, | ||||
|             {2, &RelocatableObject::LoadNrr, "LoadNrr"}, | ||||
|             {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, | ||||
|             {3, nullptr, "UnloadNrr"}, | ||||
|             {4, &RelocatableObject::Initialize, "Initialize"}, | ||||
|             {10, nullptr, "LoadNrrEx"}, | ||||
|         }; | ||||
| @ -190,46 +251,125 @@ public: | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|     } | ||||
| 
 | ||||
|     void UnloadNrr(Kernel::HLERequestContext& ctx) { | ||||
|         if (!initialized) { | ||||
|             LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(ERROR_NOT_INITIALIZED); | ||||
|             return; | ||||
|     bool ValidateRegionForMap(Kernel::Memory::PageTable& page_table, VAddr start, | ||||
|                               std::size_t size) const { | ||||
|         constexpr std::size_t padding_size{4 * Kernel::Memory::PageSize}; | ||||
|         const auto start_info{page_table.QueryInfo(start - 1)}; | ||||
| 
 | ||||
|         if (start_info.state != Kernel::Memory::MemoryState::Free) { | ||||
|             return {}; | ||||
|         } | ||||
| 
 | ||||
|         struct Parameters { | ||||
|             u64_le process_id; | ||||
|             u64_le nrr_address; | ||||
|         }; | ||||
| 
 | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         const auto [process_id, nrr_address] = rp.PopRaw<Parameters>(); | ||||
| 
 | ||||
|         LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id, | ||||
|                   nrr_address); | ||||
| 
 | ||||
|         if (!Common::Is4KBAligned(nrr_address)) { | ||||
|             LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", | ||||
|                       nrr_address); | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(ERROR_INVALID_ALIGNMENT); | ||||
|             return; | ||||
|         if (start_info.GetAddress() > (start - padding_size)) { | ||||
|             return {}; | ||||
|         } | ||||
| 
 | ||||
|         const auto iter = nrr.find(nrr_address); | ||||
|         if (iter == nrr.end()) { | ||||
|             LOG_ERROR(Service_LDR, | ||||
|                       "Attempting to unload NRR which has not been loaded! (addr={:016X})", | ||||
|                       nrr_address); | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(ERROR_INVALID_NRR_ADDRESS); | ||||
|             return; | ||||
|         const auto end_info{page_table.QueryInfo(start + size)}; | ||||
| 
 | ||||
|         if (end_info.state != Kernel::Memory::MemoryState::Free) { | ||||
|             return {}; | ||||
|         } | ||||
| 
 | ||||
|         nrr.erase(iter); | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|         return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); | ||||
|     } | ||||
| 
 | ||||
|     VAddr GetRandomMapRegion(const Kernel::Memory::PageTable& page_table, std::size_t size) const { | ||||
|         VAddr addr{}; | ||||
|         const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >> | ||||
|                                     Kernel::Memory::PageBits}; | ||||
|         do { | ||||
|             addr = page_table.GetAliasCodeRegionStart() + | ||||
|                    (Kernel::Memory::SystemControl::GenerateRandomRange(0, end_pages) | ||||
|                     << Kernel::Memory::PageBits); | ||||
|         } while (!page_table.IsInsideAddressSpace(addr, size) || | ||||
|                  page_table.IsInsideHeapRegion(addr, size) || | ||||
|                  page_table.IsInsideAliasRegion(addr, size)); | ||||
|         return addr; | ||||
|     } | ||||
| 
 | ||||
|     ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress, | ||||
|                                           u64 size) const { | ||||
|         for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { | ||||
|             auto& page_table{process->PageTable()}; | ||||
|             const VAddr addr{GetRandomMapRegion(page_table, size)}; | ||||
|             const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)}; | ||||
| 
 | ||||
|             if (result == Kernel::ERR_INVALID_ADDRESS_STATE) { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             CASCADE_CODE(result); | ||||
| 
 | ||||
|             if (ValidateRegionForMap(page_table, addr, size)) { | ||||
|                 return MakeResult<VAddr>(addr); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return ERROR_INSUFFICIENT_ADDRESS_SPACE; | ||||
|     } | ||||
| 
 | ||||
|     ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size, | ||||
|                             VAddr bss_addr, std::size_t bss_size, std::size_t size) const { | ||||
| 
 | ||||
|         for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { | ||||
|             auto& page_table{process->PageTable()}; | ||||
|             VAddr addr{}; | ||||
| 
 | ||||
|             CASCADE_RESULT(addr, MapProcessCodeMemory(process, nro_addr, nro_size)); | ||||
| 
 | ||||
|             if (bss_size) { | ||||
|                 auto block_guard = detail::ScopeExit([&] { | ||||
|                     page_table.UnmapProcessCodeMemory(addr + nro_size, bss_addr, bss_size); | ||||
|                     page_table.UnmapProcessCodeMemory(addr, nro_addr, nro_size); | ||||
|                 }); | ||||
| 
 | ||||
|                 const ResultCode result{ | ||||
|                     page_table.MapProcessCodeMemory(addr + nro_size, bss_addr, bss_size)}; | ||||
| 
 | ||||
|                 if (result == Kernel::ERR_INVALID_ADDRESS_STATE) { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 if (result.IsError()) { | ||||
|                     return result; | ||||
|                 } | ||||
| 
 | ||||
|                 block_guard.Cancel(); | ||||
|             } | ||||
| 
 | ||||
|             if (ValidateRegionForMap(page_table, addr, size)) { | ||||
|                 return MakeResult<VAddr>(addr); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return ERROR_INSUFFICIENT_ADDRESS_SPACE; | ||||
|     } | ||||
| 
 | ||||
|     ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, | ||||
|                        VAddr start) const { | ||||
|         const VAddr text_start{start + nro_header.text_offset}; | ||||
|         const VAddr ro_start{start + nro_header.ro_offset}; | ||||
|         const VAddr data_start{start + nro_header.rw_offset}; | ||||
|         const VAddr bss_start{data_start + nro_header.rw_size}; | ||||
|         const VAddr bss_end_addr{ | ||||
|             Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; | ||||
| 
 | ||||
|         auto CopyCode{[&](VAddr src_addr, VAddr dst_addr, u64 size) { | ||||
|             std::vector<u8> source_data(size); | ||||
|             system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); | ||||
|             system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); | ||||
|         }}; | ||||
|         CopyCode(nro_addr + nro_header.text_offset, text_start, nro_header.text_size); | ||||
|         CopyCode(nro_addr + nro_header.ro_offset, ro_start, nro_header.ro_size); | ||||
|         CopyCode(nro_addr + nro_header.rw_offset, data_start, nro_header.rw_size); | ||||
| 
 | ||||
|         CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( | ||||
|             text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); | ||||
|         CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( | ||||
|             ro_start, data_start - ro_start, Kernel::Memory::MemoryPermission::Read)); | ||||
| 
 | ||||
|         return process->PageTable().SetCodeMemoryPermission( | ||||
|             data_start, bss_end_addr - data_start, Kernel::Memory::MemoryPermission::ReadAndWrite); | ||||
|     } | ||||
| 
 | ||||
|     void LoadNro(Kernel::HLERequestContext& ctx) { | ||||
| @ -317,9 +457,9 @@ public: | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         NROHeader header; | ||||
|         // Load and validate the NRO header
 | ||||
|         NROHeader header{}; | ||||
|         std::memcpy(&header, nro_data.data(), sizeof(NROHeader)); | ||||
| 
 | ||||
|         if (!IsValidNRO(header, nro_size, bss_size)) { | ||||
|             LOG_ERROR(Service_LDR, "NRO was invalid!"); | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
| @ -327,62 +467,48 @@ public: | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Load NRO as new executable module
 | ||||
|         auto* process = system.CurrentProcess(); | ||||
|         auto& vm_manager = process->VMManager(); | ||||
|         auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size); | ||||
| 
 | ||||
|         if (!map_address.Succeeded() || | ||||
|             *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) { | ||||
| 
 | ||||
|             LOG_ERROR(Service_LDR, | ||||
|                       "General error while allocation memory or no available memory to allocate!"); | ||||
|         // Map memory for the NRO
 | ||||
|         const auto map_result{MapNro(system.CurrentProcess(), nro_address, nro_size, bss_address, | ||||
|                                      bss_size, nro_size + bss_size)}; | ||||
|         if (map_result.Failed()) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(ERROR_INVALID_MEMORY_STATE); | ||||
|             return; | ||||
|             rb.Push(map_result.Code()); | ||||
|         } | ||||
| 
 | ||||
|         // Mark text and read-only region as ModuleCode
 | ||||
|         ASSERT(vm_manager | ||||
|                    .MirrorMemory(*map_address, nro_address, header.text_size + header.ro_size, | ||||
|                                  Kernel::MemoryState::ModuleCode) | ||||
|                    .IsSuccess()); | ||||
|         // Mark read/write region as ModuleCodeData, which is necessary if this region is used for
 | ||||
|         // TransferMemory (e.g. Final Fantasy VIII Remastered does this)
 | ||||
|         ASSERT(vm_manager | ||||
|                    .MirrorMemory(*map_address + header.rw_offset, nro_address + header.rw_offset, | ||||
|                                  header.rw_size, Kernel::MemoryState::ModuleCodeData) | ||||
|                    .IsSuccess()); | ||||
|         // Revoke permissions from the old memory region
 | ||||
|         ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None) | ||||
|                    .IsSuccess()); | ||||
| 
 | ||||
|         if (bss_size > 0) { | ||||
|             // Mark BSS region as ModuleCodeData, which is necessary if this region is used for
 | ||||
|             // TransferMemory (e.g. Final Fantasy VIII Remastered does this)
 | ||||
|             ASSERT(vm_manager | ||||
|                        .MirrorMemory(*map_address + nro_size, bss_address, bss_size, | ||||
|                                      Kernel::MemoryState::ModuleCodeData) | ||||
|                        .IsSuccess()); | ||||
|             ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None) | ||||
|                        .IsSuccess()); | ||||
|         // Load the NRO into the mapped memory
 | ||||
|         if (const auto result{LoadNro(system.CurrentProcess(), header, nro_address, *map_result)}; | ||||
|             result.IsError()) { | ||||
|             IPC::ResponseBuilder rb{ctx, 2}; | ||||
|             rb.Push(map_result.Code()); | ||||
|         } | ||||
| 
 | ||||
|         vm_manager.ReprotectRange(*map_address, header.text_size, | ||||
|                                   Kernel::VMAPermission::ReadExecute); | ||||
|         vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size, | ||||
|                                   Kernel::VMAPermission::Read); | ||||
|         vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, | ||||
|                                   Kernel::VMAPermission::ReadWrite); | ||||
|         // Track the loaded NRO
 | ||||
|         nro.insert_or_assign(*map_result, NROInfo{hash, *map_result, nro_size, bss_address, | ||||
|                                                   bss_size, header.text_size, header.ro_size, | ||||
|                                                   header.rw_size, nro_address}); | ||||
| 
 | ||||
|         // Invalidate JIT caches for the newly mapped process code
 | ||||
|         system.InvalidateCpuInstructionCaches(); | ||||
| 
 | ||||
|         nro.insert_or_assign(*map_address, | ||||
|                              NROInfo{hash, nro_address, nro_size, bss_address, bss_size}); | ||||
| 
 | ||||
|         IPC::ResponseBuilder rb{ctx, 4}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|         rb.Push(*map_address); | ||||
|         rb.Push(*map_result); | ||||
|     } | ||||
| 
 | ||||
|     ResultCode UnmapNro(const NROInfo& info) { | ||||
|         // Each region must be unmapped separately to validate memory state
 | ||||
|         auto& page_table{system.CurrentProcess()->PageTable()}; | ||||
|         CASCADE_CODE(page_table.UnmapProcessCodeMemory(info.nro_address + info.text_size + | ||||
|                                                            info.ro_size + info.data_size, | ||||
|                                                        info.bss_address, info.bss_size)); | ||||
|         CASCADE_CODE(page_table.UnmapProcessCodeMemory( | ||||
|             info.nro_address + info.text_size + info.ro_size, | ||||
|             info.src_addr + info.text_size + info.ro_size, info.data_size)); | ||||
|         CASCADE_CODE(page_table.UnmapProcessCodeMemory( | ||||
|             info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size)); | ||||
|         CASCADE_CODE( | ||||
|             page_table.UnmapProcessCodeMemory(info.nro_address, info.src_addr, info.text_size)); | ||||
|         return RESULT_SUCCESS; | ||||
|     } | ||||
| 
 | ||||
|     void UnloadNro(Kernel::HLERequestContext& ctx) { | ||||
| @ -422,30 +548,15 @@ public: | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         auto& vm_manager = system.CurrentProcess()->VMManager(); | ||||
|         const auto& nro_info = iter->second; | ||||
| 
 | ||||
|         // Unmap the mirrored memory
 | ||||
|         ASSERT( | ||||
|             vm_manager.UnmapRange(nro_address, nro_info.nro_size + nro_info.bss_size).IsSuccess()); | ||||
| 
 | ||||
|         // Reprotect the source memory
 | ||||
|         ASSERT(vm_manager | ||||
|                    .ReprotectRange(nro_info.nro_address, nro_info.nro_size, | ||||
|                                    Kernel::VMAPermission::ReadWrite) | ||||
|                    .IsSuccess()); | ||||
|         if (nro_info.bss_size > 0) { | ||||
|             ASSERT(vm_manager | ||||
|                        .ReprotectRange(nro_info.bss_address, nro_info.bss_size, | ||||
|                                        Kernel::VMAPermission::ReadWrite) | ||||
|                        .IsSuccess()); | ||||
|         } | ||||
|         const auto result{UnmapNro(iter->second)}; | ||||
| 
 | ||||
|         system.InvalidateCpuInstructionCaches(); | ||||
| 
 | ||||
|         nro.erase(iter); | ||||
| 
 | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|         rb.Push(result); | ||||
|     } | ||||
| 
 | ||||
|     void Initialize(Kernel::HLERequestContext& ctx) { | ||||
| @ -458,56 +569,7 @@ public: | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     using SHA256Hash = std::array<u8, 0x20>; | ||||
| 
 | ||||
|     struct NROHeader { | ||||
|         INSERT_PADDING_WORDS(1); | ||||
|         u32_le mod_offset; | ||||
|         INSERT_PADDING_WORDS(2); | ||||
|         u32_le magic; | ||||
|         u32_le version; | ||||
|         u32_le nro_size; | ||||
|         u32_le flags; | ||||
|         u32_le text_offset; | ||||
|         u32_le text_size; | ||||
|         u32_le ro_offset; | ||||
|         u32_le ro_size; | ||||
|         u32_le rw_offset; | ||||
|         u32_le rw_size; | ||||
|         u32_le bss_size; | ||||
|         INSERT_PADDING_WORDS(1); | ||||
|         std::array<u8, 0x20> build_id; | ||||
|         INSERT_PADDING_BYTES(0x20); | ||||
|     }; | ||||
|     static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); | ||||
| 
 | ||||
|     struct NRRHeader { | ||||
|         u32_le magic; | ||||
|         INSERT_PADDING_BYTES(12); | ||||
|         u64_le title_id_mask; | ||||
|         u64_le title_id_pattern; | ||||
|         INSERT_PADDING_BYTES(16); | ||||
|         std::array<u8, 0x100> modulus; | ||||
|         std::array<u8, 0x100> signature_1; | ||||
|         std::array<u8, 0x100> signature_2; | ||||
|         u64_le title_id; | ||||
|         u32_le size; | ||||
|         INSERT_PADDING_BYTES(4); | ||||
|         u32_le hash_offset; | ||||
|         u32_le hash_count; | ||||
|         INSERT_PADDING_BYTES(8); | ||||
|     }; | ||||
|     static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); | ||||
| 
 | ||||
|     struct NROInfo { | ||||
|         SHA256Hash hash; | ||||
|         VAddr nro_address; | ||||
|         u64 nro_size; | ||||
|         VAddr bss_address; | ||||
|         u64 bss_size; | ||||
|     }; | ||||
| 
 | ||||
|     bool initialized = false; | ||||
|     bool initialized{}; | ||||
| 
 | ||||
|     std::map<VAddr, NROInfo> nro; | ||||
|     std::map<VAddr, std::vector<SHA256Hash>> nrr; | ||||
|  | ||||
| @ -17,7 +17,7 @@ namespace Service::LM { | ||||
| 
 | ||||
| class ILogger final : public ServiceFramework<ILogger> { | ||||
| public: | ||||
|     explicit ILogger(Manager& manager_, Memory::Memory& memory_) | ||||
|     explicit ILogger(Manager& manager_, Core::Memory::Memory& memory_) | ||||
|         : ServiceFramework("ILogger"), manager{manager_}, memory{memory_} { | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &ILogger::Log, "Log"}, | ||||
| @ -75,12 +75,12 @@ private: | ||||
|     } | ||||
| 
 | ||||
|     Manager& manager; | ||||
|     Memory::Memory& memory; | ||||
|     Core::Memory::Memory& memory; | ||||
| }; | ||||
| 
 | ||||
| class LM final : public ServiceFramework<LM> { | ||||
| public: | ||||
|     explicit LM(Manager& manager_, Memory::Memory& memory_) | ||||
|     explicit LM(Manager& manager_, Core::Memory::Memory& memory_) | ||||
|         : ServiceFramework{"lm"}, manager{manager_}, memory{memory_} { | ||||
|         // clang-format off
 | ||||
|         static const FunctionInfo functions[] = { | ||||
| @ -101,7 +101,7 @@ private: | ||||
|     } | ||||
| 
 | ||||
|     Manager& manager; | ||||
|     Memory::Memory& memory; | ||||
|     Core::Memory::Memory& memory; | ||||
| }; | ||||
| 
 | ||||
| void InstallInterfaces(Core::System& system) { | ||||
|  | ||||
| @ -19,6 +19,7 @@ | ||||
| #include "core/file_sys/romfs.h" | ||||
| #include "core/file_sys/system_archive/system_archive.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/physical_memory.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/service/filesystem/filesystem.h" | ||||
| @ -265,16 +266,13 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { | ||||
| void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { | ||||
|     // Map backing memory for the font data
 | ||||
|     LOG_DEBUG(Service_NS, "called"); | ||||
|     system.CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, | ||||
|                                                         SHARED_FONT_MEM_SIZE, | ||||
|                                                         Kernel::MemoryState::Shared); | ||||
| 
 | ||||
|     // Create shared font memory object
 | ||||
|     auto& kernel = system.Kernel(); | ||||
|     impl->shared_font_mem = Kernel::SharedMemory::Create( | ||||
|         kernel, system.CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, | ||||
|         Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, | ||||
|         "PL_U:shared_font_mem"); | ||||
|     impl->shared_font_mem = SharedFrom(&kernel.GetFontSharedMem()); | ||||
| 
 | ||||
|     std::memcpy(impl->shared_font_mem->GetPointer(), impl->shared_font->data(), | ||||
|                 impl->shared_font->size()); | ||||
| 
 | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 1}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| #include "core/core_timing.h" | ||||
| #include "core/core_timing_util.h" | ||||
| #include "core/hardware_properties.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/service/time/clock_types.h" | ||||
| #include "core/hle/service/time/steady_clock_core.h" | ||||
| #include "core/hle/service/time/time_sharedmemory.h" | ||||
| @ -15,9 +16,7 @@ namespace Service::Time { | ||||
| static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000}; | ||||
| 
 | ||||
| SharedMemory::SharedMemory(Core::System& system) : system(system) { | ||||
|     shared_memory_holder = Kernel::SharedMemory::Create( | ||||
|         system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, | ||||
|         Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory"); | ||||
|     shared_memory_holder = SharedFrom(&system.Kernel().GetTimeSharedMem()); | ||||
|     std::memset(shared_memory_holder->GetPointer(), 0, SHARED_MEMORY_SIZE); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -14,6 +14,7 @@ | ||||
| #include "core/file_sys/romfs_factory.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/service/filesystem/filesystem.h" | ||||
| #include "core/loader/deconstructed_rom_directory.h" | ||||
| @ -129,27 +130,47 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | ||||
|     } | ||||
|     metadata.Print(); | ||||
| 
 | ||||
|     if (process.LoadFromMetadata(metadata).IsError()) { | ||||
|         return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; | ||||
|     } | ||||
|     const auto static_modules = {"rtld",    "main",    "subsdk0", "subsdk1", "subsdk2", "subsdk3", | ||||
|                                  "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}; | ||||
| 
 | ||||
|     const FileSys::PatchManager pm(metadata.GetTitleID()); | ||||
| 
 | ||||
|     // Load NSO modules
 | ||||
|     modules.clear(); | ||||
|     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | ||||
|     VAddr next_load_addr = base_address; | ||||
|     for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | ||||
|                                "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { | ||||
|         const FileSys::VirtualFile module_file = dir->GetFile(module); | ||||
|         if (module_file == nullptr) { | ||||
|     // Use the NSO module loader to figure out the code layout
 | ||||
|     std::size_t code_size{}; | ||||
|     for (const auto& module : static_modules) { | ||||
|         const FileSys::VirtualFile module_file{dir->GetFile(module)}; | ||||
|         if (!module_file) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         const VAddr load_addr = next_load_addr; | ||||
|         const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; | ||||
|         const auto tentative_next_load_addr = | ||||
|             AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm); | ||||
|         const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; | ||||
|         const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( | ||||
|             process, *module_file, code_size, should_pass_arguments, false)}; | ||||
|         if (!tentative_next_load_addr) { | ||||
|             return {ResultStatus::ErrorLoadingNSO, {}}; | ||||
|         } | ||||
| 
 | ||||
|         code_size = *tentative_next_load_addr; | ||||
|     } | ||||
| 
 | ||||
|     // Setup the process code layout
 | ||||
|     if (process.LoadFromMetadata(metadata, code_size).IsError()) { | ||||
|         return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; | ||||
|     } | ||||
| 
 | ||||
|     // Load NSO modules
 | ||||
|     modules.clear(); | ||||
|     const VAddr base_address{process.PageTable().GetCodeRegionStart()}; | ||||
|     VAddr next_load_addr{base_address}; | ||||
|     const FileSys::PatchManager pm{metadata.GetTitleID()}; | ||||
|     for (const auto& module : static_modules) { | ||||
|         const FileSys::VirtualFile module_file{dir->GetFile(module)}; | ||||
|         if (!module_file) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         const VAddr load_addr{next_load_addr}; | ||||
|         const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; | ||||
|         const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( | ||||
|             process, *module_file, load_addr, should_pass_arguments, true, pm)}; | ||||
|         if (!tentative_next_load_addr) { | ||||
|             return {ResultStatus::ErrorLoadingNSO, {}}; | ||||
|         } | ||||
|  | ||||
| @ -10,8 +10,8 @@ | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/hle/kernel/code_set.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/loader/elf.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| @ -393,7 +393,7 @@ AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) { | ||||
|         return {ResultStatus::ErrorIncorrectELFFileSize, {}}; | ||||
|     } | ||||
| 
 | ||||
|     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | ||||
|     const VAddr base_address = process.PageTable().GetCodeRegionStart(); | ||||
|     ElfReader elf_reader(&buffer[0]); | ||||
|     Kernel::CodeSet codeset = elf_reader.LoadInto(base_address); | ||||
|     const VAddr entry_point = codeset.entrypoint; | ||||
| @ -401,7 +401,7 @@ AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) { | ||||
|     process.LoadModule(std::move(codeset), entry_point); | ||||
| 
 | ||||
|     is_loaded = true; | ||||
|     return {ResultStatus::Success, LoadParameters{48, Memory::DEFAULT_STACK_SIZE}}; | ||||
|     return {ResultStatus::Success, LoadParameters{48, Core::Memory::DEFAULT_STACK_SIZE}}; | ||||
| } | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  | ||||
| @ -7,14 +7,16 @@ | ||||
| #include "core/file_sys/program_metadata.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/kernel/code_set.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/loader/kip.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Loader { | ||||
| 
 | ||||
| namespace { | ||||
| constexpr u32 PageAlignSize(u32 size) { | ||||
|     return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | ||||
|     return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK; | ||||
| } | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| @ -68,7 +70,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) { | ||||
|                         kip->GetMainThreadCpuCore(), kip->GetMainThreadStackSize(), | ||||
|                         kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, kip->GetKernelCapabilities()); | ||||
| 
 | ||||
|     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | ||||
|     const VAddr base_address = process.PageTable().GetCodeRegionStart(); | ||||
|     Kernel::CodeSet codeset; | ||||
|     Kernel::PhysicalMemory program_image; | ||||
| 
 | ||||
|  | ||||
| @ -16,8 +16,8 @@ | ||||
| #include "core/file_sys/vfs_offset.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/kernel/code_set.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/hle/service/filesystem/filesystem.h" | ||||
| #include "core/loader/nro.h" | ||||
| #include "core/loader/nso.h" | ||||
| @ -127,7 +127,7 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) { | ||||
| } | ||||
| 
 | ||||
| static constexpr u32 PageAlignSize(u32 size) { | ||||
|     return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | ||||
|     return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK; | ||||
| } | ||||
| 
 | ||||
| static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data, | ||||
| @ -208,7 +208,7 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { | ||||
|     } | ||||
| 
 | ||||
|     // Load NRO
 | ||||
|     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | ||||
|     const VAddr base_address = process.PageTable().GetCodeRegionStart(); | ||||
| 
 | ||||
|     if (!LoadNro(process, *file, base_address)) { | ||||
|         return {ResultStatus::ErrorLoadingNRO, {}}; | ||||
| @ -221,7 +221,7 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { | ||||
| 
 | ||||
|     is_loaded = true; | ||||
|     return {ResultStatus::Success, | ||||
|             LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}}; | ||||
|             LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}}; | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { | ||||
|  | ||||
| @ -16,8 +16,8 @@ | ||||
| #include "core/file_sys/patch_manager.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/kernel/code_set.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/loader/nso.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/settings.h" | ||||
| @ -47,7 +47,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, | ||||
| } | ||||
| 
 | ||||
| constexpr u32 PageAlignSize(u32 size) { | ||||
|     return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | ||||
|     return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK; | ||||
| } | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| @ -73,7 +73,7 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { | ||||
| 
 | ||||
| std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | ||||
|                                                const FileSys::VfsFile& file, VAddr load_base, | ||||
|                                                bool should_pass_arguments, | ||||
|                                                bool should_pass_arguments, bool load_into_process, | ||||
|                                                std::optional<FileSys::PatchManager> pm) { | ||||
|     if (file.GetSize() < sizeof(NSOHeader)) { | ||||
|         return {}; | ||||
| @ -97,21 +97,17 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | ||||
|         if (nso_header.IsSegmentCompressed(i)) { | ||||
|             data = DecompressSegment(data, nso_header.segments[i]); | ||||
|         } | ||||
|         program_image.resize(nso_header.segments[i].location + | ||||
|                              PageAlignSize(static_cast<u32>(data.size()))); | ||||
|         program_image.resize(nso_header.segments[i].location + static_cast<u32>(data.size())); | ||||
|         std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(), | ||||
|                     data.size()); | ||||
|         codeset.segments[i].addr = nso_header.segments[i].location; | ||||
|         codeset.segments[i].offset = nso_header.segments[i].location; | ||||
|         codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size())); | ||||
|         codeset.segments[i].size = nso_header.segments[i].size; | ||||
|     } | ||||
| 
 | ||||
|     if (should_pass_arguments) { | ||||
|         std::vector<u8> arg_data{Settings::values.program_args.begin(), | ||||
|                                  Settings::values.program_args.end()}; | ||||
|         if (arg_data.empty()) { | ||||
|             arg_data.resize(NSO_ARGUMENT_DEFAULT_SIZE); | ||||
|         } | ||||
|     if (should_pass_arguments && !Settings::values.program_args.empty()) { | ||||
|         const auto arg_data{Settings::values.program_args}; | ||||
| 
 | ||||
|         codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; | ||||
|         NSOArgumentHeader args_header{ | ||||
|             NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; | ||||
| @ -123,24 +119,15 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | ||||
|                     arg_data.size()); | ||||
|     } | ||||
| 
 | ||||
|     // MOD header pointer is at .text offset + 4
 | ||||
|     u32 module_offset; | ||||
|     std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); | ||||
| 
 | ||||
|     // Read MOD header
 | ||||
|     MODHeader mod_header{}; | ||||
|     // Default .bss to size in segment header if MOD0 section doesn't exist
 | ||||
|     u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; | ||||
|     std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader)); | ||||
|     const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; | ||||
|     if (has_mod_header) { | ||||
|         // Resize program image to include .bss section and page align each section
 | ||||
|         bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); | ||||
|     } | ||||
|     codeset.DataSegment().size += bss_size; | ||||
|     const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; | ||||
|     codeset.DataSegment().size += nso_header.segments[2].bss_size; | ||||
|     const u32 image_size{ | ||||
|         PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)}; | ||||
|     program_image.resize(image_size); | ||||
| 
 | ||||
|     for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { | ||||
|         codeset.segments[i].size = PageAlignSize(codeset.segments[i].size); | ||||
|     } | ||||
| 
 | ||||
|     // Apply patches if necessary
 | ||||
|     if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { | ||||
|         std::vector<u8> pi_header; | ||||
| @ -154,6 +141,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | ||||
|         std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data()); | ||||
|     } | ||||
| 
 | ||||
|     // If we aren't actually loading (i.e. just computing the process code layout), we are done
 | ||||
|     if (!load_into_process) { | ||||
|         return load_base + image_size; | ||||
|     } | ||||
| 
 | ||||
|     // Apply cheats if they exist and the program has a valid title ID
 | ||||
|     if (pm) { | ||||
|         auto& system = Core::System::GetInstance(); | ||||
| @ -182,8 +174,8 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { | ||||
|     modules.clear(); | ||||
| 
 | ||||
|     // Load module
 | ||||
|     const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | ||||
|     if (!LoadModule(process, *file, base_address, true)) { | ||||
|     const VAddr base_address = process.PageTable().GetCodeRegionStart(); | ||||
|     if (!LoadModule(process, *file, base_address, true, true)) { | ||||
|         return {ResultStatus::ErrorLoadingNSO, {}}; | ||||
|     } | ||||
| 
 | ||||
| @ -192,7 +184,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { | ||||
| 
 | ||||
|     is_loaded = true; | ||||
|     return {ResultStatus::Success, | ||||
|             LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}}; | ||||
|             LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}}; | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) { | ||||
|  | ||||
| @ -56,8 +56,6 @@ static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size."); | ||||
| static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable."); | ||||
| 
 | ||||
| constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; | ||||
| // NOTE: Official software default argument state is unverified.
 | ||||
| constexpr u64 NSO_ARGUMENT_DEFAULT_SIZE = 1; | ||||
| 
 | ||||
| struct NSOArgumentHeader { | ||||
|     u32_le allocated_size; | ||||
| @ -84,6 +82,7 @@ public: | ||||
| 
 | ||||
|     static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file, | ||||
|                                            VAddr load_base, bool should_pass_arguments, | ||||
|                                            bool load_into_process, | ||||
|                                            std::optional<FileSys::PatchManager> pm = {}); | ||||
| 
 | ||||
|     LoadResult Load(Kernel::Process& process) override; | ||||
|  | ||||
| @ -14,13 +14,14 @@ | ||||
| #include "common/swap.h" | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/core.h" | ||||
| #include "core/device_memory.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/physical_memory.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/memory.h" | ||||
| #include "video_core/gpu.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| 
 | ||||
| // Implementation class used to keep the specifics of the memory subsystem hidden
 | ||||
| // from outside classes. This also allows modification to the internals of the memory
 | ||||
| @ -29,9 +30,9 @@ struct Memory::Impl { | ||||
|     explicit Impl(Core::System& system_) : system{system_} {} | ||||
| 
 | ||||
|     void SetCurrentPageTable(Kernel::Process& process) { | ||||
|         current_page_table = &process.VMManager().page_table; | ||||
|         current_page_table = &process.PageTable().PageTableImpl(); | ||||
| 
 | ||||
|         const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth(); | ||||
|         const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); | ||||
| 
 | ||||
|         system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width); | ||||
|         system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width); | ||||
| @ -39,12 +40,7 @@ struct Memory::Impl { | ||||
|         system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width); | ||||
|     } | ||||
| 
 | ||||
|     void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, | ||||
|                          Kernel::PhysicalMemory& memory, VAddr offset) { | ||||
|         MapMemoryRegion(page_table, base, size, memory.data() + offset); | ||||
|     } | ||||
| 
 | ||||
|     void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) { | ||||
|     void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { | ||||
|         ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); | ||||
|         ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); | ||||
|         MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); | ||||
| @ -52,46 +48,27 @@ struct Memory::Impl { | ||||
| 
 | ||||
|     void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size, | ||||
|                      Common::MemoryHookPointer mmio_handler) { | ||||
|         ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); | ||||
|         ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); | ||||
|         MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, | ||||
|                  Common::PageType::Special); | ||||
| 
 | ||||
|         const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); | ||||
|         const Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, | ||||
|                                            std::move(mmio_handler)}; | ||||
|         page_table.special_regions.add( | ||||
|             std::make_pair(interval, std::set<Common::SpecialRegion>{region})); | ||||
|         UNIMPLEMENTED(); | ||||
|     } | ||||
| 
 | ||||
|     void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { | ||||
|         ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); | ||||
|         ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); | ||||
|         MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, | ||||
|                  Common::PageType::Unmapped); | ||||
| 
 | ||||
|         const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); | ||||
|         page_table.special_regions.erase(interval); | ||||
|         MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped); | ||||
|     } | ||||
| 
 | ||||
|     void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, | ||||
|                       Common::MemoryHookPointer hook) { | ||||
|         const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); | ||||
|         const Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)}; | ||||
|         page_table.special_regions.add( | ||||
|             std::make_pair(interval, std::set<Common::SpecialRegion>{region})); | ||||
|         UNIMPLEMENTED(); | ||||
|     } | ||||
| 
 | ||||
|     void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, | ||||
|                          Common::MemoryHookPointer hook) { | ||||
|         const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); | ||||
|         const Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)}; | ||||
|         page_table.special_regions.subtract( | ||||
|             std::make_pair(interval, std::set<Common::SpecialRegion>{region})); | ||||
|         UNIMPLEMENTED(); | ||||
|     } | ||||
| 
 | ||||
|     bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { | ||||
|         const auto& page_table = process.VMManager().page_table; | ||||
|         const auto& page_table = process.PageTable().PageTableImpl(); | ||||
| 
 | ||||
|         const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; | ||||
|         if (page_pointer != nullptr) { | ||||
| @ -113,55 +90,28 @@ struct Memory::Impl { | ||||
|         return IsValidVirtualAddress(*system.CurrentProcess(), vaddr); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) | ||||
|      * using a VMA from the current process | ||||
|      */ | ||||
|     u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { | ||||
|         const auto& vm_manager = process.VMManager(); | ||||
|     u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const { | ||||
|         const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; | ||||
| 
 | ||||
|         const auto it = vm_manager.FindVMA(vaddr); | ||||
|         DEBUG_ASSERT(vm_manager.IsValidHandle(it)); | ||||
| 
 | ||||
|         u8* direct_pointer = nullptr; | ||||
|         const auto& vma = it->second; | ||||
|         switch (vma.type) { | ||||
|         case Kernel::VMAType::AllocatedMemoryBlock: | ||||
|             direct_pointer = vma.backing_block->data() + vma.offset; | ||||
|             break; | ||||
|         case Kernel::VMAType::BackingMemory: | ||||
|             direct_pointer = vma.backing_memory; | ||||
|             break; | ||||
|         case Kernel::VMAType::Free: | ||||
|             return nullptr; | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|         if (!paddr) { | ||||
|             return {}; | ||||
|         } | ||||
| 
 | ||||
|         return direct_pointer + (vaddr - vma.base); | ||||
|         return system.DeviceMemory().GetPointer(paddr) + vaddr; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) | ||||
|      * using a VMA from the current process. | ||||
|      */ | ||||
|     u8* GetPointerFromVMA(VAddr vaddr) { | ||||
|         return GetPointerFromVMA(*system.CurrentProcess(), vaddr); | ||||
|     } | ||||
| 
 | ||||
|     u8* GetPointer(const VAddr vaddr) { | ||||
|         u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||||
|         if (page_pointer != nullptr) { | ||||
|     u8* GetPointer(const VAddr vaddr) const { | ||||
|         u8* const page_pointer{current_page_table->pointers[vaddr >> PAGE_BITS]}; | ||||
|         if (page_pointer) { | ||||
|             return page_pointer + vaddr; | ||||
|         } | ||||
| 
 | ||||
|         if (current_page_table->attributes[vaddr >> PAGE_BITS] == | ||||
|             Common::PageType::RasterizerCachedMemory) { | ||||
|             return GetPointerFromVMA(vaddr); | ||||
|             return GetPointerFromRasterizerCachedMemory(vaddr); | ||||
|         } | ||||
| 
 | ||||
|         LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr); | ||||
|         return nullptr; | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     u8 Read8(const VAddr addr) { | ||||
| @ -169,15 +119,33 @@ struct Memory::Impl { | ||||
|     } | ||||
| 
 | ||||
|     u16 Read16(const VAddr addr) { | ||||
|         return Read<u16_le>(addr); | ||||
|         if ((addr & 1) == 0) { | ||||
|             return Read<u16_le>(addr); | ||||
|         } else { | ||||
|             const u8 a{Read<u8>(addr)}; | ||||
|             const u8 b{Read<u8>(addr + sizeof(u8))}; | ||||
|             return (static_cast<u16>(b) << 8) | a; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     u32 Read32(const VAddr addr) { | ||||
|         return Read<u32_le>(addr); | ||||
|         if ((addr & 3) == 0) { | ||||
|             return Read<u32_le>(addr); | ||||
|         } else { | ||||
|             const u16 a{Read16(addr)}; | ||||
|             const u16 b{Read16(addr + sizeof(u16))}; | ||||
|             return (static_cast<u32>(b) << 16) | a; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     u64 Read64(const VAddr addr) { | ||||
|         return Read<u64_le>(addr); | ||||
|         if ((addr & 7) == 0) { | ||||
|             return Read<u64_le>(addr); | ||||
|         } else { | ||||
|             const u32 a{Read32(addr)}; | ||||
|             const u32 b{Read32(addr + sizeof(u32))}; | ||||
|             return (static_cast<u64>(b) << 32) | a; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void Write8(const VAddr addr, const u8 data) { | ||||
| @ -185,15 +153,30 @@ struct Memory::Impl { | ||||
|     } | ||||
| 
 | ||||
|     void Write16(const VAddr addr, const u16 data) { | ||||
|         Write<u16_le>(addr, data); | ||||
|         if ((addr & 1) == 0) { | ||||
|             Write<u16_le>(addr, data); | ||||
|         } else { | ||||
|             Write<u8>(addr, static_cast<u8>(data)); | ||||
|             Write<u8>(addr + sizeof(u8), static_cast<u8>(data >> 8)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void Write32(const VAddr addr, const u32 data) { | ||||
|         Write<u32_le>(addr, data); | ||||
|         if ((addr & 3) == 0) { | ||||
|             Write<u32_le>(addr, data); | ||||
|         } else { | ||||
|             Write16(addr, static_cast<u16>(data)); | ||||
|             Write16(addr + sizeof(u16), static_cast<u16>(data >> 16)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void Write64(const VAddr addr, const u64 data) { | ||||
|         Write<u64_le>(addr, data); | ||||
|         if ((addr & 7) == 0) { | ||||
|             Write<u64_le>(addr, data); | ||||
|         } else { | ||||
|             Write32(addr, static_cast<u32>(data)); | ||||
|             Write32(addr + sizeof(u32), static_cast<u32>(data >> 32)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     std::string ReadCString(VAddr vaddr, std::size_t max_length) { | ||||
| @ -213,7 +196,7 @@ struct Memory::Impl { | ||||
| 
 | ||||
|     void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, | ||||
|                    const std::size_t size) { | ||||
|         const auto& page_table = process.VMManager().page_table; | ||||
|         const auto& page_table = process.PageTable().PageTableImpl(); | ||||
| 
 | ||||
|         std::size_t remaining_size = size; | ||||
|         std::size_t page_index = src_addr >> PAGE_BITS; | ||||
| @ -241,7 +224,7 @@ struct Memory::Impl { | ||||
|                 break; | ||||
|             } | ||||
|             case Common::PageType::RasterizerCachedMemory: { | ||||
|                 const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | ||||
|                 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; | ||||
|                 system.GPU().FlushRegion(current_vaddr, copy_amount); | ||||
|                 std::memcpy(dest_buffer, host_ptr, copy_amount); | ||||
|                 break; | ||||
| @ -259,7 +242,7 @@ struct Memory::Impl { | ||||
| 
 | ||||
|     void ReadBlockUnsafe(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, | ||||
|                          const std::size_t size) { | ||||
|         const auto& page_table = process.VMManager().page_table; | ||||
|         const auto& page_table = process.PageTable().PageTableImpl(); | ||||
| 
 | ||||
|         std::size_t remaining_size = size; | ||||
|         std::size_t page_index = src_addr >> PAGE_BITS; | ||||
| @ -287,7 +270,7 @@ struct Memory::Impl { | ||||
|                 break; | ||||
|             } | ||||
|             case Common::PageType::RasterizerCachedMemory: { | ||||
|                 const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | ||||
|                 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; | ||||
|                 std::memcpy(dest_buffer, host_ptr, copy_amount); | ||||
|                 break; | ||||
|             } | ||||
| @ -312,7 +295,7 @@ struct Memory::Impl { | ||||
| 
 | ||||
|     void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, | ||||
|                     const std::size_t size) { | ||||
|         const auto& page_table = process.VMManager().page_table; | ||||
|         const auto& page_table = process.PageTable().PageTableImpl(); | ||||
|         std::size_t remaining_size = size; | ||||
|         std::size_t page_index = dest_addr >> PAGE_BITS; | ||||
|         std::size_t page_offset = dest_addr & PAGE_MASK; | ||||
| @ -338,7 +321,7 @@ struct Memory::Impl { | ||||
|                 break; | ||||
|             } | ||||
|             case Common::PageType::RasterizerCachedMemory: { | ||||
|                 u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | ||||
|                 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; | ||||
|                 system.GPU().InvalidateRegion(current_vaddr, copy_amount); | ||||
|                 std::memcpy(host_ptr, src_buffer, copy_amount); | ||||
|                 break; | ||||
| @ -356,7 +339,7 @@ struct Memory::Impl { | ||||
| 
 | ||||
|     void WriteBlockUnsafe(const Kernel::Process& process, const VAddr dest_addr, | ||||
|                           const void* src_buffer, const std::size_t size) { | ||||
|         const auto& page_table = process.VMManager().page_table; | ||||
|         const auto& page_table = process.PageTable().PageTableImpl(); | ||||
|         std::size_t remaining_size = size; | ||||
|         std::size_t page_index = dest_addr >> PAGE_BITS; | ||||
|         std::size_t page_offset = dest_addr & PAGE_MASK; | ||||
| @ -382,7 +365,7 @@ struct Memory::Impl { | ||||
|                 break; | ||||
|             } | ||||
|             case Common::PageType::RasterizerCachedMemory: { | ||||
|                 u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | ||||
|                 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; | ||||
|                 std::memcpy(host_ptr, src_buffer, copy_amount); | ||||
|                 break; | ||||
|             } | ||||
| @ -406,7 +389,7 @@ struct Memory::Impl { | ||||
|     } | ||||
| 
 | ||||
|     void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { | ||||
|         const auto& page_table = process.VMManager().page_table; | ||||
|         const auto& page_table = process.PageTable().PageTableImpl(); | ||||
|         std::size_t remaining_size = size; | ||||
|         std::size_t page_index = dest_addr >> PAGE_BITS; | ||||
|         std::size_t page_offset = dest_addr & PAGE_MASK; | ||||
| @ -432,7 +415,7 @@ struct Memory::Impl { | ||||
|                 break; | ||||
|             } | ||||
|             case Common::PageType::RasterizerCachedMemory: { | ||||
|                 u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | ||||
|                 u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; | ||||
|                 system.GPU().InvalidateRegion(current_vaddr, copy_amount); | ||||
|                 std::memset(host_ptr, 0, copy_amount); | ||||
|                 break; | ||||
| @ -453,7 +436,7 @@ struct Memory::Impl { | ||||
| 
 | ||||
|     void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, | ||||
|                    const std::size_t size) { | ||||
|         const auto& page_table = process.VMManager().page_table; | ||||
|         const auto& page_table = process.PageTable().PageTableImpl(); | ||||
|         std::size_t remaining_size = size; | ||||
|         std::size_t page_index = src_addr >> PAGE_BITS; | ||||
|         std::size_t page_offset = src_addr & PAGE_MASK; | ||||
| @ -479,7 +462,7 @@ struct Memory::Impl { | ||||
|                 break; | ||||
|             } | ||||
|             case Common::PageType::RasterizerCachedMemory: { | ||||
|                 const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | ||||
|                 const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)}; | ||||
|                 system.GPU().FlushRegion(current_vaddr, copy_amount); | ||||
|                 WriteBlock(process, dest_addr, host_ptr, copy_amount); | ||||
|                 break; | ||||
| @ -512,7 +495,7 @@ struct Memory::Impl { | ||||
| 
 | ||||
|         u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1; | ||||
|         for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { | ||||
|             Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||||
|             Common::PageType& page_type{current_page_table->attributes[vaddr >> PAGE_BITS]}; | ||||
| 
 | ||||
|             if (cached) { | ||||
|                 // Switch page type to cached if now cached
 | ||||
| @ -544,7 +527,7 @@ struct Memory::Impl { | ||||
|                     // that this area is already unmarked as cached.
 | ||||
|                     break; | ||||
|                 case Common::PageType::RasterizerCachedMemory: { | ||||
|                     u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); | ||||
|                     u8* pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)}; | ||||
|                     if (pointer == nullptr) { | ||||
|                         // It's possible that this function has been called while updating the
 | ||||
|                         // pagetable after unmapping a VMA. In that case the underlying VMA will no
 | ||||
| @ -573,9 +556,9 @@ struct Memory::Impl { | ||||
|      * @param memory     The memory to map. | ||||
|      * @param type       The page type to map the memory as. | ||||
|      */ | ||||
|     void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory, | ||||
|     void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, | ||||
|                   Common::PageType type) { | ||||
|         LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, | ||||
|         LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * PAGE_SIZE, | ||||
|                   (base + size) * PAGE_SIZE); | ||||
| 
 | ||||
|         // During boot, current_page_table might not be set yet, in which case we need not flush
 | ||||
| @ -593,19 +576,26 @@ struct Memory::Impl { | ||||
|         ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", | ||||
|                    base + page_table.pointers.size()); | ||||
| 
 | ||||
|         std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type); | ||||
|         if (!target) { | ||||
|             while (base != end) { | ||||
|                 page_table.pointers[base] = nullptr; | ||||
|                 page_table.attributes[base] = type; | ||||
|                 page_table.backing_addr[base] = 0; | ||||
| 
 | ||||
|         if (memory == nullptr) { | ||||
|             std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, | ||||
|                       memory); | ||||
|                 base += 1; | ||||
|             } | ||||
|         } else { | ||||
|             while (base != end) { | ||||
|                 page_table.pointers[base] = memory - (base << PAGE_BITS); | ||||
|                 page_table.pointers[base] = | ||||
|                     system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS); | ||||
|                 page_table.attributes[base] = type; | ||||
|                 page_table.backing_addr[base] = target - (base << PAGE_BITS); | ||||
| 
 | ||||
|                 ASSERT_MSG(page_table.pointers[base], | ||||
|                            "memory mapping base yield a nullptr within the table"); | ||||
| 
 | ||||
|                 base += 1; | ||||
|                 memory += PAGE_SIZE; | ||||
|                 target += PAGE_SIZE; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -640,7 +630,7 @@ struct Memory::Impl { | ||||
|             ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); | ||||
|             break; | ||||
|         case Common::PageType::RasterizerCachedMemory: { | ||||
|             const u8* const host_ptr = GetPointerFromVMA(vaddr); | ||||
|             const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; | ||||
|             system.GPU().FlushRegion(vaddr, sizeof(T)); | ||||
|             T value; | ||||
|             std::memcpy(&value, host_ptr, sizeof(T)); | ||||
| @ -682,7 +672,7 @@ struct Memory::Impl { | ||||
|             ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); | ||||
|             break; | ||||
|         case Common::PageType::RasterizerCachedMemory: { | ||||
|             u8* const host_ptr{GetPointerFromVMA(vaddr)}; | ||||
|             u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; | ||||
|             system.GPU().InvalidateRegion(vaddr, sizeof(T)); | ||||
|             std::memcpy(host_ptr, &data, sizeof(T)); | ||||
|             break; | ||||
| @ -703,12 +693,7 @@ void Memory::SetCurrentPageTable(Kernel::Process& process) { | ||||
|     impl->SetCurrentPageTable(process); | ||||
| } | ||||
| 
 | ||||
| void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, | ||||
|                              Kernel::PhysicalMemory& memory, VAddr offset) { | ||||
|     impl->MapMemoryRegion(page_table, base, size, memory, offset); | ||||
| } | ||||
| 
 | ||||
| void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) { | ||||
| void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { | ||||
|     impl->MapMemoryRegion(page_table, base, size, target); | ||||
| } | ||||
| 
 | ||||
| @ -845,4 +830,4 @@ bool IsKernelVirtualAddress(const VAddr vaddr) { | ||||
|     return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; | ||||
| } | ||||
| 
 | ||||
| } // namespace Memory
 | ||||
| } // namespace Core::Memory
 | ||||
|  | ||||
| @ -23,7 +23,7 @@ class PhysicalMemory; | ||||
| class Process; | ||||
| } // namespace Kernel
 | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| 
 | ||||
| /**
 | ||||
|  * Page size used by the ARM architecture. This is the smallest granularity with which memory can | ||||
| @ -66,19 +66,6 @@ public: | ||||
|      */ | ||||
|     void SetCurrentPageTable(Kernel::Process& process); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Maps an physical buffer onto a region of the emulated process address space. | ||||
|      * | ||||
|      * @param page_table The page table of the emulated process. | ||||
|      * @param base       The address to start mapping at. Must be page-aligned. | ||||
|      * @param size       The amount of bytes to map. Must be page-aligned. | ||||
|      * @param memory     Physical buffer with the memory backing the mapping. Must be of length | ||||
|      *                   at least `size + offset`. | ||||
|      * @param offset     The offset within the physical memory. Must be page-aligned. | ||||
|      */ | ||||
|     void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, | ||||
|                          Kernel::PhysicalMemory& memory, VAddr offset); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Maps an allocated buffer onto a region of the emulated process address space. | ||||
|      * | ||||
| @ -88,7 +75,7 @@ public: | ||||
|      * @param target     Buffer with the memory backing the mapping. Must be of length at least | ||||
|      *                   `size`. | ||||
|      */ | ||||
|     void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target); | ||||
|     void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Maps a region of the emulated process address space as a IO region. | ||||
| @ -503,4 +490,4 @@ private: | ||||
| /// Determines if the given VAddr is a kernel address
 | ||||
| bool IsKernelVirtualAddress(VAddr vaddr); | ||||
| 
 | ||||
| } // namespace Memory
 | ||||
| } // namespace Core::Memory
 | ||||
|  | ||||
| @ -10,13 +10,15 @@ | ||||
| #include "core/core_timing.h" | ||||
| #include "core/core_timing_util.h" | ||||
| #include "core/hardware_properties.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/service/hid/controllers/npad.h" | ||||
| #include "core/hle/service/hid/hid.h" | ||||
| #include "core/hle/service/sm/sm.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/memory/cheat_engine.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| 
 | ||||
| constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 12); | ||||
| constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | ||||
| @ -194,11 +196,12 @@ void CheatEngine::Initialize() { | ||||
|     metadata.process_id = system.CurrentProcess()->GetProcessID(); | ||||
|     metadata.title_id = system.CurrentProcess()->GetTitleID(); | ||||
| 
 | ||||
|     const auto& vm_manager = system.CurrentProcess()->VMManager(); | ||||
|     metadata.heap_extents = {vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionSize()}; | ||||
|     metadata.address_space_extents = {vm_manager.GetAddressSpaceBaseAddress(), | ||||
|                                       vm_manager.GetAddressSpaceSize()}; | ||||
|     metadata.alias_extents = {vm_manager.GetMapRegionBaseAddress(), vm_manager.GetMapRegionSize()}; | ||||
|     const auto& page_table = system.CurrentProcess()->PageTable(); | ||||
|     metadata.heap_extents = {page_table.GetHeapRegionStart(), page_table.GetHeapRegionSize()}; | ||||
|     metadata.address_space_extents = {page_table.GetAddressSpaceStart(), | ||||
|                                       page_table.GetAddressSpaceSize()}; | ||||
|     metadata.alias_extents = {page_table.GetAliasCodeRegionStart(), | ||||
|                               page_table.GetAliasCodeRegionSize()}; | ||||
| 
 | ||||
|     is_pending_reload.exchange(true); | ||||
| } | ||||
| @ -230,4 +233,4 @@ void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { | ||||
|     core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); | ||||
| } | ||||
| 
 | ||||
| } // namespace Memory
 | ||||
| } // namespace Core::Memory
 | ||||
|  | ||||
| @ -20,7 +20,7 @@ class CoreTiming; | ||||
| struct EventType; | ||||
| } // namespace Core::Timing
 | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| 
 | ||||
| class StandardVmCallbacks : public DmntCheatVm::Callbacks { | ||||
| public: | ||||
| @ -84,4 +84,4 @@ private: | ||||
|     Core::System& system; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Memory
 | ||||
| } // namespace Core::Memory
 | ||||
|  | ||||
| @ -26,7 +26,7 @@ | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| 
 | ||||
| struct MemoryRegionExtents { | ||||
|     u64 base{}; | ||||
| @ -55,4 +55,4 @@ struct CheatEntry { | ||||
|     CheatDefinition definition{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Memory
 | ||||
| } // namespace Core::Memory
 | ||||
|  | ||||
| @ -27,7 +27,7 @@ | ||||
| #include "core/memory/dmnt_cheat_types.h" | ||||
| #include "core/memory/dmnt_cheat_vm.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| 
 | ||||
| DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {} | ||||
| 
 | ||||
| @ -1210,4 +1210,4 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } // namespace Memory
 | ||||
| } // namespace Core::Memory
 | ||||
|  | ||||
| @ -30,7 +30,7 @@ | ||||
| #include "common/common_types.h" | ||||
| #include "core/memory/dmnt_cheat_types.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| 
 | ||||
| enum class CheatVmOpcodeType : u32 { | ||||
|     StoreStatic = 0, | ||||
| @ -318,4 +318,4 @@ private: | ||||
|                                       MemoryAccessType mem_type, u64 rel_address); | ||||
| }; | ||||
| 
 | ||||
| }; // namespace Memory
 | ||||
| }; // namespace Core::Memory
 | ||||
|  | ||||
| @ -16,9 +16,11 @@ | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/kernel/hle_ipc.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/service/lm/manager.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/reporter.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| @ -108,14 +110,13 @@ json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 | ||||
| 
 | ||||
| json GetProcessorStateDataAuto(Core::System& system) { | ||||
|     const auto* process{system.CurrentProcess()}; | ||||
|     const auto& vm_manager{process->VMManager()}; | ||||
|     auto& arm{system.CurrentArmInterface()}; | ||||
| 
 | ||||
|     Core::ARM_Interface::ThreadContext64 context{}; | ||||
|     arm.SaveContext(context); | ||||
| 
 | ||||
|     return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32", | ||||
|                                  vm_manager.GetCodeRegionBaseAddress(), context.sp, context.pc, | ||||
|                                  process->PageTable().GetCodeRegionStart(), context.sp, context.pc, | ||||
|                                  context.pstate, context.cpu_registers); | ||||
| } | ||||
| 
 | ||||
| @ -147,7 +148,8 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s | ||||
| } | ||||
| 
 | ||||
| template <bool read_value, typename DescriptorType> | ||||
| json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, Memory::Memory& memory) { | ||||
| json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, | ||||
|                                 Core::Memory::Memory& memory) { | ||||
|     auto buffer_out = json::array(); | ||||
|     for (const auto& desc : buffer) { | ||||
|         auto entry = json{ | ||||
| @ -167,7 +169,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, Memor | ||||
|     return buffer_out; | ||||
| } | ||||
| 
 | ||||
| json GetHLERequestContextData(Kernel::HLERequestContext& ctx, Memory::Memory& memory) { | ||||
| json GetHLERequestContextData(Kernel::HLERequestContext& ctx, Core::Memory::Memory& memory) { | ||||
|     json out; | ||||
| 
 | ||||
|     auto cmd_buf = json::array(); | ||||
|  | ||||
| @ -16,7 +16,7 @@ namespace { | ||||
| 
 | ||||
| constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); | ||||
| 
 | ||||
| u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { | ||||
| u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { | ||||
|     switch (width) { | ||||
|     case 1: | ||||
|         return memory.Read8(addr); | ||||
| @ -32,7 +32,7 @@ u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) { | ||||
| void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) { | ||||
|     switch (width) { | ||||
|     case 1: | ||||
|         memory.Write8(addr, static_cast<u8>(value)); | ||||
| @ -53,7 +53,7 @@ void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) | ||||
| 
 | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_) | ||||
| Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) | ||||
|     : core_timing{core_timing_}, memory{memory_} { | ||||
|     event = Core::Timing::CreateEvent( | ||||
|         "MemoryFreezer::FrameCallback", | ||||
|  | ||||
| @ -16,7 +16,7 @@ class CoreTiming; | ||||
| struct EventType; | ||||
| } // namespace Core::Timing
 | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
| @ -38,7 +38,7 @@ public: | ||||
|         u64 value; | ||||
|     }; | ||||
| 
 | ||||
|     explicit Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_); | ||||
|     explicit Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_); | ||||
|     ~Freezer(); | ||||
| 
 | ||||
|     // Enables or disables the entire memory freezer.
 | ||||
| @ -82,7 +82,7 @@ private: | ||||
| 
 | ||||
|     std::shared_ptr<Core::Timing::EventType> event; | ||||
|     Core::Timing::CoreTiming& core_timing; | ||||
|     Memory::Memory& memory; | ||||
|     Core::Memory::Memory& memory; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Tools
 | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| 
 | ||||
| #include "common/page_table.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/memory.h" | ||||
| #include "tests/core/arm/arm_test_common.h" | ||||
| @ -18,12 +19,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) | ||||
|     auto& system = Core::System::GetInstance(); | ||||
| 
 | ||||
|     auto process = Kernel::Process::Create(system, "", Kernel::Process::ProcessType::Userland); | ||||
|     page_table = &process->VMManager().page_table; | ||||
| 
 | ||||
|     std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); | ||||
|     page_table->special_regions.clear(); | ||||
|     std::fill(page_table->attributes.begin(), page_table->attributes.end(), | ||||
|               Common::PageType::Unmapped); | ||||
|     page_table = &process->PageTable().PageTableImpl(); | ||||
| 
 | ||||
|     system.Memory().MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); | ||||
|     system.Memory().MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); | ||||
|  | ||||
| @ -6,8 +6,8 @@ | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/memory.h" | ||||
| #include "video_core/gpu.h" | ||||
| #include "video_core/memory_manager.h" | ||||
| @ -17,10 +17,7 @@ namespace Tegra { | ||||
| 
 | ||||
| MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer) | ||||
|     : rasterizer{rasterizer}, system{system} { | ||||
|     std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); | ||||
|     std::fill(page_table.attributes.begin(), page_table.attributes.end(), | ||||
|               Common::PageType::Unmapped); | ||||
|     page_table.Resize(address_space_width); | ||||
|     page_table.Resize(address_space_width, page_bits, false); | ||||
| 
 | ||||
|     // Initialize the map with a single free region covering the entire managed space.
 | ||||
|     VirtualMemoryArea initial_vma; | ||||
| @ -55,9 +52,9 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { | ||||
| 
 | ||||
|     MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); | ||||
|     ASSERT(system.CurrentProcess() | ||||
|                ->VMManager() | ||||
|                .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped, | ||||
|                                    Kernel::MemoryAttribute::DeviceMapped) | ||||
|                ->PageTable() | ||||
|                .SetMemoryAttribute(cpu_addr, size, Kernel::Memory::MemoryAttribute::DeviceShared, | ||||
|                                    Kernel::Memory::MemoryAttribute::DeviceShared) | ||||
|                .IsSuccess()); | ||||
| 
 | ||||
|     return gpu_addr; | ||||
| @ -70,9 +67,9 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) | ||||
| 
 | ||||
|     MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr); | ||||
|     ASSERT(system.CurrentProcess() | ||||
|                ->VMManager() | ||||
|                .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped, | ||||
|                                    Kernel::MemoryAttribute::DeviceMapped) | ||||
|                ->PageTable() | ||||
|                .SetMemoryAttribute(cpu_addr, size, Kernel::Memory::MemoryAttribute::DeviceShared, | ||||
|                                    Kernel::Memory::MemoryAttribute::DeviceShared) | ||||
|                .IsSuccess()); | ||||
|     return gpu_addr; | ||||
| } | ||||
| @ -89,9 +86,10 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { | ||||
| 
 | ||||
|     UnmapRange(gpu_addr, aligned_size); | ||||
|     ASSERT(system.CurrentProcess() | ||||
|                ->VMManager() | ||||
|                .SetMemoryAttribute(cpu_addr.value(), size, Kernel::MemoryAttribute::DeviceMapped, | ||||
|                                    Kernel::MemoryAttribute::None) | ||||
|                ->PageTable() | ||||
|                .SetMemoryAttribute(cpu_addr.value(), size, | ||||
|                                    Kernel::Memory::MemoryAttribute::DeviceShared, | ||||
|                                    Kernel::Memory::MemoryAttribute::None) | ||||
|                .IsSuccess()); | ||||
| 
 | ||||
|     return gpu_addr; | ||||
| @ -147,16 +145,8 @@ T MemoryManager::Read(GPUVAddr addr) const { | ||||
|         return value; | ||||
|     } | ||||
| 
 | ||||
|     switch (page_table.attributes[addr >> page_bits]) { | ||||
|     case Common::PageType::Unmapped: | ||||
|         LOG_ERROR(HW_GPU, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, addr); | ||||
|         return 0; | ||||
|     case Common::PageType::Memory: | ||||
|         ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", addr); | ||||
|         break; | ||||
|     default: | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
|     UNREACHABLE(); | ||||
| 
 | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| @ -173,17 +163,7 @@ void MemoryManager::Write(GPUVAddr addr, T data) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     switch (page_table.attributes[addr >> page_bits]) { | ||||
|     case Common::PageType::Unmapped: | ||||
|         LOG_ERROR(HW_GPU, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, | ||||
|                   static_cast<u32>(data), addr); | ||||
|         return; | ||||
|     case Common::PageType::Memory: | ||||
|         ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", addr); | ||||
|         break; | ||||
|     default: | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
|     UNREACHABLE(); | ||||
| } | ||||
| 
 | ||||
| template u8 MemoryManager::Read<u8>(GPUVAddr addr) const; | ||||
| @ -249,18 +229,11 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s | ||||
|         const std::size_t copy_amount{ | ||||
|             std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | ||||
| 
 | ||||
|         switch (page_table.attributes[page_index]) { | ||||
|         case Common::PageType::Memory: { | ||||
|             const VAddr src_addr{page_table.backing_addr[page_index] + page_offset}; | ||||
|             // Flush must happen on the rasterizer interface, such that memory is always synchronous
 | ||||
|             // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
 | ||||
|             rasterizer.FlushRegion(src_addr, copy_amount); | ||||
|             memory.ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
|         const VAddr src_addr{page_table.backing_addr[page_index] + page_offset}; | ||||
|         // Flush must happen on the rasterizer interface, such that memory is always synchronous
 | ||||
|         // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu.
 | ||||
|         rasterizer.FlushRegion(src_addr, copy_amount); | ||||
|         memory.ReadBlockUnsafe(src_addr, dest_buffer, copy_amount); | ||||
| 
 | ||||
|         page_index++; | ||||
|         page_offset = 0; | ||||
| @ -305,18 +278,11 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const | ||||
|         const std::size_t copy_amount{ | ||||
|             std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | ||||
| 
 | ||||
|         switch (page_table.attributes[page_index]) { | ||||
|         case Common::PageType::Memory: { | ||||
|             const VAddr dest_addr{page_table.backing_addr[page_index] + page_offset}; | ||||
|             // Invalidate must happen on the rasterizer interface, such that memory is always
 | ||||
|             // synchronous when it is written (even when in asynchronous GPU mode).
 | ||||
|             rasterizer.InvalidateRegion(dest_addr, copy_amount); | ||||
|             memory.WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
|         const VAddr dest_addr{page_table.backing_addr[page_index] + page_offset}; | ||||
|         // Invalidate must happen on the rasterizer interface, such that memory is always
 | ||||
|         // synchronous when it is written (even when in asynchronous GPU mode).
 | ||||
|         rasterizer.InvalidateRegion(dest_addr, copy_amount); | ||||
|         memory.WriteBlockUnsafe(dest_addr, src_buffer, copy_amount); | ||||
| 
 | ||||
|         page_index++; | ||||
|         page_offset = 0; | ||||
| @ -362,8 +328,8 @@ void MemoryManager::CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const | ||||
| 
 | ||||
| bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) { | ||||
|     const VAddr addr = page_table.backing_addr[gpu_addr >> page_bits]; | ||||
|     const std::size_t page = (addr & Memory::PAGE_MASK) + size; | ||||
|     return page <= Memory::PAGE_SIZE; | ||||
|     const std::size_t page = (addr & Core::Memory::PAGE_MASK) + size; | ||||
|     return page <= Core::Memory::PAGE_SIZE; | ||||
| } | ||||
| 
 | ||||
| void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, | ||||
| @ -375,12 +341,13 @@ void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageTy | ||||
|     ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", | ||||
|                base + page_table.pointers.size()); | ||||
| 
 | ||||
|     std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type); | ||||
| 
 | ||||
|     if (memory == nullptr) { | ||||
|         std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, memory); | ||||
|         std::fill(page_table.backing_addr.begin() + base, page_table.backing_addr.begin() + end, | ||||
|                   backing_addr); | ||||
|         while (base != end) { | ||||
|             page_table.pointers[base] = nullptr; | ||||
|             page_table.backing_addr[base] = 0; | ||||
| 
 | ||||
|             base += 1; | ||||
|         } | ||||
|     } else { | ||||
|         while (base != end) { | ||||
|             page_table.pointers[base] = memory; | ||||
|  | ||||
| @ -179,7 +179,7 @@ private: | ||||
|     /// End of address space, based on address space in bits.
 | ||||
|     static constexpr GPUVAddr address_space_end{1ULL << address_space_width}; | ||||
| 
 | ||||
|     Common::BackingPageTable page_table{page_bits}; | ||||
|     Common::PageTable page_table; | ||||
|     VMAMap vma_map; | ||||
|     VideoCore::RasterizerInterface& rasterizer; | ||||
| 
 | ||||
|  | ||||
| @ -23,15 +23,15 @@ constexpr auto RangeFromInterval(Map& map, const Interval& interval) { | ||||
| 
 | ||||
| } // Anonymous namespace
 | ||||
| 
 | ||||
| RasterizerAccelerated::RasterizerAccelerated(Memory::Memory& cpu_memory_) | ||||
| RasterizerAccelerated::RasterizerAccelerated(Core::Memory::Memory& cpu_memory_) | ||||
|     : cpu_memory{cpu_memory_} {} | ||||
| 
 | ||||
| RasterizerAccelerated::~RasterizerAccelerated() = default; | ||||
| 
 | ||||
| void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { | ||||
|     std::lock_guard lock{pages_mutex}; | ||||
|     const u64 page_start{addr >> Memory::PAGE_BITS}; | ||||
|     const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS}; | ||||
|     const u64 page_start{addr >> Core::Memory::PAGE_BITS}; | ||||
|     const u64 page_end{(addr + size + Core::Memory::PAGE_SIZE - 1) >> Core::Memory::PAGE_BITS}; | ||||
| 
 | ||||
|     // Interval maps will erase segments if count reaches 0, so if delta is negative we have to
 | ||||
|     // subtract after iterating
 | ||||
| @ -44,8 +44,8 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del | ||||
|         const auto interval = pair.first & pages_interval; | ||||
|         const int count = pair.second; | ||||
| 
 | ||||
|         const VAddr interval_start_addr = boost::icl::first(interval) << Memory::PAGE_BITS; | ||||
|         const VAddr interval_end_addr = boost::icl::last_next(interval) << Memory::PAGE_BITS; | ||||
|         const VAddr interval_start_addr = boost::icl::first(interval) << Core::Memory::PAGE_BITS; | ||||
|         const VAddr interval_end_addr = boost::icl::last_next(interval) << Core::Memory::PAGE_BITS; | ||||
|         const u64 interval_size = interval_end_addr - interval_start_addr; | ||||
| 
 | ||||
|         if (delta > 0 && count == delta) { | ||||
|  | ||||
| @ -11,7 +11,7 @@ | ||||
| #include "common/common_types.h" | ||||
| #include "video_core/rasterizer_interface.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| namespace Core::Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
| @ -20,7 +20,7 @@ namespace VideoCore { | ||||
| /// Implements the shared part in GPU accelerated rasterizers in RasterizerInterface.
 | ||||
| class RasterizerAccelerated : public RasterizerInterface { | ||||
| public: | ||||
|     explicit RasterizerAccelerated(Memory::Memory& cpu_memory_); | ||||
|     explicit RasterizerAccelerated(Core::Memory::Memory& cpu_memory_); | ||||
|     ~RasterizerAccelerated() override; | ||||
| 
 | ||||
|     void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override; | ||||
| @ -30,7 +30,7 @@ private: | ||||
|     CachedPageMap cached_pages; | ||||
|     std::mutex pages_mutex; | ||||
| 
 | ||||
|     Memory::Memory& cpu_memory; | ||||
|     Core::Memory::Memory& cpu_memory; | ||||
| }; | ||||
| 
 | ||||
| } // namespace VideoCore
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei