summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/kernel/k_memory_block.h13
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.cpp70
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.h6
-rw-r--r--src/core/hle/kernel/k_page_table.cpp54
-rw-r--r--src/core/hle/kernel/k_page_table.h3
-rw-r--r--src/core/hle/kernel/svc/svc_memory.cpp8
6 files changed, 128 insertions, 26 deletions
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index 8437cb659..ef3f61321 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -192,7 +192,7 @@ enum class KMemoryAttribute : u8 {
192 Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached), 192 Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
193 PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked), 193 PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked),
194 194
195 SetMask = Uncached, 195 SetMask = Uncached | PermissionLocked,
196}; 196};
197DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute); 197DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
198 198
@@ -339,6 +339,10 @@ public:
339 return this->GetEndAddress() - 1; 339 return this->GetEndAddress() - 1;
340 } 340 }
341 341
342 constexpr KMemoryState GetState() const {
343 return m_memory_state;
344 }
345
342 constexpr u16 GetIpcLockCount() const { 346 constexpr u16 GetIpcLockCount() const {
343 return m_ipc_lock_count; 347 return m_ipc_lock_count;
344 } 348 }
@@ -456,6 +460,13 @@ public:
456 } 460 }
457 } 461 }
458 462
463 constexpr void UpdateAttribute(KMemoryAttribute mask, KMemoryAttribute attr) {
464 ASSERT(False(mask & KMemoryAttribute::IpcLocked));
465 ASSERT(False(mask & KMemoryAttribute::DeviceShared));
466
467 m_attribute = (m_attribute & ~mask) | attr;
468 }
469
459 constexpr void Split(KMemoryBlock* block, KProcessAddress addr) { 470 constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
460 ASSERT(this->GetAddress() < addr); 471 ASSERT(this->GetAddress() < addr);
461 ASSERT(this->Contains(addr)); 472 ASSERT(this->Contains(addr));
diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp
index ab75f550e..58a1e7216 100644
--- a/src/core/hle/kernel/k_memory_block_manager.cpp
+++ b/src/core/hle/kernel/k_memory_block_manager.cpp
@@ -160,8 +160,8 @@ void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator,
160 } 160 }
161 161
162 // Update block state. 162 // Update block state.
163 it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr), 163 it->Update(state, perm, attr, it->GetAddress() == address,
164 static_cast<u8>(clear_disable_attr)); 164 static_cast<u8>(set_disable_attr), static_cast<u8>(clear_disable_attr));
165 cur_address += cur_info.GetSize(); 165 cur_address += cur_info.GetSize();
166 remaining_pages -= cur_info.GetNumPages(); 166 remaining_pages -= cur_info.GetNumPages();
167 } 167 }
@@ -175,7 +175,9 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
175 KProcessAddress address, size_t num_pages, 175 KProcessAddress address, size_t num_pages,
176 KMemoryState test_state, KMemoryPermission test_perm, 176 KMemoryState test_state, KMemoryPermission test_perm,
177 KMemoryAttribute test_attr, KMemoryState state, 177 KMemoryAttribute test_attr, KMemoryState state,
178 KMemoryPermission perm, KMemoryAttribute attr) { 178 KMemoryPermission perm, KMemoryAttribute attr,
179 KMemoryBlockDisableMergeAttribute set_disable_attr,
180 KMemoryBlockDisableMergeAttribute clear_disable_attr) {
179 // Ensure for auditing that we never end up with an invalid tree. 181 // Ensure for auditing that we never end up with an invalid tree.
180 KScopedMemoryBlockManagerAuditor auditor(this); 182 KScopedMemoryBlockManagerAuditor auditor(this);
181 ASSERT(Common::IsAligned(GetInteger(address), PageSize)); 183 ASSERT(Common::IsAligned(GetInteger(address), PageSize));
@@ -214,7 +216,8 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
214 } 216 }
215 217
216 // Update block state. 218 // Update block state.
217 it->Update(state, perm, attr, false, 0, 0); 219 it->Update(state, perm, attr, false, static_cast<u8>(set_disable_attr),
220 static_cast<u8>(clear_disable_attr));
218 cur_address += cur_info.GetSize(); 221 cur_address += cur_info.GetSize();
219 remaining_pages -= cur_info.GetNumPages(); 222 remaining_pages -= cur_info.GetNumPages();
220 } else { 223 } else {
@@ -284,6 +287,65 @@ void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocat
284 this->CoalesceForUpdate(allocator, address, num_pages); 287 this->CoalesceForUpdate(allocator, address, num_pages);
285} 288}
286 289
290void KMemoryBlockManager::UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator,
291 KProcessAddress address, size_t num_pages,
292 KMemoryAttribute mask, KMemoryAttribute attr) {
293 // Ensure for auditing that we never end up with an invalid tree.
294 KScopedMemoryBlockManagerAuditor auditor(this);
295 ASSERT(Common::IsAligned(GetInteger(address), PageSize));
296
297 KProcessAddress cur_address = address;
298 size_t remaining_pages = num_pages;
299 iterator it = this->FindIterator(address);
300
301 while (remaining_pages > 0) {
302 const size_t remaining_size = remaining_pages * PageSize;
303 KMemoryInfo cur_info = it->GetMemoryInfo();
304
305 if ((it->GetAttribute() & mask) != attr) {
306 // If we need to, create a new block before and insert it.
307 if (cur_info.GetAddress() != GetInteger(cur_address)) {
308 KMemoryBlock* new_block = allocator->Allocate();
309
310 it->Split(new_block, cur_address);
311 it = m_memory_block_tree.insert(*new_block);
312 it++;
313
314 cur_info = it->GetMemoryInfo();
315 cur_address = cur_info.GetAddress();
316 }
317
318 // If we need to, create a new block after and insert it.
319 if (cur_info.GetSize() > remaining_size) {
320 KMemoryBlock* new_block = allocator->Allocate();
321
322 it->Split(new_block, cur_address + remaining_size);
323 it = m_memory_block_tree.insert(*new_block);
324
325 cur_info = it->GetMemoryInfo();
326 }
327
328 // Update block state.
329 it->UpdateAttribute(mask, attr);
330 cur_address += cur_info.GetSize();
331 remaining_pages -= cur_info.GetNumPages();
332 } else {
333 // If we already have the right attributes, just advance.
334 if (cur_address + remaining_size < cur_info.GetEndAddress()) {
335 remaining_pages = 0;
336 cur_address += remaining_size;
337 } else {
338 remaining_pages =
339 (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
340 cur_address = cur_info.GetEndAddress();
341 }
342 }
343 it++;
344 }
345
346 this->CoalesceForUpdate(allocator, address, num_pages);
347}
348
287// Debug. 349// Debug.
288bool KMemoryBlockManager::CheckState() const { 350bool KMemoryBlockManager::CheckState() const {
289 // Loop over every block, ensuring that we are sorted and coalesced. 351 // Loop over every block, ensuring that we are sorted and coalesced.
diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h
index 96496e990..cb7b6f430 100644
--- a/src/core/hle/kernel/k_memory_block_manager.h
+++ b/src/core/hle/kernel/k_memory_block_manager.h
@@ -115,7 +115,11 @@ public:
115 void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address, 115 void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
116 size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm, 116 size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
117 KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm, 117 KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
118 KMemoryAttribute attr); 118 KMemoryAttribute attr, KMemoryBlockDisableMergeAttribute set_disable_attr,
119 KMemoryBlockDisableMergeAttribute clear_disable_attr);
120
121 void UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
122 size_t num_pages, KMemoryAttribute mask, KMemoryAttribute attr);
119 123
120 iterator FindIterator(KProcessAddress address) const { 124 iterator FindIterator(KProcessAddress address) const {
121 return m_memory_block_tree.find(KMemoryBlock( 125 return m_memory_block_tree.find(KMemoryBlock(
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index ebc540316..387f2c962 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -505,7 +505,7 @@ Result KPageTable::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress
505 R_TRY(this->CheckMemoryStateContiguous( 505 R_TRY(this->CheckMemoryStateContiguous(
506 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias, 506 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
507 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None, 507 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
508 KMemoryAttribute::All, KMemoryAttribute::None)); 508 KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None));
509 509
510 // Determine whether any pages being unmapped are code. 510 // Determine whether any pages being unmapped are code.
511 bool any_code_pages = false; 511 bool any_code_pages = false;
@@ -1770,7 +1770,11 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
1770 m_memory_block_manager.UpdateIfMatch( 1770 m_memory_block_manager.UpdateIfMatch(
1771 std::addressof(allocator), address, size / PageSize, KMemoryState::Free, 1771 std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
1772 KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal, 1772 KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
1773 KMemoryPermission::UserReadWrite, KMemoryAttribute::None); 1773 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1774 address == this->GetAliasRegionStart()
1775 ? KMemoryBlockDisableMergeAttribute::Normal
1776 : KMemoryBlockDisableMergeAttribute::None,
1777 KMemoryBlockDisableMergeAttribute::None);
1774 1778
1775 R_SUCCEED(); 1779 R_SUCCEED();
1776 } 1780 }
@@ -1868,6 +1872,13 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
1868 1872
1869 // Iterate over the memory, unmapping as we go. 1873 // Iterate over the memory, unmapping as we go.
1870 auto it = m_memory_block_manager.FindIterator(cur_address); 1874 auto it = m_memory_block_manager.FindIterator(cur_address);
1875
1876 const auto clear_merge_attr =
1877 (it->GetState() == KMemoryState::Normal &&
1878 it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address)
1879 ? KMemoryBlockDisableMergeAttribute::Normal
1880 : KMemoryBlockDisableMergeAttribute::None;
1881
1871 while (true) { 1882 while (true) {
1872 // Check that the iterator is valid. 1883 // Check that the iterator is valid.
1873 ASSERT(it != m_memory_block_manager.end()); 1884 ASSERT(it != m_memory_block_manager.end());
@@ -1905,7 +1916,7 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
1905 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, 1916 m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
1906 KMemoryState::Free, KMemoryPermission::None, 1917 KMemoryState::Free, KMemoryPermission::None,
1907 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, 1918 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
1908 KMemoryBlockDisableMergeAttribute::None); 1919 clear_merge_attr);
1909 1920
1910 // We succeeded. 1921 // We succeeded.
1911 R_SUCCEED(); 1922 R_SUCCEED();
@@ -2650,11 +2661,18 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
2650 size_t num_allocator_blocks; 2661 size_t num_allocator_blocks;
2651 constexpr auto AttributeTestMask = 2662 constexpr auto AttributeTestMask =
2652 ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared); 2663 ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
2653 R_TRY(this->CheckMemoryState( 2664 const KMemoryState state_test_mask =
2654 std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr), 2665 static_cast<KMemoryState>(((mask & static_cast<u32>(KMemoryAttribute::Uncached))
2655 std::addressof(num_allocator_blocks), addr, size, KMemoryState::FlagCanChangeAttribute, 2666 ? static_cast<u32>(KMemoryState::FlagCanChangeAttribute)
2656 KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None, 2667 : 0) |
2657 AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask)); 2668 ((mask & static_cast<u32>(KMemoryAttribute::PermissionLocked))
2669 ? static_cast<u32>(KMemoryState::FlagCanPermissionLock)
2670 : 0));
2671 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2672 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2673 addr, size, state_test_mask, state_test_mask,
2674 KMemoryPermission::None, KMemoryPermission::None,
2675 AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
2658 2676
2659 // Create an update allocator. 2677 // Create an update allocator.
2660 Result allocator_result{ResultSuccess}; 2678 Result allocator_result{ResultSuccess};
@@ -2662,18 +2680,17 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
2662 m_memory_block_slab_manager, num_allocator_blocks); 2680 m_memory_block_slab_manager, num_allocator_blocks);
2663 R_TRY(allocator_result); 2681 R_TRY(allocator_result);
2664 2682
2665 // Determine the new attribute. 2683 // If we need to, perform a change attribute operation.
2666 const KMemoryAttribute new_attr = 2684 if (True(KMemoryAttribute::Uncached & static_cast<KMemoryAttribute>(mask))) {
2667 static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) | 2685 // Perform operation.
2668 static_cast<KMemoryAttribute>(attr & mask))); 2686 R_TRY(this->Operate(addr, num_pages, old_perm,
2669 2687 OperationType::ChangePermissionsAndRefreshAndFlush, 0));
2670 // Perform operation. 2688 }
2671 this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
2672 2689
2673 // Update the blocks. 2690 // Update the blocks.
2674 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, 2691 m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages,
2675 new_attr, KMemoryBlockDisableMergeAttribute::None, 2692 static_cast<KMemoryAttribute>(mask),
2676 KMemoryBlockDisableMergeAttribute::None); 2693 static_cast<KMemoryAttribute>(attr));
2677 2694
2678 R_SUCCEED(); 2695 R_SUCCEED();
2679} 2696}
@@ -3086,6 +3103,7 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
3086 } 3103 }
3087 case OperationType::ChangePermissions: 3104 case OperationType::ChangePermissions:
3088 case OperationType::ChangePermissionsAndRefresh: 3105 case OperationType::ChangePermissionsAndRefresh:
3106 case OperationType::ChangePermissionsAndRefreshAndFlush:
3089 break; 3107 break;
3090 default: 3108 default:
3091 ASSERT(false); 3109 ASSERT(false);
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index e69498f02..cbcbb6f62 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -222,7 +222,8 @@ private:
222 Unmap = 3, 222 Unmap = 3,
223 ChangePermissions = 4, 223 ChangePermissions = 4,
224 ChangePermissionsAndRefresh = 5, 224 ChangePermissionsAndRefresh = 5,
225 Separate = 6, 225 ChangePermissionsAndRefreshAndFlush = 6,
226 Separate = 7,
226 }; 227 };
227 228
228 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = 229 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp
index 2cab74127..372684094 100644
--- a/src/core/hle/kernel/svc/svc_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_memory.cpp
@@ -108,10 +108,16 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
108 R_UNLESS((address < address + size), ResultInvalidCurrentMemory); 108 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
109 109
110 // Validate the attribute and mask. 110 // Validate the attribute and mask.
111 constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached); 111 constexpr u32 SupportedMask =
112 static_cast<u32>(MemoryAttribute::Uncached | MemoryAttribute::PermissionLocked);
112 R_UNLESS((mask | attr) == mask, ResultInvalidCombination); 113 R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
113 R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); 114 R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
114 115
116 // Check that permission locked is either being set or not masked.
117 R_UNLESS((static_cast<Svc::MemoryAttribute>(mask) & Svc::MemoryAttribute::PermissionLocked) ==
118 (static_cast<Svc::MemoryAttribute>(attr) & Svc::MemoryAttribute::PermissionLocked),
119 ResultInvalidCombination);
120
115 // Validate that the region is in range for the current process. 121 // Validate that the region is in range for the current process.
116 auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()}; 122 auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
117 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); 123 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);