Explorar el Código

core: hle: kernel: k_page_table: Add implementations of MapPages, UnmapPages, and FindFreeArea for TLS.

bunnei hace 4 años
padre
commit
3210bc2767
Se han modificado 2 ficheros con 141 adiciones y 2 borrados
  1. 126 0
      src/core/hle/kernel/k_page_table.cpp
  2. 15 2
      src/core/hle/kernel/k_page_table.h

+ 126 - 0
src/core/hle/kernel/k_page_table.cpp

@@ -424,6 +424,68 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std
     return ResultSuccess;
 }
 
+VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
+                               std::size_t num_pages, std::size_t alignment, std::size_t offset,
+                               std::size_t guard_pages) {
+    VAddr address = 0;
+
+    if (num_pages <= region_num_pages) {
+        if (this->IsAslrEnabled()) {
+            // Try to directly find a free area up to 8 times.
+            for (std::size_t i = 0; i < 8; i++) {
+                const std::size_t random_offset =
+                    KSystemControl::GenerateRandomRange(
+                        0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) *
+                    alignment;
+                const VAddr candidate =
+                    Common::AlignDown((region_start + random_offset), alignment) + offset;
+
+                KMemoryInfo info = this->QueryInfoImpl(candidate);
+
+                if (info.state != KMemoryState::Free) {
+                    continue;
+                }
+                if (region_start > candidate) {
+                    continue;
+                }
+                if (info.GetAddress() + guard_pages * PageSize > candidate) {
+                    continue;
+                }
+
+                const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1;
+                if (candidate_end > info.GetLastAddress()) {
+                    continue;
+                }
+                if (candidate_end > region_start + region_num_pages * PageSize - 1) {
+                    continue;
+                }
+
+                address = candidate;
+                break;
+            }
+            // Fall back to finding the first free area with a random offset.
+            if (address == 0) {
+                // NOTE: Nintendo does not account for guard pages here.
+                // This may theoretically cause an offset to be chosen that cannot be mapped. We
+                // will account for guard pages.
+                const std::size_t offset_pages = KSystemControl::GenerateRandomRange(
+                    0, region_num_pages - num_pages - guard_pages);
+                address = block_manager->FindFreeArea(region_start + offset_pages * PageSize,
+                                                      region_num_pages - offset_pages, num_pages,
+                                                      alignment, offset, guard_pages);
+            }
+        }
+
+        // Find the first free area.
+        if (address == 0) {
+            address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages,
+                                                  alignment, offset, guard_pages);
+        }
+    }
+
+    return address;
+}
+
 ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
                                           KPageTable& src_page_table, VAddr src_addr) {
     KScopedLightLock lk(general_lock);
@@ -1055,6 +1117,46 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
     return ResultSuccess;
 }
 
+ResultCode KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
+                                PAddr phys_addr, bool is_pa_valid, VAddr region_start,
+                                std::size_t region_num_pages, KMemoryState state,
+                                KMemoryPermission perm) {
+    ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
+
+    // Ensure this is a valid map request.
+    R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
+             ResultInvalidCurrentMemory);
+    R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
+
+    // Lock the table.
+    KScopedLightLock lk(general_lock);
+
+    // Find a random address to map at.
+    VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
+                                    this->GetNumGuardPages());
+    R_UNLESS(addr != 0, ResultOutOfMemory);
+    ASSERT(Common::IsAligned(addr, alignment));
+    ASSERT(this->CanContain(addr, num_pages * PageSize, state));
+    ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
+                                  KMemoryPermission::None, KMemoryPermission::None,
+                                  KMemoryAttribute::None, KMemoryAttribute::None)
+               .IsSuccess());
+
+    // Perform mapping operation.
+    if (is_pa_valid) {
+        R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr));
+    } else {
+        UNIMPLEMENTED();
+    }
+
+    // Update the blocks.
+    block_manager->Update(addr, num_pages, state, perm);
+
+    // We successfully mapped the pages.
+    *out_addr = addr;
+    return ResultSuccess;
+}
+
 ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
     ASSERT(this->IsLockedByCurrentThread());
 
@@ -1097,6 +1199,30 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
     return ResultSuccess;
 }
 
+ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) {
+    // Check that the unmap is in range.
+    const std::size_t size = num_pages * PageSize;
+    R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
+
+    // Lock the table.
+    KScopedLightLock lk(general_lock);
+
+    // Check the memory state.
+    std::size_t num_allocator_blocks{};
+    R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
+                                 KMemoryState::All, state, KMemoryPermission::None,
+                                 KMemoryPermission::None, KMemoryAttribute::All,
+                                 KMemoryAttribute::None));
+
+    // Perform the unmap.
+    R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap));
+
+    // Update the blocks.
+    block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None);
+
+    return ResultSuccess;
+}
+
 ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
                                                   Svc::MemoryPermission svc_perm) {
     const size_t num_pages = size / PageSize;

+ 15 - 2
src/core/hle/kernel/k_page_table.h

@@ -46,7 +46,14 @@ public:
     ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
     ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
                         KMemoryPermission perm);
+    ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
+                        PAddr phys_addr, KMemoryState state, KMemoryPermission perm) {
+        return this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
+                              this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize,
+                              state, perm);
+    }
     ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
+    ResultCode UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state);
     ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
                                           Svc::MemoryPermission svc_perm);
     KMemoryInfo QueryInfo(VAddr addr);
@@ -91,6 +98,9 @@ private:
     ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
     ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
                         KMemoryPermission perm);
+    ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
+                        PAddr phys_addr, bool is_pa_valid, VAddr region_start,
+                        std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
     ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
     bool IsRegionMapped(VAddr address, u64 size);
     bool IsRegionContiguous(VAddr addr, u64 size) const;
@@ -105,6 +115,9 @@ private:
     VAddr GetRegionAddress(KMemoryState state) const;
     std::size_t GetRegionSize(KMemoryState state) const;
 
+    VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
+                       std::size_t alignment, std::size_t offset, std::size_t guard_pages);
+
     ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
                                           std::size_t size, KMemoryState state_mask,
                                           KMemoryState state, KMemoryPermission perm_mask,
@@ -137,7 +150,7 @@ private:
         return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
                                 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
     }
-    ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask,
+    ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
                                 KMemoryState state, KMemoryPermission perm_mask,
                                 KMemoryPermission perm, KMemoryAttribute attr_mask,
                                 KMemoryAttribute attr,
@@ -210,7 +223,7 @@ public:
     constexpr VAddr GetAliasCodeRegionSize() const {
         return alias_code_region_end - alias_code_region_start;
     }
-    size_t GetNormalMemorySize() {
+    std::size_t GetNormalMemorySize() {
         KScopedLightLock lk(general_lock);
         return GetHeapSize() + mapped_physical_memory_size;
     }