summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2021-12-09 00:29:09 -0800
committerGravatar GitHub2021-12-09 00:29:09 -0800
commit46366c6dcaa47fe7bccb021c90b77d0d8be23b6f (patch)
tree3267f43c67c15e88524cb9d6106726eb8e89ea9d
parentMerge pull request #7545 from Morph1984/qt-deprecated-warn (diff)
parentUpdate k_code_memory.h (diff)
downloadyuzu-46366c6dcaa47fe7bccb021c90b77d0d8be23b6f.tar.gz
yuzu-46366c6dcaa47fe7bccb021c90b77d0d8be23b6f.tar.xz
yuzu-46366c6dcaa47fe7bccb021c90b77d0d8be23b6f.zip
Merge pull request #7519 from itsmeft24/master
kernel: svc: Implement ProcessMemory and CodeMemory SVCs
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp2
-rw-r--r--src/core/hle/kernel/k_class_token.cpp5
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp146
-rw-r--r--src/core/hle/kernel/k_code_memory.h66
-rw-r--r--src/core/hle/kernel/k_memory_block.h20
-rw-r--r--src/core/hle/kernel/k_page_linked_list.h4
-rw-r--r--src/core/hle/kernel/k_page_table.cpp119
-rw-r--r--src/core/hle/kernel/k_page_table.h8
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/svc.cpp214
-rw-r--r--src/core/hle/kernel/svc_wrap.h27
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);
48static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000); 49static_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); 52static_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
79static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>)); 80static_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>)); 83static_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
18namespace Kernel {
19
20KCodeMemory::KCodeMemory(KernelCore& kernel_)
21 : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
22
23ResultCode 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
52void 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
60ResultCode 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
80ResultCode 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
97ResultCode 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
130ResultCode 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
17namespace Kernel {
18
19enum class CodeMemoryOperation : u32 {
20 Map = 0,
21 MapToOwner = 1,
22 Unmap = 2,
23 UnmapFromOwner = 3,
24};
25
26class KCodeMemory final
27 : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
28 KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
29
30public:
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
56private:
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};
135DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission); 155DECLARE_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
371ResultCode 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}
371void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) { 398void 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
972ResultCode 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
999ResultCode 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
945ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1026ResultCode 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
1315ResultCode 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;
53class KThread; 53class KThread;
54class KTransferMemory; 54class KTransferMemory;
55class KWritableEvent; 55class KWritableEvent;
56class KCodeMemory;
56class PhysicalCore; 57class PhysicalCore;
57class ServiceThread; 58class ServiceThread;
58class Synchronization; 59class 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
1201constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) {
1202 return perm == Svc::MemoryPermission::ReadWrite;
1203}
1204
1205constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
1206 return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute;
1207}
1208
1209constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) {
1210 return perm == Svc::MemoryPermission::None;
1211}
1212
1213constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
1214 return perm == Svc::MemoryPermission::None;
1215}
1216
1200} // Anonymous namespace 1217} // Anonymous namespace
1201 1218
1202static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, 1219static 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
1326static 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
1366static 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
1401static 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
1436static 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
1309static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, 1515static 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
77template <ResultCode func(Core::System&, u64, u32, u64, u64)>
78void 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
85template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
86void 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
76template <ResultCode func(Core::System&, u32*)> 93template <ResultCode func(Core::System&, u32*)>
77void SvcWrap64(Core::System& system) { 94void 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
322template <ResultCode func(Core::System&, Handle*, u64, u64)>
323void SvcWrap64(Core::System& system) {
324 u32 param_1 = 0;
325 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
326
327 system.CurrentArmInterface().SetReg(1, param_1);
328 FuncReturn(system, retval);
329}
330
304template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> 331template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
305void SvcWrap64(Core::System& system) { 332void SvcWrap64(Core::System& system) {
306 u32 param_1 = 0; 333 u32 param_1 = 0;