|
|
@@ -285,72 +285,141 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
|
|
|
return ResultSuccess;
|
|
|
}
|
|
|
|
|
|
-ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
|
|
+ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
|
|
|
+ // Validate the mapping request.
|
|
|
+ R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
|
|
|
+ ResultInvalidMemoryRegion);
|
|
|
+
|
|
|
+ // Lock the table.
|
|
|
KScopedLightLock lk(general_lock);
|
|
|
|
|
|
- const std::size_t num_pages{size / PageSize};
|
|
|
+ // Verify that the source memory is normal heap.
|
|
|
+ KMemoryState src_state{};
|
|
|
+ KMemoryPermission src_perm{};
|
|
|
+ std::size_t num_src_allocator_blocks{};
|
|
|
+ R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks,
|
|
|
+ src_address, size, KMemoryState::All, KMemoryState::Normal,
|
|
|
+ KMemoryPermission::All, KMemoryPermission::UserReadWrite,
|
|
|
+ KMemoryAttribute::All, KMemoryAttribute::None));
|
|
|
|
|
|
- KMemoryState state{};
|
|
|
- KMemoryPermission perm{};
|
|
|
- CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size,
|
|
|
- KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
|
|
|
- KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask,
|
|
|
- KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
|
|
|
+ // Verify that the destination memory is unmapped.
|
|
|
+ std::size_t num_dst_allocator_blocks{};
|
|
|
+ R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All,
|
|
|
+ KMemoryState::Free, KMemoryPermission::None,
|
|
|
+ KMemoryPermission::None, KMemoryAttribute::None,
|
|
|
+ KMemoryAttribute::None));
|
|
|
|
|
|
- if (IsRegionMapped(dst_addr, size)) {
|
|
|
- return ResultInvalidCurrentMemory;
|
|
|
- }
|
|
|
+ // Map the code memory.
|
|
|
+ {
|
|
|
+ // Determine the number of pages being operated on.
|
|
|
+ const std::size_t num_pages = size / PageSize;
|
|
|
|
|
|
- KPageLinkedList page_linked_list;
|
|
|
- AddRegionToPages(src_addr, num_pages, page_linked_list);
|
|
|
+ // Create page groups for the memory being mapped.
|
|
|
+ KPageLinkedList pg;
|
|
|
+ AddRegionToPages(src_address, num_pages, pg);
|
|
|
|
|
|
- {
|
|
|
- auto block_guard = detail::ScopeExit(
|
|
|
- [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); });
|
|
|
+ // Reprotect the source as kernel-read/not mapped.
|
|
|
+ const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
|
|
|
+ KMemoryPermission::NotMapped);
|
|
|
+ R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions));
|
|
|
|
|
|
- CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None,
|
|
|
- OperationType::ChangePermissions));
|
|
|
- CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None));
|
|
|
+ // Ensure that we unprotect the source pages on failure.
|
|
|
+ auto unprot_guard = SCOPE_GUARD({
|
|
|
+ ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions)
|
|
|
+ .IsSuccess());
|
|
|
+ });
|
|
|
|
|
|
- block_guard.Cancel();
|
|
|
- }
|
|
|
+ // Map the alias pages.
|
|
|
+ R_TRY(MapPages(dst_address, pg, new_perm));
|
|
|
|
|
|
- block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None,
|
|
|
- KMemoryAttribute::Locked);
|
|
|
- block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode);
|
|
|
+ // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
|
|
|
+ // failure.
|
|
|
+ unprot_guard.Cancel();
|
|
|
+
|
|
|
+ // Apply the memory block updates.
|
|
|
+ block_manager->Update(src_address, num_pages, src_state, new_perm,
|
|
|
+ KMemoryAttribute::Locked);
|
|
|
+ block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm,
|
|
|
+ KMemoryAttribute::None);
|
|
|
+ }
|
|
|
|
|
|
return ResultSuccess;
|
|
|
}
|
|
|
|
|
|
-ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
|
|
+ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
|
|
|
+ // Validate the mapping request.
|
|
|
+ R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
|
|
|
+ ResultInvalidMemoryRegion);
|
|
|
+
|
|
|
+ // Lock the table.
|
|
|
KScopedLightLock lk(general_lock);
|
|
|
|
|
|
- if (!size) {
|
|
|
- return ResultSuccess;
|
|
|
+ // Verify that the source memory is locked normal heap.
|
|
|
+ std::size_t num_src_allocator_blocks{};
|
|
|
+ R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
|
|
|
+ KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
|
|
|
+ KMemoryPermission::None, KMemoryAttribute::All,
|
|
|
+ KMemoryAttribute::Locked));
|
|
|
+
|
|
|
+ // Verify that the destination memory is aliasable code.
|
|
|
+ std::size_t num_dst_allocator_blocks{};
|
|
|
+ R_TRY(this->CheckMemoryStateContiguous(
|
|
|
+ std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
|
|
|
+ KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
|
|
|
+ KMemoryAttribute::All, KMemoryAttribute::None));
|
|
|
+
|
|
|
+ // Determine whether any pages being unmapped are code.
|
|
|
+ bool any_code_pages = false;
|
|
|
+ {
|
|
|
+ KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address);
|
|
|
+ while (true) {
|
|
|
+ // Get the memory info.
|
|
|
+ const KMemoryInfo info = it->GetMemoryInfo();
|
|
|
+
|
|
|
+ // Check if the memory has code flag.
|
|
|
+ if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) {
|
|
|
+ any_code_pages = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if we're done.
|
|
|
+ if (dst_address + size - 1 <= info.GetLastAddress()) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Advance.
|
|
|
+ ++it;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- const std::size_t num_pages{size / PageSize};
|
|
|
+ // Ensure that we maintain the instruction cache.
|
|
|
+ bool reprotected_pages = false;
|
|
|
+ SCOPE_EXIT({
|
|
|
+ if (reprotected_pages && any_code_pages) {
|
|
|
+ system.InvalidateCpuInstructionCacheRange(dst_address, size);
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size,
|
|
|
- KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
|
|
|
- KMemoryPermission::None, KMemoryAttribute::Mask,
|
|
|
- KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
|
|
|
+ // Unmap.
|
|
|
+ {
|
|
|
+ // Determine the number of pages being operated on.
|
|
|
+ const std::size_t num_pages = size / PageSize;
|
|
|
|
|
|
- KMemoryState state{};
|
|
|
- CASCADE_CODE(CheckMemoryState(
|
|
|
- &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
|
|
|
- KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
|
|
|
- KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
|
|
|
- CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
|
|
|
- KMemoryPermission::None, KMemoryAttribute::Mask,
|
|
|
- KMemoryAttribute::None));
|
|
|
- CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
|
|
|
+ // Unmap the aliased copy of the pages.
|
|
|
+ R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
|
|
|
|
|
|
- block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
|
|
|
- block_manager->Update(src_addr, num_pages, KMemoryState::Normal,
|
|
|
- KMemoryPermission::UserReadWrite);
|
|
|
+ // Try to set the permissions for the source pages back to what they should be.
|
|
|
+ R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
|
|
|
+ OperationType::ChangePermissions));
|
|
|
|
|
|
- system.InvalidateCpuInstructionCacheRange(dst_addr, size);
|
|
|
+ // Apply the memory block updates.
|
|
|
+ block_manager->Update(dst_address, num_pages, KMemoryState::None);
|
|
|
+ block_manager->Update(src_address, num_pages, KMemoryState::Normal,
|
|
|
+ KMemoryPermission::UserReadWrite);
|
|
|
+
|
|
|
+ // Note that we reprotected pages.
|
|
|
+ reprotected_pages = true;
|
|
|
+ }
|
|
|
|
|
|
return ResultSuccess;
|
|
|
}
|