diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/init/init_slab_setup.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_class_token.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_code_memory.cpp | 146 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_code_memory.h | 66 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_memory_block.h | 20 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_page_linked_list.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_page_table.cpp | 119 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_page_table.h | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 214 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc_wrap.h | 27 |
12 files changed, 611 insertions, 6 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 721685bb7..506885659 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -179,6 +179,8 @@ add_library(core STATIC | |||
| 179 | hle/kernel/k_client_port.h | 179 | hle/kernel/k_client_port.h |
| 180 | hle/kernel/k_client_session.cpp | 180 | hle/kernel/k_client_session.cpp |
| 181 | hle/kernel/k_client_session.h | 181 | hle/kernel/k_client_session.h |
| 182 | hle/kernel/k_code_memory.cpp | ||
| 183 | hle/kernel/k_code_memory.h | ||
| 182 | hle/kernel/k_condition_variable.cpp | 184 | hle/kernel/k_condition_variable.cpp |
| 183 | hle/kernel/k_condition_variable.h | 185 | hle/kernel/k_condition_variable.h |
| 184 | hle/kernel/k_event.cpp | 186 | hle/kernel/k_event.cpp |
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index 8ff0f695d..36fc0944a 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hardware_properties.h" | 10 | #include "core/hardware_properties.h" |
| 11 | #include "core/hle/kernel/init/init_slab_setup.h" | 11 | #include "core/hle/kernel/init/init_slab_setup.h" |
| 12 | #include "core/hle/kernel/k_code_memory.h" | ||
| 12 | #include "core/hle/kernel/k_event.h" | 13 | #include "core/hle/kernel/k_event.h" |
| 13 | #include "core/hle/kernel/k_memory_layout.h" | 14 | #include "core/hle/kernel/k_memory_layout.h" |
| 14 | #include "core/hle/kernel/k_memory_manager.h" | 15 | #include "core/hle/kernel/k_memory_manager.h" |
| @@ -32,6 +33,7 @@ namespace Kernel::Init { | |||
| 32 | HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ | 33 | HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ |
| 33 | HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ | 34 | HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ |
| 34 | HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ | 35 | HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ |
| 36 | HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ | ||
| 35 | HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ | 37 | HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ |
| 36 | HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) | 38 | HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) |
| 37 | 39 | ||
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp index 0be0027be..21e2fe494 100644 --- a/src/core/hle/kernel/k_class_token.cpp +++ b/src/core/hle/kernel/k_class_token.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "core/hle/kernel/k_class_token.h" | 6 | #include "core/hle/kernel/k_class_token.h" |
| 7 | #include "core/hle/kernel/k_client_port.h" | 7 | #include "core/hle/kernel/k_client_port.h" |
| 8 | #include "core/hle/kernel/k_client_session.h" | 8 | #include "core/hle/kernel/k_client_session.h" |
| 9 | #include "core/hle/kernel/k_code_memory.h" | ||
| 9 | #include "core/hle/kernel/k_event.h" | 10 | #include "core/hle/kernel/k_event.h" |
| 10 | #include "core/hle/kernel/k_port.h" | 11 | #include "core/hle/kernel/k_port.h" |
| 11 | #include "core/hle/kernel/k_process.h" | 12 | #include "core/hle/kernel/k_process.h" |
| @@ -48,7 +49,7 @@ static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000); | |||
| 48 | static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000); | 49 | static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000); |
| 49 | // static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000); | 50 | // static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000); |
| 50 | // static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000); | 51 | // static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000); |
| 51 | // static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000); | 52 | static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000); |
| 52 | 53 | ||
| 53 | // Ensure that the token hierarchy is correct. | 54 | // Ensure that the token hierarchy is correct. |
| 54 | 55 | ||
| @@ -79,7 +80,7 @@ static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAut | |||
| 79 | static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>)); | 80 | static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>)); |
| 80 | // static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>)); | 81 | // static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>)); |
| 81 | // static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>)); | 82 | // static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>)); |
| 82 | // static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>)); | 83 | static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>)); |
| 83 | 84 | ||
| 84 | // Ensure that the token hierarchy reflects the class hierarchy. | 85 | // Ensure that the token hierarchy reflects the class hierarchy. |
| 85 | 86 | ||
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp new file mode 100644 index 000000000..d69f7ffb7 --- /dev/null +++ b/src/core/hle/kernel/k_code_memory.cpp | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "core/device_memory.h" | ||
| 7 | #include "core/hle/kernel/k_auto_object.h" | ||
| 8 | #include "core/hle/kernel/k_code_memory.h" | ||
| 9 | #include "core/hle/kernel/k_light_lock.h" | ||
| 10 | #include "core/hle/kernel/k_memory_block.h" | ||
| 11 | #include "core/hle/kernel/k_page_linked_list.h" | ||
| 12 | #include "core/hle/kernel/k_page_table.h" | ||
| 13 | #include "core/hle/kernel/k_process.h" | ||
| 14 | #include "core/hle/kernel/slab_helpers.h" | ||
| 15 | #include "core/hle/kernel/svc_types.h" | ||
| 16 | #include "core/hle/result.h" | ||
| 17 | |||
| 18 | namespace Kernel { | ||
| 19 | |||
| 20 | KCodeMemory::KCodeMemory(KernelCore& kernel_) | ||
| 21 | : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {} | ||
| 22 | |||
| 23 | ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) { | ||
| 24 | // Set members. | ||
| 25 | m_owner = kernel.CurrentProcess(); | ||
| 26 | |||
| 27 | // Get the owner page table. | ||
| 28 | auto& page_table = m_owner->PageTable(); | ||
| 29 | |||
| 30 | // Construct the page group. | ||
| 31 | KMemoryInfo kBlockInfo = page_table.QueryInfo(addr); | ||
| 32 | m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); | ||
| 33 | |||
| 34 | // Lock the memory. | ||
| 35 | R_TRY(page_table.LockForCodeMemory(addr, size)) | ||
| 36 | |||
| 37 | // Clear the memory. | ||
| 38 | for (const auto& block : m_page_group.Nodes()) { | ||
| 39 | std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); | ||
| 40 | } | ||
| 41 | |||
| 42 | // Set remaining tracking members. | ||
| 43 | m_address = addr; | ||
| 44 | m_is_initialized = true; | ||
| 45 | m_is_owner_mapped = false; | ||
| 46 | m_is_mapped = false; | ||
| 47 | |||
| 48 | // We succeeded. | ||
| 49 | return ResultSuccess; | ||
| 50 | } | ||
| 51 | |||
| 52 | void KCodeMemory::Finalize() { | ||
| 53 | // Unlock. | ||
| 54 | if (!m_is_mapped && !m_is_owner_mapped) { | ||
| 55 | const size_t size = m_page_group.GetNumPages() * PageSize; | ||
| 56 | m_owner->PageTable().UnlockForCodeMemory(m_address, size); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | ResultCode KCodeMemory::Map(VAddr address, size_t size) { | ||
| 61 | // Validate the size. | ||
| 62 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 63 | |||
| 64 | // Lock ourselves. | ||
| 65 | KScopedLightLock lk(m_lock); | ||
| 66 | |||
| 67 | // Ensure we're not already mapped. | ||
| 68 | R_UNLESS(!m_is_mapped, ResultInvalidState); | ||
| 69 | |||
| 70 | // Map the memory. | ||
| 71 | R_TRY(kernel.CurrentProcess()->PageTable().MapPages( | ||
| 72 | address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); | ||
| 73 | |||
| 74 | // Mark ourselves as mapped. | ||
| 75 | m_is_mapped = true; | ||
| 76 | |||
| 77 | return ResultSuccess; | ||
| 78 | } | ||
| 79 | |||
| 80 | ResultCode KCodeMemory::Unmap(VAddr address, size_t size) { | ||
| 81 | // Validate the size. | ||
| 82 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 83 | |||
| 84 | // Lock ourselves. | ||
| 85 | KScopedLightLock lk(m_lock); | ||
| 86 | |||
| 87 | // Unmap the memory. | ||
| 88 | R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group, | ||
| 89 | KMemoryState::CodeOut)); | ||
| 90 | |||
| 91 | // Mark ourselves as unmapped. | ||
| 92 | m_is_mapped = false; | ||
| 93 | |||
| 94 | return ResultSuccess; | ||
| 95 | } | ||
| 96 | |||
| 97 | ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) { | ||
| 98 | // Validate the size. | ||
| 99 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 100 | |||
| 101 | // Lock ourselves. | ||
| 102 | KScopedLightLock lk(m_lock); | ||
| 103 | |||
| 104 | // Ensure we're not already mapped. | ||
| 105 | R_UNLESS(!m_is_owner_mapped, ResultInvalidState); | ||
| 106 | |||
| 107 | // Convert the memory permission. | ||
| 108 | KMemoryPermission k_perm{}; | ||
| 109 | switch (perm) { | ||
| 110 | case Svc::MemoryPermission::Read: | ||
| 111 | k_perm = KMemoryPermission::UserRead; | ||
| 112 | break; | ||
| 113 | case Svc::MemoryPermission::ReadExecute: | ||
| 114 | k_perm = KMemoryPermission::UserReadExecute; | ||
| 115 | break; | ||
| 116 | default: | ||
| 117 | break; | ||
| 118 | } | ||
| 119 | |||
| 120 | // Map the memory. | ||
| 121 | R_TRY( | ||
| 122 | m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm)); | ||
| 123 | |||
| 124 | // Mark ourselves as mapped. | ||
| 125 | m_is_owner_mapped = true; | ||
| 126 | |||
| 127 | return ResultSuccess; | ||
| 128 | } | ||
| 129 | |||
| 130 | ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) { | ||
| 131 | // Validate the size. | ||
| 132 | R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 133 | |||
| 134 | // Lock ourselves. | ||
| 135 | KScopedLightLock lk(m_lock); | ||
| 136 | |||
| 137 | // Unmap the memory. | ||
| 138 | R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode)); | ||
| 139 | |||
| 140 | // Mark ourselves as unmapped. | ||
| 141 | m_is_owner_mapped = false; | ||
| 142 | |||
| 143 | return ResultSuccess; | ||
| 144 | } | ||
| 145 | |||
| 146 | } // namespace Kernel \ No newline at end of file | ||
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h new file mode 100644 index 000000000..e0ba19a53 --- /dev/null +++ b/src/core/hle/kernel/k_code_memory.h | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "core/device_memory.h" | ||
| 9 | #include "core/hle/kernel/k_auto_object.h" | ||
| 10 | #include "core/hle/kernel/k_light_lock.h" | ||
| 11 | #include "core/hle/kernel/k_page_linked_list.h" | ||
| 12 | #include "core/hle/kernel/k_process.h" | ||
| 13 | #include "core/hle/kernel/slab_helpers.h" | ||
| 14 | #include "core/hle/kernel/svc_types.h" | ||
| 15 | #include "core/hle/result.h" | ||
| 16 | |||
| 17 | namespace Kernel { | ||
| 18 | |||
| 19 | enum class CodeMemoryOperation : u32 { | ||
| 20 | Map = 0, | ||
| 21 | MapToOwner = 1, | ||
| 22 | Unmap = 2, | ||
| 23 | UnmapFromOwner = 3, | ||
| 24 | }; | ||
| 25 | |||
| 26 | class KCodeMemory final | ||
| 27 | : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> { | ||
| 28 | KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject); | ||
| 29 | |||
| 30 | public: | ||
| 31 | explicit KCodeMemory(KernelCore& kernel_); | ||
| 32 | |||
| 33 | ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size); | ||
| 34 | void Finalize(); | ||
| 35 | |||
| 36 | ResultCode Map(VAddr address, size_t size); | ||
| 37 | ResultCode Unmap(VAddr address, size_t size); | ||
| 38 | ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm); | ||
| 39 | ResultCode UnmapFromOwner(VAddr address, size_t size); | ||
| 40 | |||
| 41 | bool IsInitialized() const { | ||
| 42 | return m_is_initialized; | ||
| 43 | } | ||
| 44 | static void PostDestroy([[maybe_unused]] uintptr_t arg) {} | ||
| 45 | |||
| 46 | KProcess* GetOwner() const { | ||
| 47 | return m_owner; | ||
| 48 | } | ||
| 49 | VAddr GetSourceAddress() const { | ||
| 50 | return m_address; | ||
| 51 | } | ||
| 52 | size_t GetSize() const { | ||
| 53 | return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | private: | ||
| 57 | KPageLinkedList m_page_group{}; | ||
| 58 | KProcess* m_owner{}; | ||
| 59 | VAddr m_address{}; | ||
| 60 | KLightLock m_lock; | ||
| 61 | bool m_is_initialized{}; | ||
| 62 | bool m_is_owner_mapped{}; | ||
| 63 | bool m_is_mapped{}; | ||
| 64 | }; | ||
| 65 | |||
| 66 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index a7fdb5fb8..fd491146f 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h | |||
| @@ -131,6 +131,26 @@ enum class KMemoryPermission : u8 { | |||
| 131 | 131 | ||
| 132 | UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | | 132 | UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | |
| 133 | Svc::MemoryPermission::Execute), | 133 | Svc::MemoryPermission::Execute), |
| 134 | |||
| 135 | KernelShift = 3, | ||
| 136 | |||
| 137 | KernelRead = Read << KernelShift, | ||
| 138 | KernelWrite = Write << KernelShift, | ||
| 139 | KernelExecute = Execute << KernelShift, | ||
| 140 | |||
| 141 | NotMapped = (1 << (2 * KernelShift)), | ||
| 142 | |||
| 143 | KernelReadWrite = KernelRead | KernelWrite, | ||
| 144 | KernelReadExecute = KernelRead | KernelExecute, | ||
| 145 | |||
| 146 | UserRead = Read | KernelRead, | ||
| 147 | UserWrite = Write | KernelWrite, | ||
| 148 | UserExecute = Execute, | ||
| 149 | |||
| 150 | UserReadWrite = UserRead | UserWrite, | ||
| 151 | UserReadExecute = UserRead | UserExecute, | ||
| 152 | |||
| 153 | IpcLockChangeMask = NotMapped | UserReadWrite | ||
| 134 | }; | 154 | }; |
| 135 | DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); | 155 | DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); |
| 136 | 156 | ||
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h index 3362fb236..0e2ae582a 100644 --- a/src/core/hle/kernel/k_page_linked_list.h +++ b/src/core/hle/kernel/k_page_linked_list.h | |||
| @@ -27,6 +27,10 @@ public: | |||
| 27 | return num_pages; | 27 | return num_pages; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | constexpr std::size_t GetSize() const { | ||
| 31 | return GetNumPages() * PageSize; | ||
| 32 | } | ||
| 33 | |||
| 30 | private: | 34 | private: |
| 31 | u64 addr{}; | 35 | u64 addr{}; |
| 32 | std::size_t num_pages{}; | 36 | std::size_t num_pages{}; |
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 9bda5c5b2..99982e5a3 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -368,6 +368,33 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st | |||
| 368 | return ResultSuccess; | 368 | return ResultSuccess; |
| 369 | } | 369 | } |
| 370 | 370 | ||
| 371 | ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, | ||
| 372 | KPageTable& src_page_table, VAddr src_addr) { | ||
| 373 | std::lock_guard lock{page_table_lock}; | ||
| 374 | |||
| 375 | const std::size_t num_pages{size / PageSize}; | ||
| 376 | |||
| 377 | // Check that the memory is mapped in the destination process. | ||
| 378 | size_t num_allocator_blocks; | ||
| 379 | R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All, | ||
| 380 | KMemoryState::SharedCode, KMemoryPermission::UserReadWrite, | ||
| 381 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, | ||
| 382 | KMemoryAttribute::None)); | ||
| 383 | |||
| 384 | // Check that the memory is mapped in the source process. | ||
| 385 | R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess, | ||
| 386 | KMemoryState::FlagCanMapProcess, KMemoryPermission::None, | ||
| 387 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 388 | KMemoryAttribute::None)); | ||
| 389 | |||
| 390 | CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap)); | ||
| 391 | |||
| 392 | // Apply the memory block update. | ||
| 393 | block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, | ||
| 394 | KMemoryAttribute::None); | ||
| 395 | |||
| 396 | return ResultSuccess; | ||
| 397 | } | ||
| 371 | void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) { | 398 | void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) { |
| 372 | auto node{page_linked_list.Nodes().begin()}; | 399 | auto node{page_linked_list.Nodes().begin()}; |
| 373 | PAddr map_addr{node->GetAddress()}; | 400 | PAddr map_addr{node->GetAddress()}; |
| @@ -942,6 +969,60 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) | |||
| 942 | return ResultSuccess; | 969 | return ResultSuccess; |
| 943 | } | 970 | } |
| 944 | 971 | ||
| 972 | ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { | ||
| 973 | std::lock_guard lock{page_table_lock}; | ||
| 974 | |||
| 975 | KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; | ||
| 976 | |||
| 977 | KMemoryPermission old_perm{}; | ||
| 978 | |||
| 979 | if (const ResultCode result{CheckMemoryState( | ||
| 980 | nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, | ||
| 981 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask, | ||
| 982 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)}; | ||
| 983 | result.IsError()) { | ||
| 984 | return result; | ||
| 985 | } | ||
| 986 | |||
| 987 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 988 | |||
| 989 | block_manager->UpdateLock( | ||
| 990 | addr, size / PageSize, | ||
| 991 | [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { | ||
| 992 | block->ShareToDevice(permission); | ||
| 993 | }, | ||
| 994 | new_perm); | ||
| 995 | |||
| 996 | return ResultSuccess; | ||
| 997 | } | ||
| 998 | |||
| 999 | ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { | ||
| 1000 | std::lock_guard lock{page_table_lock}; | ||
| 1001 | |||
| 1002 | KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; | ||
| 1003 | |||
| 1004 | KMemoryPermission old_perm{}; | ||
| 1005 | |||
| 1006 | if (const ResultCode result{CheckMemoryState( | ||
| 1007 | nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, | ||
| 1008 | KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, | ||
| 1009 | KMemoryAttribute::All, KMemoryAttribute::Locked)}; | ||
| 1010 | result.IsError()) { | ||
| 1011 | return result; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm; | ||
| 1015 | |||
| 1016 | block_manager->UpdateLock( | ||
| 1017 | addr, size / PageSize, | ||
| 1018 | [](KMemoryBlockManager::iterator block, KMemoryPermission permission) { | ||
| 1019 | block->UnshareToDevice(permission); | ||
| 1020 | }, | ||
| 1021 | new_perm); | ||
| 1022 | |||
| 1023 | return ResultSuccess; | ||
| 1024 | } | ||
| 1025 | |||
| 945 | ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { | 1026 | ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { |
| 946 | block_manager = std::make_unique<KMemoryBlockManager>(start, end); | 1027 | block_manager = std::make_unique<KMemoryBlockManager>(start, end); |
| 947 | 1028 | ||
| @@ -1231,4 +1312,42 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi | |||
| 1231 | return ResultSuccess; | 1312 | return ResultSuccess; |
| 1232 | } | 1313 | } |
| 1233 | 1314 | ||
| 1315 | ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size, | ||
| 1316 | KMemoryState state_mask, KMemoryState state, | ||
| 1317 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 1318 | KMemoryAttribute attr_mask, KMemoryAttribute attr) const { | ||
| 1319 | // Get information about the first block. | ||
| 1320 | const VAddr last_addr = addr + size - 1; | ||
| 1321 | KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)}; | ||
| 1322 | KMemoryInfo info = it->GetMemoryInfo(); | ||
| 1323 | |||
| 1324 | // If the start address isn't aligned, we need a block. | ||
| 1325 | const size_t blocks_for_start_align = | ||
| 1326 | (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0; | ||
| 1327 | |||
| 1328 | while (true) { | ||
| 1329 | // Validate against the provided masks. | ||
| 1330 | R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); | ||
| 1331 | |||
| 1332 | // Break once we're done. | ||
| 1333 | if (last_addr <= info.GetLastAddress()) { | ||
| 1334 | break; | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | // Advance our iterator. | ||
| 1338 | it++; | ||
| 1339 | info = it->GetMemoryInfo(); | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | // If the end address isn't aligned, we need a block. | ||
| 1343 | const size_t blocks_for_end_align = | ||
| 1344 | (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0; | ||
| 1345 | |||
| 1346 | if (out_blocks_needed != nullptr) { | ||
| 1347 | *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | return ResultSuccess; | ||
| 1351 | } | ||
| 1352 | |||
| 1234 | } // namespace Kernel | 1353 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index b7ec38f06..d784aa67e 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -33,6 +33,8 @@ public: | |||
| 33 | KMemoryPermission perm); | 33 | KMemoryPermission perm); |
| 34 | ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 34 | ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); |
| 35 | ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); | 35 | ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); |
| 36 | ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, | ||
| 37 | VAddr src_addr); | ||
| 36 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); | 38 | ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); |
| 37 | ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); | 39 | ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); |
| 38 | ResultCode UnmapMemory(VAddr addr, std::size_t size); | 40 | ResultCode UnmapMemory(VAddr addr, std::size_t size); |
| @@ -55,6 +57,8 @@ public: | |||
| 55 | KMemoryPermission perm, PAddr map_addr = 0); | 57 | KMemoryPermission perm, PAddr map_addr = 0); |
| 56 | ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); | 58 | ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); |
| 57 | ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); | 59 | ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); |
| 60 | ResultCode LockForCodeMemory(VAddr addr, std::size_t size); | ||
| 61 | ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); | ||
| 58 | 62 | ||
| 59 | Common::PageTable& PageTableImpl() { | 63 | Common::PageTable& PageTableImpl() { |
| 60 | return page_table_impl; | 64 | return page_table_impl; |
| @@ -115,6 +119,10 @@ private: | |||
| 115 | return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, | 119 | return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, |
| 116 | perm, attr_mask, attr, ignore_attr); | 120 | perm, attr_mask, attr, ignore_attr); |
| 117 | } | 121 | } |
| 122 | ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size, | ||
| 123 | KMemoryState state_mask, KMemoryState state, | ||
| 124 | KMemoryPermission perm_mask, KMemoryPermission perm, | ||
| 125 | KMemoryAttribute attr_mask, KMemoryAttribute attr) const; | ||
| 118 | 126 | ||
| 119 | std::recursive_mutex page_table_lock; | 127 | std::recursive_mutex page_table_lock; |
| 120 | std::unique_ptr<KMemoryBlockManager> block_manager; | 128 | std::unique_ptr<KMemoryBlockManager> block_manager; |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index d2ceae950..d847fd0c5 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -53,6 +53,7 @@ class KSharedMemoryInfo; | |||
| 53 | class KThread; | 53 | class KThread; |
| 54 | class KTransferMemory; | 54 | class KTransferMemory; |
| 55 | class KWritableEvent; | 55 | class KWritableEvent; |
| 56 | class KCodeMemory; | ||
| 56 | class PhysicalCore; | 57 | class PhysicalCore; |
| 57 | class ServiceThread; | 58 | class ServiceThread; |
| 58 | class Synchronization; | 59 | class Synchronization; |
| @@ -326,6 +327,8 @@ public: | |||
| 326 | return slab_heap_container->transfer_memory; | 327 | return slab_heap_container->transfer_memory; |
| 327 | } else if constexpr (std::is_same_v<T, KWritableEvent>) { | 328 | } else if constexpr (std::is_same_v<T, KWritableEvent>) { |
| 328 | return slab_heap_container->writeable_event; | 329 | return slab_heap_container->writeable_event; |
| 330 | } else if constexpr (std::is_same_v<T, KCodeMemory>) { | ||
| 331 | return slab_heap_container->code_memory; | ||
| 329 | } | 332 | } |
| 330 | } | 333 | } |
| 331 | 334 | ||
| @@ -377,6 +380,7 @@ private: | |||
| 377 | KSlabHeap<KThread> thread; | 380 | KSlabHeap<KThread> thread; |
| 378 | KSlabHeap<KTransferMemory> transfer_memory; | 381 | KSlabHeap<KTransferMemory> transfer_memory; |
| 379 | KSlabHeap<KWritableEvent> writeable_event; | 382 | KSlabHeap<KWritableEvent> writeable_event; |
| 383 | KSlabHeap<KCodeMemory> code_memory; | ||
| 380 | }; | 384 | }; |
| 381 | 385 | ||
| 382 | std::unique_ptr<SlabHeapContainer> slab_heap_container; | 386 | std::unique_ptr<SlabHeapContainer> slab_heap_container; |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f0cd8471e..b37db918e 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "core/core_timing.h" | 18 | #include "core/core_timing.h" |
| 19 | #include "core/hle/kernel/k_client_port.h" | 19 | #include "core/hle/kernel/k_client_port.h" |
| 20 | #include "core/hle/kernel/k_client_session.h" | 20 | #include "core/hle/kernel/k_client_session.h" |
| 21 | #include "core/hle/kernel/k_code_memory.h" | ||
| 21 | #include "core/hle/kernel/k_event.h" | 22 | #include "core/hle/kernel/k_event.h" |
| 22 | #include "core/hle/kernel/k_handle_table.h" | 23 | #include "core/hle/kernel/k_handle_table.h" |
| 23 | #include "core/hle/kernel/k_memory_block.h" | 24 | #include "core/hle/kernel/k_memory_block.h" |
| @@ -1197,6 +1198,22 @@ constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) { | |||
| 1197 | } | 1198 | } |
| 1198 | } | 1199 | } |
| 1199 | 1200 | ||
| 1201 | constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1202 | return perm == Svc::MemoryPermission::ReadWrite; | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1206 | return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute; | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1210 | return perm == Svc::MemoryPermission::None; | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { | ||
| 1214 | return perm == Svc::MemoryPermission::None; | ||
| 1215 | } | ||
| 1216 | |||
| 1200 | } // Anonymous namespace | 1217 | } // Anonymous namespace |
| 1201 | 1218 | ||
| 1202 | static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, | 1219 | static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, |
| @@ -1306,6 +1323,195 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces | |||
| 1306 | return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm)); | 1323 | return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm)); |
| 1307 | } | 1324 | } |
| 1308 | 1325 | ||
| 1326 | static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 1327 | VAddr src_address, u64 size) { | ||
| 1328 | LOG_TRACE(Kernel_SVC, | ||
| 1329 | "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", | ||
| 1330 | dst_address, process_handle, src_address, size); | ||
| 1331 | |||
| 1332 | // Validate the address/size. | ||
| 1333 | R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); | ||
| 1334 | R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); | ||
| 1335 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1336 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1337 | R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); | ||
| 1338 | R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); | ||
| 1339 | |||
| 1340 | // Get the processes. | ||
| 1341 | KProcess* dst_process = system.CurrentProcess(); | ||
| 1342 | KScopedAutoObject src_process = | ||
| 1343 | dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); | ||
| 1344 | R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); | ||
| 1345 | |||
| 1346 | // Get the page tables. | ||
| 1347 | auto& dst_pt = dst_process->PageTable(); | ||
| 1348 | auto& src_pt = src_process->PageTable(); | ||
| 1349 | |||
| 1350 | // Validate that the mapping is in range. | ||
| 1351 | R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 1352 | R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), | ||
| 1353 | ResultInvalidMemoryRegion); | ||
| 1354 | |||
| 1355 | // Create a new page group. | ||
| 1356 | KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); | ||
| 1357 | KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); | ||
| 1358 | |||
| 1359 | // Map the group. | ||
| 1360 | R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, | ||
| 1361 | KMemoryPermission::UserReadWrite)); | ||
| 1362 | |||
| 1363 | return ResultSuccess; | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, | ||
| 1367 | VAddr src_address, u64 size) { | ||
| 1368 | LOG_TRACE(Kernel_SVC, | ||
| 1369 | "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", | ||
| 1370 | dst_address, process_handle, src_address, size); | ||
| 1371 | |||
| 1372 | // Validate the address/size. | ||
| 1373 | R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); | ||
| 1374 | R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); | ||
| 1375 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1376 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1377 | R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); | ||
| 1378 | R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); | ||
| 1379 | |||
| 1380 | // Get the processes. | ||
| 1381 | KProcess* dst_process = system.CurrentProcess(); | ||
| 1382 | KScopedAutoObject src_process = | ||
| 1383 | dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); | ||
| 1384 | R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); | ||
| 1385 | |||
| 1386 | // Get the page tables. | ||
| 1387 | auto& dst_pt = dst_process->PageTable(); | ||
| 1388 | auto& src_pt = src_process->PageTable(); | ||
| 1389 | |||
| 1390 | // Validate that the mapping is in range. | ||
| 1391 | R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); | ||
| 1392 | R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), | ||
| 1393 | ResultInvalidMemoryRegion); | ||
| 1394 | |||
| 1395 | // Unmap the memory. | ||
| 1396 | R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address)); | ||
| 1397 | |||
| 1398 | return ResultSuccess; | ||
| 1399 | } | ||
| 1400 | |||
| 1401 | static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { | ||
| 1402 | LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}", | ||
| 1403 | static_cast<void*>(out), address, size); | ||
| 1404 | // Get kernel instance. | ||
| 1405 | auto& kernel = system.Kernel(); | ||
| 1406 | |||
| 1407 | // Validate address / size. | ||
| 1408 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1409 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1410 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1411 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1412 | |||
| 1413 | // Create the code memory. | ||
| 1414 | |||
| 1415 | KCodeMemory* code_mem = KCodeMemory::Create(kernel); | ||
| 1416 | R_UNLESS(code_mem != nullptr, ResultOutOfResource); | ||
| 1417 | |||
| 1418 | // Verify that the region is in range. | ||
| 1419 | R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size), | ||
| 1420 | ResultInvalidCurrentMemory); | ||
| 1421 | |||
| 1422 | // Initialize the code memory. | ||
| 1423 | R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size)); | ||
| 1424 | |||
| 1425 | // Register the code memory. | ||
| 1426 | KCodeMemory::Register(kernel, code_mem); | ||
| 1427 | |||
| 1428 | // Add the code memory to the handle table. | ||
| 1429 | R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem)); | ||
| 1430 | |||
| 1431 | code_mem->Close(); | ||
| 1432 | |||
| 1433 | return ResultSuccess; | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, | ||
| 1437 | VAddr address, size_t size, Svc::MemoryPermission perm) { | ||
| 1438 | |||
| 1439 | LOG_TRACE(Kernel_SVC, | ||
| 1440 | "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " | ||
| 1441 | "permission=0x{:X}", | ||
| 1442 | code_memory_handle, operation, address, size, perm); | ||
| 1443 | |||
| 1444 | // Validate the address / size. | ||
| 1445 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); | ||
| 1446 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 1447 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 1448 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 1449 | |||
| 1450 | // Get the code memory from its handle. | ||
| 1451 | KScopedAutoObject code_mem = | ||
| 1452 | system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle); | ||
| 1453 | R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle); | ||
| 1454 | |||
| 1455 | // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. | ||
| 1456 | // This enables homebrew usage of these SVCs for JIT. | ||
| 1457 | |||
| 1458 | // Perform the operation. | ||
| 1459 | switch (static_cast<CodeMemoryOperation>(operation)) { | ||
| 1460 | case CodeMemoryOperation::Map: { | ||
| 1461 | // Check that the region is in range. | ||
| 1462 | R_UNLESS( | ||
| 1463 | system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), | ||
| 1464 | ResultInvalidMemoryRegion); | ||
| 1465 | |||
| 1466 | // Check the memory permission. | ||
| 1467 | R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1468 | |||
| 1469 | // Map the memory. | ||
| 1470 | R_TRY(code_mem->Map(address, size)); | ||
| 1471 | } break; | ||
| 1472 | case CodeMemoryOperation::Unmap: { | ||
| 1473 | // Check that the region is in range. | ||
| 1474 | R_UNLESS( | ||
| 1475 | system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), | ||
| 1476 | ResultInvalidMemoryRegion); | ||
| 1477 | |||
| 1478 | // Check the memory permission. | ||
| 1479 | R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1480 | |||
| 1481 | // Unmap the memory. | ||
| 1482 | R_TRY(code_mem->Unmap(address, size)); | ||
| 1483 | } break; | ||
| 1484 | case CodeMemoryOperation::MapToOwner: { | ||
| 1485 | // Check that the region is in range. | ||
| 1486 | R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, | ||
| 1487 | KMemoryState::GeneratedCode), | ||
| 1488 | ResultInvalidMemoryRegion); | ||
| 1489 | |||
| 1490 | // Check the memory permission. | ||
| 1491 | R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1492 | |||
| 1493 | // Map the memory to its owner. | ||
| 1494 | R_TRY(code_mem->MapToOwner(address, size, perm)); | ||
| 1495 | } break; | ||
| 1496 | case CodeMemoryOperation::UnmapFromOwner: { | ||
| 1497 | // Check that the region is in range. | ||
| 1498 | R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, | ||
| 1499 | KMemoryState::GeneratedCode), | ||
| 1500 | ResultInvalidMemoryRegion); | ||
| 1501 | |||
| 1502 | // Check the memory permission. | ||
| 1503 | R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); | ||
| 1504 | |||
| 1505 | // Unmap the memory from its owner. | ||
| 1506 | R_TRY(code_mem->UnmapFromOwner(address, size)); | ||
| 1507 | } break; | ||
| 1508 | default: | ||
| 1509 | return ResultInvalidEnumValue; | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | return ResultSuccess; | ||
| 1513 | } | ||
| 1514 | |||
| 1309 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, | 1515 | static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, |
| 1310 | VAddr page_info_address, Handle process_handle, | 1516 | VAddr page_info_address, Handle process_handle, |
| 1311 | VAddr address) { | 1517 | VAddr address) { |
| @@ -2600,8 +2806,8 @@ static const FunctionDef SVC_Table_64[] = { | |||
| 2600 | {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, | 2806 | {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, |
| 2601 | {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, | 2807 | {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, |
| 2602 | {0x4A, nullptr, "SetUnsafeLimit"}, | 2808 | {0x4A, nullptr, "SetUnsafeLimit"}, |
| 2603 | {0x4B, nullptr, "CreateCodeMemory"}, | 2809 | {0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"}, |
| 2604 | {0x4C, nullptr, "ControlCodeMemory"}, | 2810 | {0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"}, |
| 2605 | {0x4D, nullptr, "SleepSystem"}, | 2811 | {0x4D, nullptr, "SleepSystem"}, |
| 2606 | {0x4E, nullptr, "ReadWriteRegister"}, | 2812 | {0x4E, nullptr, "ReadWriteRegister"}, |
| 2607 | {0x4F, nullptr, "SetProcessActivity"}, | 2813 | {0x4F, nullptr, "SetProcessActivity"}, |
| @@ -2641,8 +2847,8 @@ static const FunctionDef SVC_Table_64[] = { | |||
| 2641 | {0x71, nullptr, "ManageNamedPort"}, | 2847 | {0x71, nullptr, "ManageNamedPort"}, |
| 2642 | {0x72, nullptr, "ConnectToPort"}, | 2848 | {0x72, nullptr, "ConnectToPort"}, |
| 2643 | {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"}, | 2849 | {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"}, |
| 2644 | {0x74, nullptr, "MapProcessMemory"}, | 2850 | {0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"}, |
| 2645 | {0x75, nullptr, "UnmapProcessMemory"}, | 2851 | {0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"}, |
| 2646 | {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, | 2852 | {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, |
| 2647 | {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, | 2853 | {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, |
| 2648 | {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, | 2854 | {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 6e62e656f..86255fe6d 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -73,6 +73,23 @@ void SvcWrap64(Core::System& system) { | |||
| 73 | .raw); | 73 | .raw); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | // Used by MapProcessMemory and UnmapProcessMemory | ||
| 77 | template <ResultCode func(Core::System&, u64, u32, u64, u64)> | ||
| 78 | void SvcWrap64(Core::System& system) { | ||
| 79 | FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), | ||
| 80 | Param(system, 2), Param(system, 3)) | ||
| 81 | .raw); | ||
| 82 | } | ||
| 83 | |||
| 84 | // Used by ControlCodeMemory | ||
| 85 | template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> | ||
| 86 | void SvcWrap64(Core::System& system) { | ||
| 87 | FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), | ||
| 88 | static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3), | ||
| 89 | static_cast<Svc::MemoryPermission>(Param(system, 4))) | ||
| 90 | .raw); | ||
| 91 | } | ||
| 92 | |||
| 76 | template <ResultCode func(Core::System&, u32*)> | 93 | template <ResultCode func(Core::System&, u32*)> |
| 77 | void SvcWrap64(Core::System& system) { | 94 | void SvcWrap64(Core::System& system) { |
| 78 | u32 param = 0; | 95 | u32 param = 0; |
| @@ -301,6 +318,16 @@ void SvcWrap64(Core::System& system) { | |||
| 301 | FuncReturn(system, retval); | 318 | FuncReturn(system, retval); |
| 302 | } | 319 | } |
| 303 | 320 | ||
| 321 | // Used by CreateCodeMemory | ||
| 322 | template <ResultCode func(Core::System&, Handle*, u64, u64)> | ||
| 323 | void SvcWrap64(Core::System& system) { | ||
| 324 | u32 param_1 = 0; | ||
| 325 | const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2)).raw; | ||
| 326 | |||
| 327 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 328 | FuncReturn(system, retval); | ||
| 329 | } | ||
| 330 | |||
| 304 | template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> | 331 | template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> |
| 305 | void SvcWrap64(Core::System& system) { | 332 | void SvcWrap64(Core::System& system) { |
| 306 | u32 param_1 = 0; | 333 | u32 param_1 = 0; |