Browse Source
kernel: add KPageTableBase
kernel: add KPageTableBase
Co-authored-by: Kelebek1 <eeeedddccc@hotmail.co.uk>pull/15/merge
31 changed files with 7204 additions and 4879 deletions
-
30src/common/page_table.cpp
-
17src/common/page_table.h
-
6src/core/CMakeLists.txt
-
102src/core/debugger/gdbstub.cpp
-
13src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
-
7src/core/hle/kernel/board/nintendo/nx/k_system_control.h
-
36src/core/hle/kernel/k_capabilities.cpp
-
17src/core/hle/kernel/k_capabilities.h
-
4src/core/hle/kernel/k_device_address_space.cpp
-
10src/core/hle/kernel/k_device_address_space.h
-
8src/core/hle/kernel/k_memory_layout.h
-
12src/core/hle/kernel/k_memory_manager.cpp
-
3519src/core/hle/kernel/k_page_table.cpp
-
542src/core/hle/kernel/k_page_table.h
-
5718src/core/hle/kernel/k_page_table_base.cpp
-
759src/core/hle/kernel/k_page_table_base.h
-
18src/core/hle/kernel/k_process.cpp
-
14src/core/hle/kernel/k_process.h
-
480src/core/hle/kernel/k_process_page_table.h
-
2src/core/hle/kernel/k_server_session.cpp
-
2src/core/hle/kernel/k_system_resource.cpp
-
4src/core/hle/kernel/k_thread_local_page.cpp
-
389src/core/hle/kernel/process_capability.cpp
-
266src/core/hle/kernel/process_capability.h
-
6src/core/hle/kernel/svc/svc_memory.cpp
-
9src/core/hle/kernel/svc/svc_physical_memory.cpp
-
3src/core/hle/kernel/svc/svc_process_memory.cpp
-
8src/core/hle/kernel/svc/svc_query_memory.cpp
-
31src/core/hle/result.h
-
45src/core/hle/service/ldr/ldr.cpp
-
6src/core/memory.cpp
3519
src/core/hle/kernel/k_page_table.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
5718
src/core/hle/kernel/k_page_table_base.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,759 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
|
|||
#include "common/common_funcs.h" |
|||
#include "common/page_table.h" |
|||
#include "core/core.h" |
|||
#include "core/hle/kernel/k_dynamic_resource_manager.h" |
|||
#include "core/hle/kernel/k_light_lock.h" |
|||
#include "core/hle/kernel/k_memory_block.h" |
|||
#include "core/hle/kernel/k_memory_block_manager.h" |
|||
#include "core/hle/kernel/k_memory_layout.h" |
|||
#include "core/hle/kernel/k_memory_manager.h" |
|||
#include "core/hle/kernel/k_typed_address.h" |
|||
#include "core/hle/kernel/kernel.h" |
|||
#include "core/hle/result.h" |
|||
#include "core/memory.h" |
|||
|
|||
namespace Kernel { |
|||
|
|||
enum class DisableMergeAttribute : u8 { |
|||
None = (0U << 0), |
|||
|
|||
DisableHead = (1U << 0), |
|||
DisableHeadAndBody = (1U << 1), |
|||
EnableHeadAndBody = (1U << 2), |
|||
DisableTail = (1U << 3), |
|||
EnableTail = (1U << 4), |
|||
EnableAndMergeHeadBodyTail = (1U << 5), |
|||
|
|||
EnableHeadBodyTail = EnableHeadAndBody | EnableTail, |
|||
DisableHeadBodyTail = DisableHeadAndBody | DisableTail, |
|||
}; |
|||
DECLARE_ENUM_FLAG_OPERATORS(DisableMergeAttribute); |
|||
|
|||
struct KPageProperties { |
|||
KMemoryPermission perm; |
|||
bool io; |
|||
bool uncached; |
|||
DisableMergeAttribute disable_merge_attributes; |
|||
}; |
|||
static_assert(std::is_trivial_v<KPageProperties>); |
|||
static_assert(sizeof(KPageProperties) == sizeof(u32)); |
|||
|
|||
class KResourceLimit; |
|||
class KSystemResource; |
|||
|
|||
class KPageTableBase { |
|||
YUZU_NON_COPYABLE(KPageTableBase); |
|||
YUZU_NON_MOVEABLE(KPageTableBase); |
|||
|
|||
public: |
|||
using TraversalEntry = Common::PageTable::TraversalEntry; |
|||
using TraversalContext = Common::PageTable::TraversalContext; |
|||
|
|||
class MemoryRange { |
|||
private: |
|||
KernelCore& m_kernel; |
|||
KPhysicalAddress m_address; |
|||
size_t m_size; |
|||
bool m_heap; |
|||
|
|||
public: |
|||
explicit MemoryRange(KernelCore& kernel) |
|||
: m_kernel(kernel), m_address(0), m_size(0), m_heap(false) {} |
|||
|
|||
void Set(KPhysicalAddress address, size_t size, bool heap) { |
|||
m_address = address; |
|||
m_size = size; |
|||
m_heap = heap; |
|||
} |
|||
|
|||
KPhysicalAddress GetAddress() const { |
|||
return m_address; |
|||
} |
|||
size_t GetSize() const { |
|||
return m_size; |
|||
} |
|||
bool IsHeap() const { |
|||
return m_heap; |
|||
} |
|||
|
|||
void Open(); |
|||
void Close(); |
|||
}; |
|||
|
|||
protected: |
|||
enum MemoryFillValue : u8 { |
|||
MemoryFillValue_Zero = 0, |
|||
MemoryFillValue_Stack = 'X', |
|||
MemoryFillValue_Ipc = 'Y', |
|||
MemoryFillValue_Heap = 'Z', |
|||
}; |
|||
|
|||
enum class OperationType { |
|||
Map = 0, |
|||
MapGroup = 1, |
|||
MapFirstGroup = 2, |
|||
Unmap = 3, |
|||
ChangePermissions = 4, |
|||
ChangePermissionsAndRefresh = 5, |
|||
ChangePermissionsAndRefreshAndFlush = 6, |
|||
Separate = 7, |
|||
}; |
|||
|
|||
static constexpr size_t MaxPhysicalMapAlignment = 1_GiB; |
|||
static constexpr size_t RegionAlignment = 2_MiB; |
|||
static_assert(RegionAlignment == KernelAslrAlignment); |
|||
|
|||
struct PageLinkedList { |
|||
private: |
|||
struct Node { |
|||
Node* m_next; |
|||
std::array<u8, PageSize - sizeof(Node*)> m_buffer; |
|||
}; |
|||
static_assert(std::is_trivial_v<Node>); |
|||
|
|||
private: |
|||
Node* m_root{}; |
|||
|
|||
public: |
|||
constexpr PageLinkedList() : m_root(nullptr) {} |
|||
|
|||
void Push(Node* n) { |
|||
ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(n), PageSize)); |
|||
n->m_next = m_root; |
|||
m_root = n; |
|||
} |
|||
|
|||
Node* Peek() const { |
|||
return m_root; |
|||
} |
|||
|
|||
Node* Pop() { |
|||
Node* const r = m_root; |
|||
|
|||
m_root = r->m_next; |
|||
r->m_next = nullptr; |
|||
|
|||
return r; |
|||
} |
|||
}; |
|||
static_assert(std::is_trivially_destructible_v<PageLinkedList>); |
|||
|
|||
static constexpr auto DefaultMemoryIgnoreAttr = |
|||
KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; |
|||
|
|||
static constexpr size_t GetAddressSpaceWidth(Svc::CreateProcessFlag as_type) { |
|||
switch (static_cast<Svc::CreateProcessFlag>(as_type & |
|||
Svc::CreateProcessFlag::AddressSpaceMask)) { |
|||
case Svc::CreateProcessFlag::AddressSpace64Bit: |
|||
return 39; |
|||
case Svc::CreateProcessFlag::AddressSpace64BitDeprecated: |
|||
return 36; |
|||
case Svc::CreateProcessFlag::AddressSpace32Bit: |
|||
case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias: |
|||
return 32; |
|||
default: |
|||
UNREACHABLE(); |
|||
} |
|||
} |
|||
|
|||
private: |
|||
class KScopedPageTableUpdater { |
|||
private: |
|||
KPageTableBase* m_pt; |
|||
PageLinkedList m_ll; |
|||
|
|||
public: |
|||
explicit KScopedPageTableUpdater(KPageTableBase* pt) : m_pt(pt), m_ll() {} |
|||
explicit KScopedPageTableUpdater(KPageTableBase& pt) |
|||
: KScopedPageTableUpdater(std::addressof(pt)) {} |
|||
~KScopedPageTableUpdater() { |
|||
m_pt->FinalizeUpdate(this->GetPageList()); |
|||
} |
|||
|
|||
PageLinkedList* GetPageList() { |
|||
return std::addressof(m_ll); |
|||
} |
|||
}; |
|||
|
|||
private: |
|||
KernelCore& m_kernel; |
|||
Core::System& m_system; |
|||
KProcessAddress m_address_space_start{}; |
|||
KProcessAddress m_address_space_end{}; |
|||
KProcessAddress m_heap_region_start{}; |
|||
KProcessAddress m_heap_region_end{}; |
|||
KProcessAddress m_current_heap_end{}; |
|||
KProcessAddress m_alias_region_start{}; |
|||
KProcessAddress m_alias_region_end{}; |
|||
KProcessAddress m_stack_region_start{}; |
|||
KProcessAddress m_stack_region_end{}; |
|||
KProcessAddress m_kernel_map_region_start{}; |
|||
KProcessAddress m_kernel_map_region_end{}; |
|||
KProcessAddress m_alias_code_region_start{}; |
|||
KProcessAddress m_alias_code_region_end{}; |
|||
KProcessAddress m_code_region_start{}; |
|||
KProcessAddress m_code_region_end{}; |
|||
size_t m_max_heap_size{}; |
|||
size_t m_mapped_physical_memory_size{}; |
|||
size_t m_mapped_unsafe_physical_memory{}; |
|||
size_t m_mapped_insecure_memory{}; |
|||
size_t m_mapped_ipc_server_memory{}; |
|||
mutable KLightLock m_general_lock; |
|||
mutable KLightLock m_map_physical_memory_lock; |
|||
KLightLock m_device_map_lock; |
|||
std::unique_ptr<Common::PageTable> m_impl{}; |
|||
Core::Memory::Memory* m_memory{}; |
|||
KMemoryBlockManager m_memory_block_manager{}; |
|||
u32 m_allocate_option{}; |
|||
u32 m_address_space_width{}; |
|||
bool m_is_kernel{}; |
|||
bool m_enable_aslr{}; |
|||
bool m_enable_device_address_space_merge{}; |
|||
KMemoryBlockSlabManager* m_memory_block_slab_manager{}; |
|||
KBlockInfoManager* m_block_info_manager{}; |
|||
KResourceLimit* m_resource_limit{}; |
|||
const KMemoryRegion* m_cached_physical_linear_region{}; |
|||
const KMemoryRegion* m_cached_physical_heap_region{}; |
|||
MemoryFillValue m_heap_fill_value{}; |
|||
MemoryFillValue m_ipc_fill_value{}; |
|||
MemoryFillValue m_stack_fill_value{}; |
|||
|
|||
public: |
|||
explicit KPageTableBase(KernelCore& kernel); |
|||
~KPageTableBase(); |
|||
|
|||
Result InitializeForKernel(bool is_64_bit, KVirtualAddress start, KVirtualAddress end, |
|||
Core::Memory::Memory& memory); |
|||
Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, |
|||
bool enable_device_address_space_merge, bool from_back, |
|||
KMemoryManager::Pool pool, KProcessAddress code_address, |
|||
size_t code_size, KSystemResource* system_resource, |
|||
KResourceLimit* resource_limit, Core::Memory::Memory& memory); |
|||
|
|||
void Finalize(); |
|||
|
|||
bool IsKernel() const { |
|||
return m_is_kernel; |
|||
} |
|||
bool IsAslrEnabled() const { |
|||
return m_enable_aslr; |
|||
} |
|||
|
|||
bool Contains(KProcessAddress addr) const { |
|||
return m_address_space_start <= addr && addr <= m_address_space_end - 1; |
|||
} |
|||
|
|||
bool Contains(KProcessAddress addr, size_t size) const { |
|||
return m_address_space_start <= addr && addr < addr + size && |
|||
addr + size - 1 <= m_address_space_end - 1; |
|||
} |
|||
|
|||
bool IsInAliasRegion(KProcessAddress addr, size_t size) const { |
|||
return this->Contains(addr, size) && m_alias_region_start <= addr && |
|||
addr + size - 1 <= m_alias_region_end - 1; |
|||
} |
|||
|
|||
bool IsInHeapRegion(KProcessAddress addr, size_t size) const { |
|||
return this->Contains(addr, size) && m_heap_region_start <= addr && |
|||
addr + size - 1 <= m_heap_region_end - 1; |
|||
} |
|||
|
|||
bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const { |
|||
// Even though Unsafe physical memory is KMemoryState_Normal, it must be mapped inside the |
|||
// alias code region. |
|||
return this->CanContain(addr, size, Svc::MemoryState::AliasCode); |
|||
} |
|||
|
|||
KScopedLightLock AcquireDeviceMapLock() { |
|||
return KScopedLightLock(m_device_map_lock); |
|||
} |
|||
|
|||
KProcessAddress GetRegionAddress(Svc::MemoryState state) const; |
|||
size_t GetRegionSize(Svc::MemoryState state) const; |
|||
bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const; |
|||
|
|||
KProcessAddress GetRegionAddress(KMemoryState state) const { |
|||
return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask)); |
|||
} |
|||
size_t GetRegionSize(KMemoryState state) const { |
|||
return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask)); |
|||
} |
|||
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { |
|||
return this->CanContain(addr, size, |
|||
static_cast<Svc::MemoryState>(state & KMemoryState::Mask)); |
|||
} |
|||
|
|||
public: |
|||
Core::Memory::Memory& GetMemory() { |
|||
return *m_memory; |
|||
} |
|||
|
|||
Core::Memory::Memory& GetMemory() const { |
|||
return *m_memory; |
|||
} |
|||
|
|||
Common::PageTable& GetImpl() { |
|||
return *m_impl; |
|||
} |
|||
|
|||
Common::PageTable& GetImpl() const { |
|||
return *m_impl; |
|||
} |
|||
|
|||
size_t GetNumGuardPages() const { |
|||
return this->IsKernel() ? 1 : 4; |
|||
} |
|||
|
|||
protected: |
|||
// NOTE: These three functions (Operate, Operate, FinalizeUpdate) are virtual functions |
|||
// in Nintendo's kernel. We devirtualize them, since KPageTable is the only derived |
|||
// class, and this avoids unnecessary virtual function calls. |
|||
Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages, |
|||
KPhysicalAddress phys_addr, bool is_pa_valid, const KPageProperties properties, |
|||
OperationType operation, bool reuse_ll); |
|||
Result Operate(PageLinkedList* page_list, KProcessAddress virt_addr, size_t num_pages, |
|||
const KPageGroup& page_group, const KPageProperties properties, |
|||
OperationType operation, bool reuse_ll); |
|||
void FinalizeUpdate(PageLinkedList* page_list); |
|||
|
|||
bool IsLockedByCurrentThread() const { |
|||
return m_general_lock.IsLockedByCurrentThread(); |
|||
} |
|||
|
|||
bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr) { |
|||
ASSERT(this->IsLockedByCurrentThread()); |
|||
|
|||
return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress( |
|||
m_cached_physical_linear_region, phys_addr); |
|||
} |
|||
|
|||
bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr, size_t size) { |
|||
ASSERT(this->IsLockedByCurrentThread()); |
|||
|
|||
return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress( |
|||
m_cached_physical_linear_region, phys_addr, size); |
|||
} |
|||
|
|||
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) { |
|||
ASSERT(this->IsLockedByCurrentThread()); |
|||
|
|||
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, |
|||
phys_addr); |
|||
} |
|||
|
|||
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr, size_t size) { |
|||
ASSERT(this->IsLockedByCurrentThread()); |
|||
|
|||
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, |
|||
phys_addr, size); |
|||
} |
|||
|
|||
bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) { |
|||
ASSERT(!this->IsLockedByCurrentThread()); |
|||
|
|||
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region, |
|||
phys_addr); |
|||
} |
|||
|
|||
bool ContainsPages(KProcessAddress addr, size_t num_pages) const { |
|||
return (m_address_space_start <= addr) && |
|||
(num_pages <= (m_address_space_end - m_address_space_start) / PageSize) && |
|||
(addr + num_pages * PageSize - 1 <= m_address_space_end - 1); |
|||
} |
|||
|
|||
private: |
|||
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages, |
|||
size_t num_pages, size_t alignment, size_t offset, |
|||
size_t guard_pages) const; |
|||
|
|||
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size, |
|||
KMemoryState state_mask, KMemoryState state, |
|||
KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr) const; |
|||
Result CheckMemoryStateContiguous(KProcessAddress addr, size_t size, KMemoryState state_mask, |
|||
KMemoryState state, KMemoryPermission perm_mask, |
|||
KMemoryPermission perm, KMemoryAttribute attr_mask, |
|||
KMemoryAttribute attr) const { |
|||
R_RETURN(this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask, |
|||
perm, attr_mask, attr)); |
|||
} |
|||
|
|||
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state, |
|||
KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr) const; |
|||
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, |
|||
KMemoryAttribute* out_attr, size_t* out_blocks_needed, |
|||
KMemoryBlockManager::const_iterator it, KProcessAddress last_addr, |
|||
KMemoryState state_mask, KMemoryState state, |
|||
KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr, |
|||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; |
|||
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, |
|||
KMemoryAttribute* out_attr, size_t* out_blocks_needed, |
|||
KProcessAddress addr, size_t size, KMemoryState state_mask, |
|||
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr, |
|||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const; |
|||
Result CheckMemoryState(size_t* out_blocks_needed, KProcessAddress addr, size_t size, |
|||
KMemoryState state_mask, KMemoryState state, |
|||
KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr, |
|||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { |
|||
R_RETURN(this->CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, |
|||
state_mask, state, perm_mask, perm, attr_mask, attr, |
|||
ignore_attr)); |
|||
} |
|||
Result CheckMemoryState(KProcessAddress addr, size_t size, KMemoryState state_mask, |
|||
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr, |
|||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const { |
|||
R_RETURN(this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm, |
|||
attr_mask, attr, ignore_attr)); |
|||
} |
|||
|
|||
Result LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_paddr, KProcessAddress addr, |
|||
size_t size, KMemoryState state_mask, KMemoryState state, |
|||
KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr, |
|||
KMemoryPermission new_perm, KMemoryAttribute lock_attr); |
|||
Result UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask, |
|||
KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr, |
|||
KMemoryPermission new_perm, KMemoryAttribute lock_attr, |
|||
const KPageGroup* pg); |
|||
|
|||
Result QueryInfoImpl(KMemoryInfo* out_info, Svc::PageInfo* out_page, |
|||
KProcessAddress address) const; |
|||
|
|||
Result QueryMappingImpl(KProcessAddress* out, KPhysicalAddress address, size_t size, |
|||
Svc::MemoryState state) const; |
|||
|
|||
Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address, |
|||
size_t num_pages, KMemoryPermission perm); |
|||
Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address, |
|||
const KPageGroup& pg, const KPageProperties properties, bool reuse_ll); |
|||
|
|||
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, |
|||
const KPageGroup& pg); |
|||
|
|||
Result MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages); |
|||
bool IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages); |
|||
|
|||
Result GetContiguousMemoryRangeWithState(MemoryRange* out, KProcessAddress address, size_t size, |
|||
KMemoryState state_mask, KMemoryState state, |
|||
KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr); |
|||
|
|||
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, |
|||
KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, |
|||
size_t region_num_pages, KMemoryState state, KMemoryPermission perm); |
|||
|
|||
Result MapIoImpl(KProcessAddress* out, PageLinkedList* page_list, KPhysicalAddress phys_addr, |
|||
size_t size, KMemoryState state, KMemoryPermission perm); |
|||
Result ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddress phys_addr, size_t size, |
|||
KMemoryState state); |
|||
Result WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAddress src_addr, size_t size, |
|||
KMemoryState state); |
|||
|
|||
Result SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, |
|||
KProcessAddress address, size_t size, KMemoryPermission test_perm, |
|||
KMemoryState dst_state); |
|||
Result SetupForIpcServer(KProcessAddress* out_addr, size_t size, KProcessAddress src_addr, |
|||
KMemoryPermission test_perm, KMemoryState dst_state, |
|||
KPageTableBase& src_page_table, bool send); |
|||
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, KProcessAddress address, |
|||
size_t size, KMemoryPermission prot_perm); |
|||
|
|||
size_t GetSize(KMemoryState state) const; |
|||
|
|||
bool GetPhysicalAddressLocked(KPhysicalAddress* out, KProcessAddress virt_addr) const { |
|||
// Validate pre-conditions. |
|||
ASSERT(this->IsLockedByCurrentThread()); |
|||
|
|||
return this->GetImpl().GetPhysicalAddress(out, virt_addr); |
|||
} |
|||
|
|||
public: |
|||
bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress virt_addr) const { |
|||
// Validate pre-conditions. |
|||
ASSERT(!this->IsLockedByCurrentThread()); |
|||
|
|||
// Acquire exclusive access to the table while doing address translation. |
|||
KScopedLightLock lk(m_general_lock); |
|||
|
|||
return this->GetPhysicalAddressLocked(out, virt_addr); |
|||
} |
|||
|
|||
KBlockInfoManager* GetBlockInfoManager() const { |
|||
return m_block_info_manager; |
|||
} |
|||
|
|||
Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm); |
|||
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, |
|||
Svc::MemoryPermission perm); |
|||
Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask, |
|||
KMemoryAttribute attr); |
|||
Result SetHeapSize(KProcessAddress* out, size_t size); |
|||
Result SetMaxHeapSize(size_t size); |
|||
Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info, |
|||
KProcessAddress addr) const; |
|||
Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) const; |
|||
Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const { |
|||
R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Static)); |
|||
} |
|||
Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) const { |
|||
R_RETURN(this->QueryMappingImpl(out, address, size, Svc::MemoryState::Io)); |
|||
} |
|||
Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); |
|||
Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); |
|||
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); |
|||
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); |
|||
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm); |
|||
Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, |
|||
Svc::MemoryMapping mapping, Svc::MemoryPermission perm); |
|||
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, |
|||
Svc::MemoryMapping mapping); |
|||
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm); |
|||
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm); |
|||
Result MapInsecureMemory(KProcessAddress address, size_t size); |
|||
Result UnmapInsecureMemory(KProcessAddress address, size_t size); |
|||
|
|||
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, |
|||
KPhysicalAddress phys_addr, KProcessAddress region_start, |
|||
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) { |
|||
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start, |
|||
region_num_pages, state, perm)); |
|||
} |
|||
|
|||
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, |
|||
KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) { |
|||
R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, |
|||
this->GetRegionAddress(state), |
|||
this->GetRegionSize(state) / PageSize, state, perm)); |
|||
} |
|||
|
|||
Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state, |
|||
KMemoryPermission perm) { |
|||
R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false, |
|||
this->GetRegionAddress(state), |
|||
this->GetRegionSize(state) / PageSize, state, perm)); |
|||
} |
|||
|
|||
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, |
|||
KMemoryPermission perm); |
|||
Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state); |
|||
|
|||
Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg, |
|||
KProcessAddress region_start, size_t region_num_pages, KMemoryState state, |
|||
KMemoryPermission perm); |
|||
Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state, |
|||
KMemoryPermission perm); |
|||
Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state); |
|||
|
|||
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, |
|||
KMemoryState state_mask, KMemoryState state, |
|||
KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr); |
|||
|
|||
Result InvalidateProcessDataCache(KProcessAddress address, size_t size); |
|||
Result InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size); |
|||
|
|||
Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); |
|||
Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size, |
|||
KMemoryState state); |
|||
|
|||
Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); |
|||
Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size, |
|||
KMemoryState state); |
|||
|
|||
Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size, |
|||
KMemoryPermission perm, bool is_aligned, bool check_heap); |
|||
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap); |
|||
|
|||
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size); |
|||
Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size); |
|||
|
|||
Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out, |
|||
KProcessAddress address, size_t size, |
|||
KMemoryPermission perm, bool is_aligned); |
|||
Result OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* out, KProcessAddress address, |
|||
size_t size); |
|||
|
|||
Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size); |
|||
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size); |
|||
|
|||
Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, |
|||
KMemoryPermission perm); |
|||
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg); |
|||
Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size); |
|||
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg); |
|||
|
|||
Result OpenMemoryRangeForProcessCacheOperation(MemoryRange* out, KProcessAddress address, |
|||
size_t size); |
|||
|
|||
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, |
|||
KProcessAddress src_addr, KMemoryState src_state_mask, |
|||
KMemoryState src_state, KMemoryPermission src_test_perm, |
|||
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr); |
|||
Result CopyMemoryFromLinearToKernel(void* buffer, size_t size, KProcessAddress src_addr, |
|||
KMemoryState src_state_mask, KMemoryState src_state, |
|||
KMemoryPermission src_test_perm, |
|||
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr); |
|||
Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size, |
|||
KMemoryState dst_state_mask, KMemoryState dst_state, |
|||
KMemoryPermission dst_test_perm, |
|||
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, |
|||
KProcessAddress src_addr); |
|||
Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size, |
|||
KMemoryState dst_state_mask, KMemoryState dst_state, |
|||
KMemoryPermission dst_test_perm, |
|||
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, |
|||
void* buffer); |
|||
Result CopyMemoryFromHeapToHeap(KPageTableBase& dst_page_table, KProcessAddress dst_addr, |
|||
size_t size, KMemoryState dst_state_mask, |
|||
KMemoryState dst_state, KMemoryPermission dst_test_perm, |
|||
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, |
|||
KProcessAddress src_addr, KMemoryState src_state_mask, |
|||
KMemoryState src_state, KMemoryPermission src_test_perm, |
|||
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr); |
|||
Result CopyMemoryFromHeapToHeapWithoutCheckDestination( |
|||
KPageTableBase& dst_page_table, KProcessAddress dst_addr, size_t size, |
|||
KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm, |
|||
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr, |
|||
KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm, |
|||
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr); |
|||
|
|||
Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr, |
|||
KPageTableBase& src_page_table, KMemoryPermission test_perm, |
|||
KMemoryState dst_state, bool send); |
|||
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); |
|||
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); |
|||
|
|||
Result MapPhysicalMemory(KProcessAddress address, size_t size); |
|||
Result UnmapPhysicalMemory(KProcessAddress address, size_t size); |
|||
|
|||
Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size); |
|||
Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size); |
|||
|
|||
Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, KPageTableBase& src_pt, |
|||
KProcessAddress src_address); |
|||
|
|||
public: |
|||
KProcessAddress GetAddressSpaceStart() const { |
|||
return m_address_space_start; |
|||
} |
|||
KProcessAddress GetHeapRegionStart() const { |
|||
return m_heap_region_start; |
|||
} |
|||
KProcessAddress GetAliasRegionStart() const { |
|||
return m_alias_region_start; |
|||
} |
|||
KProcessAddress GetStackRegionStart() const { |
|||
return m_stack_region_start; |
|||
} |
|||
KProcessAddress GetKernelMapRegionStart() const { |
|||
return m_kernel_map_region_start; |
|||
} |
|||
KProcessAddress GetCodeRegionStart() const { |
|||
return m_code_region_start; |
|||
} |
|||
KProcessAddress GetAliasCodeRegionStart() const { |
|||
return m_alias_code_region_start; |
|||
} |
|||
|
|||
size_t GetAddressSpaceSize() const { |
|||
return m_address_space_end - m_address_space_start; |
|||
} |
|||
size_t GetHeapRegionSize() const { |
|||
return m_heap_region_end - m_heap_region_start; |
|||
} |
|||
size_t GetAliasRegionSize() const { |
|||
return m_alias_region_end - m_alias_region_start; |
|||
} |
|||
size_t GetStackRegionSize() const { |
|||
return m_stack_region_end - m_stack_region_start; |
|||
} |
|||
size_t GetKernelMapRegionSize() const { |
|||
return m_kernel_map_region_end - m_kernel_map_region_start; |
|||
} |
|||
size_t GetCodeRegionSize() const { |
|||
return m_code_region_end - m_code_region_start; |
|||
} |
|||
size_t GetAliasCodeRegionSize() const { |
|||
return m_alias_code_region_end - m_alias_code_region_start; |
|||
} |
|||
|
|||
size_t GetNormalMemorySize() const { |
|||
// Lock the table. |
|||
KScopedLightLock lk(m_general_lock); |
|||
|
|||
return (m_current_heap_end - m_heap_region_start) + m_mapped_physical_memory_size; |
|||
} |
|||
|
|||
size_t GetCodeSize() const; |
|||
size_t GetCodeDataSize() const; |
|||
size_t GetAliasCodeSize() const; |
|||
size_t GetAliasCodeDataSize() const; |
|||
|
|||
u32 GetAllocateOption() const { |
|||
return m_allocate_option; |
|||
} |
|||
|
|||
u32 GetAddressSpaceWidth() const { |
|||
return m_address_space_width; |
|||
} |
|||
|
|||
public: |
|||
// Linear mapped |
|||
static u8* GetLinearMappedVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) { |
|||
return kernel.System().DeviceMemory().GetPointer<u8>(addr); |
|||
} |
|||
|
|||
static KPhysicalAddress GetLinearMappedPhysicalAddress(KernelCore& kernel, |
|||
KVirtualAddress addr) { |
|||
return kernel.MemoryLayout().GetLinearPhysicalAddress(addr); |
|||
} |
|||
|
|||
static KVirtualAddress GetLinearMappedVirtualAddress(KernelCore& kernel, |
|||
KPhysicalAddress addr) { |
|||
return kernel.MemoryLayout().GetLinearVirtualAddress(addr); |
|||
} |
|||
|
|||
// Heap |
|||
static u8* GetHeapVirtualPointer(KernelCore& kernel, KPhysicalAddress addr) { |
|||
return kernel.System().DeviceMemory().GetPointer<u8>(addr); |
|||
} |
|||
|
|||
static KPhysicalAddress GetHeapPhysicalAddress(KernelCore& kernel, KVirtualAddress addr) { |
|||
return GetLinearMappedPhysicalAddress(kernel, addr); |
|||
} |
|||
|
|||
static KVirtualAddress GetHeapVirtualAddress(KernelCore& kernel, KPhysicalAddress addr) { |
|||
return GetLinearMappedVirtualAddress(kernel, addr); |
|||
} |
|||
|
|||
// Member heap |
|||
u8* GetHeapVirtualPointer(KPhysicalAddress addr) { |
|||
return GetHeapVirtualPointer(m_kernel, addr); |
|||
} |
|||
|
|||
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) { |
|||
return GetHeapPhysicalAddress(m_kernel, addr); |
|||
} |
|||
|
|||
KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) { |
|||
return GetHeapVirtualAddress(m_kernel, addr); |
|||
} |
|||
|
|||
// TODO: GetPageTableVirtualAddress |
|||
// TODO: GetPageTablePhysicalAddress |
|||
}; |
|||
|
|||
} // namespace Kernel |
|||
@ -0,0 +1,480 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "core/hle/kernel/k_page_table.h" |
|||
#include "core/hle/kernel/k_scoped_lock.h" |
|||
#include "core/hle/kernel/svc_types.h" |
|||
|
|||
namespace Core { |
|||
class ARM_Interface; |
|||
} |
|||
|
|||
namespace Kernel { |
|||
|
|||
class KProcessPageTable { |
|||
private: |
|||
KPageTable m_page_table; |
|||
|
|||
public: |
|||
KProcessPageTable(KernelCore& kernel) : m_page_table(kernel) {} |
|||
|
|||
Result Initialize(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, |
|||
bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, |
|||
size_t code_size, KSystemResource* system_resource, |
|||
KResourceLimit* resource_limit, Core::Memory::Memory& memory) { |
|||
R_RETURN(m_page_table.InitializeForProcess(as_type, enable_aslr, enable_das_merge, |
|||
from_back, pool, code_address, code_size, |
|||
system_resource, resource_limit, memory)); |
|||
} |
|||
|
|||
void Finalize() { |
|||
m_page_table.Finalize(); |
|||
} |
|||
|
|||
Core::Memory::Memory& GetMemory() { |
|||
return m_page_table.GetMemory(); |
|||
} |
|||
|
|||
Core::Memory::Memory& GetMemory() const { |
|||
return m_page_table.GetMemory(); |
|||
} |
|||
|
|||
Common::PageTable& GetImpl() { |
|||
return m_page_table.GetImpl(); |
|||
} |
|||
|
|||
Common::PageTable& GetImpl() const { |
|||
return m_page_table.GetImpl(); |
|||
} |
|||
|
|||
size_t GetNumGuardPages() const { |
|||
return m_page_table.GetNumGuardPages(); |
|||
} |
|||
|
|||
KScopedLightLock AcquireDeviceMapLock() { |
|||
return m_page_table.AcquireDeviceMapLock(); |
|||
} |
|||
|
|||
Result SetMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission perm) { |
|||
R_RETURN(m_page_table.SetMemoryPermission(addr, size, perm)); |
|||
} |
|||
|
|||
Result SetProcessMemoryPermission(KProcessAddress addr, size_t size, |
|||
Svc::MemoryPermission perm) { |
|||
R_RETURN(m_page_table.SetProcessMemoryPermission(addr, size, perm)); |
|||
} |
|||
|
|||
Result SetMemoryAttribute(KProcessAddress addr, size_t size, KMemoryAttribute mask, |
|||
KMemoryAttribute attr) { |
|||
R_RETURN(m_page_table.SetMemoryAttribute(addr, size, mask, attr)); |
|||
} |
|||
|
|||
Result SetHeapSize(KProcessAddress* out, size_t size) { |
|||
R_RETURN(m_page_table.SetHeapSize(out, size)); |
|||
} |
|||
|
|||
Result SetMaxHeapSize(size_t size) { |
|||
R_RETURN(m_page_table.SetMaxHeapSize(size)); |
|||
} |
|||
|
|||
Result QueryInfo(KMemoryInfo* out_info, Svc::PageInfo* out_page_info, |
|||
KProcessAddress addr) const { |
|||
R_RETURN(m_page_table.QueryInfo(out_info, out_page_info, addr)); |
|||
} |
|||
|
|||
Result QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out, KProcessAddress address) { |
|||
R_RETURN(m_page_table.QueryPhysicalAddress(out, address)); |
|||
} |
|||
|
|||
Result QueryStaticMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) { |
|||
R_RETURN(m_page_table.QueryStaticMapping(out, address, size)); |
|||
} |
|||
|
|||
Result QueryIoMapping(KProcessAddress* out, KPhysicalAddress address, size_t size) { |
|||
R_RETURN(m_page_table.QueryIoMapping(out, address, size)); |
|||
} |
|||
|
|||
Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { |
|||
R_RETURN(m_page_table.MapMemory(dst_address, src_address, size)); |
|||
} |
|||
|
|||
Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { |
|||
R_RETURN(m_page_table.UnmapMemory(dst_address, src_address, size)); |
|||
} |
|||
|
|||
Result MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { |
|||
R_RETURN(m_page_table.MapCodeMemory(dst_address, src_address, size)); |
|||
} |
|||
|
|||
Result UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { |
|||
R_RETURN(m_page_table.UnmapCodeMemory(dst_address, src_address, size)); |
|||
} |
|||
|
|||
Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { |
|||
R_RETURN(m_page_table.MapIo(phys_addr, size, perm)); |
|||
} |
|||
|
|||
Result MapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, |
|||
Svc::MemoryMapping mapping, Svc::MemoryPermission perm) { |
|||
R_RETURN(m_page_table.MapIoRegion(dst_address, phys_addr, size, mapping, perm)); |
|||
} |
|||
|
|||
Result UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddress phys_addr, size_t size, |
|||
Svc::MemoryMapping mapping) { |
|||
R_RETURN(m_page_table.UnmapIoRegion(dst_address, phys_addr, size, mapping)); |
|||
} |
|||
|
|||
Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { |
|||
R_RETURN(m_page_table.MapStatic(phys_addr, size, perm)); |
|||
} |
|||
|
|||
Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) { |
|||
R_RETURN(m_page_table.MapRegion(region_type, perm)); |
|||
} |
|||
|
|||
Result MapInsecureMemory(KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.MapInsecureMemory(address, size)); |
|||
} |
|||
|
|||
Result UnmapInsecureMemory(KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.UnmapInsecureMemory(address, size)); |
|||
} |
|||
|
|||
Result MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state, |
|||
KMemoryPermission perm) { |
|||
R_RETURN(m_page_table.MapPageGroup(addr, pg, state, perm)); |
|||
} |
|||
|
|||
Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state) { |
|||
R_RETURN(m_page_table.UnmapPageGroup(address, pg, state)); |
|||
} |
|||
|
|||
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, |
|||
KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) { |
|||
R_RETURN(m_page_table.MapPages(out_addr, num_pages, alignment, phys_addr, state, perm)); |
|||
} |
|||
|
|||
Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state, |
|||
KMemoryPermission perm) { |
|||
R_RETURN(m_page_table.MapPages(out_addr, num_pages, state, perm)); |
|||
} |
|||
|
|||
Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, |
|||
KMemoryPermission perm) { |
|||
R_RETURN(m_page_table.MapPages(address, num_pages, state, perm)); |
|||
} |
|||
|
|||
Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) { |
|||
R_RETURN(m_page_table.UnmapPages(addr, num_pages, state)); |
|||
} |
|||
|
|||
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, |
|||
KMemoryState state_mask, KMemoryState state, |
|||
KMemoryPermission perm_mask, KMemoryPermission perm, |
|||
KMemoryAttribute attr_mask, KMemoryAttribute attr) { |
|||
R_RETURN(m_page_table.MakeAndOpenPageGroup(out, address, num_pages, state_mask, state, |
|||
perm_mask, perm, attr_mask, attr)); |
|||
} |
|||
|
|||
Result InvalidateProcessDataCache(KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.InvalidateProcessDataCache(address, size)); |
|||
} |
|||
|
|||
Result ReadDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { |
|||
R_RETURN(m_page_table.ReadDebugMemory(dst_address, src_address, size)); |
|||
} |
|||
|
|||
Result ReadDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size, |
|||
KMemoryState state) { |
|||
R_RETURN(m_page_table.ReadDebugIoMemory(dst_address, src_address, size, state)); |
|||
} |
|||
|
|||
Result WriteDebugMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { |
|||
R_RETURN(m_page_table.WriteDebugMemory(dst_address, src_address, size)); |
|||
} |
|||
|
|||
Result WriteDebugIoMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size, |
|||
KMemoryState state) { |
|||
R_RETURN(m_page_table.WriteDebugIoMemory(dst_address, src_address, size, state)); |
|||
} |
|||
|
|||
Result LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, size_t size, |
|||
KMemoryPermission perm, bool is_aligned, bool check_heap) { |
|||
R_RETURN(m_page_table.LockForMapDeviceAddressSpace(out_is_io, address, size, perm, |
|||
is_aligned, check_heap)); |
|||
} |
|||
|
|||
Result LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, bool check_heap) { |
|||
R_RETURN(m_page_table.LockForUnmapDeviceAddressSpace(address, size, check_heap)); |
|||
} |
|||
|
|||
Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.UnlockForDeviceAddressSpace(address, size)); |
|||
} |
|||
|
|||
Result UnlockForDeviceAddressSpacePartialMap(KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.UnlockForDeviceAddressSpacePartialMap(address, size)); |
|||
} |
|||
|
|||
Result OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::MemoryRange* out, |
|||
KProcessAddress address, size_t size, |
|||
KMemoryPermission perm, bool is_aligned) { |
|||
R_RETURN(m_page_table.OpenMemoryRangeForMapDeviceAddressSpace(out, address, size, perm, |
|||
is_aligned)); |
|||
} |
|||
|
|||
Result OpenMemoryRangeForUnmapDeviceAddressSpace(KPageTableBase::MemoryRange* out, |
|||
KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.OpenMemoryRangeForUnmapDeviceAddressSpace(out, address, size)); |
|||
} |
|||
|
|||
Result LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.LockForIpcUserBuffer(out, address, size)); |
|||
} |
|||
|
|||
Result UnlockForIpcUserBuffer(KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.UnlockForIpcUserBuffer(address, size)); |
|||
} |
|||
|
|||
Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, |
|||
KMemoryPermission perm) { |
|||
R_RETURN(m_page_table.LockForTransferMemory(out, address, size, perm)); |
|||
} |
|||
|
|||
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg) { |
|||
R_RETURN(m_page_table.UnlockForTransferMemory(address, size, pg)); |
|||
} |
|||
|
|||
Result LockForCodeMemory(KPageGroup* out, KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.LockForCodeMemory(out, address, size)); |
|||
} |
|||
|
|||
Result UnlockForCodeMemory(KProcessAddress address, size_t size, const KPageGroup& pg) { |
|||
R_RETURN(m_page_table.UnlockForCodeMemory(address, size, pg)); |
|||
} |
|||
|
|||
Result OpenMemoryRangeForProcessCacheOperation(KPageTableBase::MemoryRange* out, |
|||
KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.OpenMemoryRangeForProcessCacheOperation(out, address, size)); |
|||
} |
|||
|
|||
Result CopyMemoryFromLinearToUser(KProcessAddress dst_addr, size_t size, |
|||
KProcessAddress src_addr, KMemoryState src_state_mask, |
|||
KMemoryState src_state, KMemoryPermission src_test_perm, |
|||
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) { |
|||
R_RETURN(m_page_table.CopyMemoryFromLinearToUser(dst_addr, size, src_addr, src_state_mask, |
|||
src_state, src_test_perm, src_attr_mask, |
|||
src_attr)); |
|||
} |
|||
|
|||
Result CopyMemoryFromLinearToKernel(void* dst_addr, size_t size, KProcessAddress src_addr, |
|||
KMemoryState src_state_mask, KMemoryState src_state, |
|||
KMemoryPermission src_test_perm, |
|||
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) { |
|||
R_RETURN(m_page_table.CopyMemoryFromLinearToKernel(dst_addr, size, src_addr, src_state_mask, |
|||
src_state, src_test_perm, src_attr_mask, |
|||
src_attr)); |
|||
} |
|||
|
|||
Result CopyMemoryFromUserToLinear(KProcessAddress dst_addr, size_t size, |
|||
KMemoryState dst_state_mask, KMemoryState dst_state, |
|||
KMemoryPermission dst_test_perm, |
|||
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, |
|||
KProcessAddress src_addr) { |
|||
R_RETURN(m_page_table.CopyMemoryFromUserToLinear(dst_addr, size, dst_state_mask, dst_state, |
|||
dst_test_perm, dst_attr_mask, dst_attr, |
|||
src_addr)); |
|||
} |
|||
|
|||
Result CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, size_t size, |
|||
KMemoryState dst_state_mask, KMemoryState dst_state, |
|||
KMemoryPermission dst_test_perm, |
|||
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, |
|||
void* src_addr) { |
|||
R_RETURN(m_page_table.CopyMemoryFromKernelToLinear(dst_addr, size, dst_state_mask, |
|||
dst_state, dst_test_perm, dst_attr_mask, |
|||
dst_attr, src_addr)); |
|||
} |
|||
|
|||
Result CopyMemoryFromHeapToHeap(KProcessPageTable& dst_page_table, KProcessAddress dst_addr, |
|||
size_t size, KMemoryState dst_state_mask, |
|||
KMemoryState dst_state, KMemoryPermission dst_test_perm, |
|||
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, |
|||
KProcessAddress src_addr, KMemoryState src_state_mask, |
|||
KMemoryState src_state, KMemoryPermission src_test_perm, |
|||
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) { |
|||
R_RETURN(m_page_table.CopyMemoryFromHeapToHeap( |
|||
dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm, |
|||
dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm, |
|||
src_attr_mask, src_attr)); |
|||
} |
|||
|
|||
Result CopyMemoryFromHeapToHeapWithoutCheckDestination( |
|||
KProcessPageTable& dst_page_table, KProcessAddress dst_addr, size_t size, |
|||
KMemoryState dst_state_mask, KMemoryState dst_state, KMemoryPermission dst_test_perm, |
|||
KMemoryAttribute dst_attr_mask, KMemoryAttribute dst_attr, KProcessAddress src_addr, |
|||
KMemoryState src_state_mask, KMemoryState src_state, KMemoryPermission src_test_perm, |
|||
KMemoryAttribute src_attr_mask, KMemoryAttribute src_attr) { |
|||
R_RETURN(m_page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination( |
|||
dst_page_table.m_page_table, dst_addr, size, dst_state_mask, dst_state, dst_test_perm, |
|||
dst_attr_mask, dst_attr, src_addr, src_state_mask, src_state, src_test_perm, |
|||
src_attr_mask, src_attr)); |
|||
} |
|||
|
|||
Result SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr, |
|||
KProcessPageTable& src_page_table, KMemoryPermission test_perm, |
|||
KMemoryState dst_state, bool send) { |
|||
R_RETURN(m_page_table.SetupForIpc(out_dst_addr, size, src_addr, src_page_table.m_page_table, |
|||
test_perm, dst_state, send)); |
|||
} |
|||
|
|||
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state) { |
|||
R_RETURN(m_page_table.CleanupForIpcServer(address, size, dst_state)); |
|||
} |
|||
|
|||
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state) { |
|||
R_RETURN(m_page_table.CleanupForIpcClient(address, size, dst_state)); |
|||
} |
|||
|
|||
Result MapPhysicalMemory(KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.MapPhysicalMemory(address, size)); |
|||
} |
|||
|
|||
Result UnmapPhysicalMemory(KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.UnmapPhysicalMemory(address, size)); |
|||
} |
|||
|
|||
Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.MapPhysicalMemoryUnsafe(address, size)); |
|||
} |
|||
|
|||
Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size) { |
|||
R_RETURN(m_page_table.UnmapPhysicalMemoryUnsafe(address, size)); |
|||
} |
|||
|
|||
Result UnmapProcessMemory(KProcessAddress dst_address, size_t size, |
|||
KProcessPageTable& src_page_table, KProcessAddress src_address) { |
|||
R_RETURN(m_page_table.UnmapProcessMemory(dst_address, size, src_page_table.m_page_table, |
|||
src_address)); |
|||
} |
|||
|
|||
bool GetPhysicalAddress(KPhysicalAddress* out, KProcessAddress address) { |
|||
return m_page_table.GetPhysicalAddress(out, address); |
|||
} |
|||
|
|||
bool Contains(KProcessAddress addr, size_t size) const { |
|||
return m_page_table.Contains(addr, size); |
|||
} |
|||
|
|||
bool IsInAliasRegion(KProcessAddress addr, size_t size) const { |
|||
return m_page_table.IsInAliasRegion(addr, size); |
|||
} |
|||
bool IsInHeapRegion(KProcessAddress addr, size_t size) const { |
|||
return m_page_table.IsInHeapRegion(addr, size); |
|||
} |
|||
bool IsInUnsafeAliasRegion(KProcessAddress addr, size_t size) const { |
|||
return m_page_table.IsInUnsafeAliasRegion(addr, size); |
|||
} |
|||
|
|||
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { |
|||
return m_page_table.CanContain(addr, size, state); |
|||
} |
|||
|
|||
KProcessAddress GetAddressSpaceStart() const { |
|||
return m_page_table.GetAddressSpaceStart(); |
|||
} |
|||
KProcessAddress GetHeapRegionStart() const { |
|||
return m_page_table.GetHeapRegionStart(); |
|||
} |
|||
KProcessAddress GetAliasRegionStart() const { |
|||
return m_page_table.GetAliasRegionStart(); |
|||
} |
|||
KProcessAddress GetStackRegionStart() const { |
|||
return m_page_table.GetStackRegionStart(); |
|||
} |
|||
KProcessAddress GetKernelMapRegionStart() const { |
|||
return m_page_table.GetKernelMapRegionStart(); |
|||
} |
|||
KProcessAddress GetCodeRegionStart() const { |
|||
return m_page_table.GetCodeRegionStart(); |
|||
} |
|||
KProcessAddress GetAliasCodeRegionStart() const { |
|||
return m_page_table.GetAliasCodeRegionStart(); |
|||
} |
|||
|
|||
size_t GetAddressSpaceSize() const { |
|||
return m_page_table.GetAddressSpaceSize(); |
|||
} |
|||
size_t GetHeapRegionSize() const { |
|||
return m_page_table.GetHeapRegionSize(); |
|||
} |
|||
size_t GetAliasRegionSize() const { |
|||
return m_page_table.GetAliasRegionSize(); |
|||
} |
|||
size_t GetStackRegionSize() const { |
|||
return m_page_table.GetStackRegionSize(); |
|||
} |
|||
size_t GetKernelMapRegionSize() const { |
|||
return m_page_table.GetKernelMapRegionSize(); |
|||
} |
|||
size_t GetCodeRegionSize() const { |
|||
return m_page_table.GetCodeRegionSize(); |
|||
} |
|||
size_t GetAliasCodeRegionSize() const { |
|||
return m_page_table.GetAliasCodeRegionSize(); |
|||
} |
|||
|
|||
size_t GetNormalMemorySize() const { |
|||
return m_page_table.GetNormalMemorySize(); |
|||
} |
|||
|
|||
size_t GetCodeSize() const { |
|||
return m_page_table.GetCodeSize(); |
|||
} |
|||
size_t GetCodeDataSize() const { |
|||
return m_page_table.GetCodeDataSize(); |
|||
} |
|||
|
|||
size_t GetAliasCodeSize() const { |
|||
return m_page_table.GetAliasCodeSize(); |
|||
} |
|||
size_t GetAliasCodeDataSize() const { |
|||
return m_page_table.GetAliasCodeDataSize(); |
|||
} |
|||
|
|||
u32 GetAllocateOption() const { |
|||
return m_page_table.GetAllocateOption(); |
|||
} |
|||
|
|||
u32 GetAddressSpaceWidth() const { |
|||
return m_page_table.GetAddressSpaceWidth(); |
|||
} |
|||
|
|||
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) { |
|||
return m_page_table.GetHeapPhysicalAddress(address); |
|||
} |
|||
|
|||
u8* GetHeapVirtualPointer(KPhysicalAddress address) { |
|||
return m_page_table.GetHeapVirtualPointer(address); |
|||
} |
|||
|
|||
KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress address) { |
|||
return m_page_table.GetHeapVirtualAddress(address); |
|||
} |
|||
|
|||
KBlockInfoManager* GetBlockInfoManager() { |
|||
return m_page_table.GetBlockInfoManager(); |
|||
} |
|||
|
|||
KPageTable& GetBasePageTable() { |
|||
return m_page_table; |
|||
} |
|||
|
|||
const KPageTable& GetBasePageTable() const { |
|||
return m_page_table; |
|||
} |
|||
}; |
|||
|
|||
} // namespace Kernel |
|||
@ -1,389 +0,0 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include <bit>
|
|||
|
|||
#include "common/bit_util.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "core/hle/kernel/k_handle_table.h"
|
|||
#include "core/hle/kernel/k_page_table.h"
|
|||
#include "core/hle/kernel/process_capability.h"
|
|||
#include "core/hle/kernel/svc_results.h"
|
|||
|
|||
namespace Kernel { |
|||
namespace { |
|||
|
|||
// clang-format off
|
|||
|
|||
// Shift offsets for kernel capability types.
|
|||
enum : u32 { |
|||
CapabilityOffset_PriorityAndCoreNum = 3, |
|||
CapabilityOffset_Syscall = 4, |
|||
CapabilityOffset_MapPhysical = 6, |
|||
CapabilityOffset_MapIO = 7, |
|||
CapabilityOffset_MapRegion = 10, |
|||
CapabilityOffset_Interrupt = 11, |
|||
CapabilityOffset_ProgramType = 13, |
|||
CapabilityOffset_KernelVersion = 14, |
|||
CapabilityOffset_HandleTableSize = 15, |
|||
CapabilityOffset_Debug = 16, |
|||
}; |
|||
|
|||
// Combined mask of all parameters that may be initialized only once.
|
|||
constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) | |
|||
(1U << CapabilityOffset_ProgramType) | |
|||
(1U << CapabilityOffset_KernelVersion) | |
|||
(1U << CapabilityOffset_HandleTableSize) | |
|||
(1U << CapabilityOffset_Debug); |
|||
|
|||
// Packed kernel version indicating 10.4.0
|
|||
constexpr u32 PackedKernelVersion = 0x520000; |
|||
|
|||
// Indicates possible types of capabilities that can be specified.
|
|||
enum class CapabilityType : u32 { |
|||
Unset = 0U, |
|||
PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1, |
|||
Syscall = (1U << CapabilityOffset_Syscall) - 1, |
|||
MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1, |
|||
MapIO = (1U << CapabilityOffset_MapIO) - 1, |
|||
MapRegion = (1U << CapabilityOffset_MapRegion) - 1, |
|||
Interrupt = (1U << CapabilityOffset_Interrupt) - 1, |
|||
ProgramType = (1U << CapabilityOffset_ProgramType) - 1, |
|||
KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1, |
|||
HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1, |
|||
Debug = (1U << CapabilityOffset_Debug) - 1, |
|||
Ignorable = 0xFFFFFFFFU, |
|||
}; |
|||
|
|||
// clang-format on
|
|||
|
|||
constexpr CapabilityType GetCapabilityType(u32 value) { |
|||
return static_cast<CapabilityType>((~value & (value + 1)) - 1); |
|||
} |
|||
|
|||
u32 GetFlagBitOffset(CapabilityType type) { |
|||
const auto value = static_cast<u32>(type); |
|||
return static_cast<u32>(Common::BitSize<u32>() - static_cast<u32>(std::countl_zero(value))); |
|||
} |
|||
|
|||
} // Anonymous namespace
|
|||
|
|||
Result ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities, |
|||
std::size_t num_capabilities, |
|||
KPageTable& page_table) { |
|||
Clear(); |
|||
|
|||
// Allow all cores and priorities.
|
|||
core_mask = 0xF; |
|||
priority_mask = 0xFFFFFFFFFFFFFFFF; |
|||
kernel_version = PackedKernelVersion; |
|||
|
|||
return ParseCapabilities(capabilities, num_capabilities, page_table); |
|||
} |
|||
|
|||
Result ProcessCapabilities::InitializeForUserProcess(const u32* capabilities, |
|||
std::size_t num_capabilities, |
|||
KPageTable& page_table) { |
|||
Clear(); |
|||
|
|||
return ParseCapabilities(capabilities, num_capabilities, page_table); |
|||
} |
|||
|
|||
void ProcessCapabilities::InitializeForMetadatalessProcess() { |
|||
// Allow all cores and priorities
|
|||
core_mask = 0xF; |
|||
priority_mask = 0xFFFFFFFFFFFFFFFF; |
|||
kernel_version = PackedKernelVersion; |
|||
|
|||
// Allow all system calls and interrupts.
|
|||
svc_capabilities.set(); |
|||
interrupt_capabilities.set(); |
|||
|
|||
// Allow using the maximum possible amount of handles
|
|||
handle_table_size = static_cast<s32>(KHandleTable::MaxTableSize); |
|||
|
|||
// Allow all debugging capabilities.
|
|||
is_debuggable = true; |
|||
can_force_debug = true; |
|||
} |
|||
|
|||
Result ProcessCapabilities::ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, |
|||
KPageTable& page_table) { |
|||
u32 set_flags = 0; |
|||
u32 set_svc_bits = 0; |
|||
|
|||
for (std::size_t i = 0; i < num_capabilities; ++i) { |
|||
const u32 descriptor = capabilities[i]; |
|||
const auto type = GetCapabilityType(descriptor); |
|||
|
|||
if (type == CapabilityType::MapPhysical) { |
|||
i++; |
|||
|
|||
// The MapPhysical type uses two descriptor flags for its parameters.
|
|||
// If there's only one, then there's a problem.
|
|||
if (i >= num_capabilities) { |
|||
LOG_ERROR(Kernel, "Invalid combination! i={}", i); |
|||
return ResultInvalidCombination; |
|||
} |
|||
|
|||
const auto size_flags = capabilities[i]; |
|||
if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) { |
|||
LOG_ERROR(Kernel, "Invalid capability type! size_flags={}", size_flags); |
|||
return ResultInvalidCombination; |
|||
} |
|||
|
|||
const auto result = HandleMapPhysicalFlags(descriptor, size_flags, page_table); |
|||
if (result.IsError()) { |
|||
LOG_ERROR(Kernel, "Failed to map physical flags! descriptor={}, size_flags={}", |
|||
descriptor, size_flags); |
|||
return result; |
|||
} |
|||
} else { |
|||
const auto result = |
|||
ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, page_table); |
|||
if (result.IsError()) { |
|||
LOG_ERROR( |
|||
Kernel, |
|||
"Failed to parse capability flag! set_flags={}, set_svc_bits={}, descriptor={}", |
|||
set_flags, set_svc_bits, descriptor); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return ResultSuccess; |
|||
} |
|||
|
|||
Result ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, |
|||
KPageTable& page_table) { |
|||
const auto type = GetCapabilityType(flag); |
|||
|
|||
if (type == CapabilityType::Unset) { |
|||
return ResultInvalidArgument; |
|||
} |
|||
|
|||
// Bail early on ignorable entries, as one would expect,
|
|||
// ignorable descriptors can be ignored.
|
|||
if (type == CapabilityType::Ignorable) { |
|||
return ResultSuccess; |
|||
} |
|||
|
|||
// Ensure that the give flag hasn't already been initialized before.
|
|||
// If it has been, then bail.
|
|||
const u32 flag_length = GetFlagBitOffset(type); |
|||
const u32 set_flag = 1U << flag_length; |
|||
if ((set_flag & set_flags & InitializeOnceMask) != 0) { |
|||
LOG_ERROR(Kernel, |
|||
"Attempted to initialize flags that may only be initialized once. set_flags={}", |
|||
set_flags); |
|||
return ResultInvalidCombination; |
|||
} |
|||
set_flags |= set_flag; |
|||
|
|||
switch (type) { |
|||
case CapabilityType::PriorityAndCoreNum: |
|||
return HandlePriorityCoreNumFlags(flag); |
|||
case CapabilityType::Syscall: |
|||
return HandleSyscallFlags(set_svc_bits, flag); |
|||
case CapabilityType::MapIO: |
|||
return HandleMapIOFlags(flag, page_table); |
|||
case CapabilityType::MapRegion: |
|||
return HandleMapRegionFlags(flag, page_table); |
|||
case CapabilityType::Interrupt: |
|||
return HandleInterruptFlags(flag); |
|||
case CapabilityType::ProgramType: |
|||
return HandleProgramTypeFlags(flag); |
|||
case CapabilityType::KernelVersion: |
|||
return HandleKernelVersionFlags(flag); |
|||
case CapabilityType::HandleTableSize: |
|||
return HandleHandleTableFlags(flag); |
|||
case CapabilityType::Debug: |
|||
return HandleDebugFlags(flag); |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
LOG_ERROR(Kernel, "Invalid capability type! type={}", type); |
|||
return ResultInvalidArgument; |
|||
} |
|||
|
|||
void ProcessCapabilities::Clear() { |
|||
svc_capabilities.reset(); |
|||
interrupt_capabilities.reset(); |
|||
|
|||
core_mask = 0; |
|||
priority_mask = 0; |
|||
|
|||
handle_table_size = 0; |
|||
kernel_version = 0; |
|||
|
|||
program_type = ProgramType::SysModule; |
|||
|
|||
is_debuggable = false; |
|||
can_force_debug = false; |
|||
} |
|||
|
|||
Result ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { |
|||
if (priority_mask != 0 || core_mask != 0) { |
|||
LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}", |
|||
priority_mask, core_mask); |
|||
return ResultInvalidArgument; |
|||
} |
|||
|
|||
const u32 core_num_min = (flags >> 16) & 0xFF; |
|||
const u32 core_num_max = (flags >> 24) & 0xFF; |
|||
if (core_num_min > core_num_max) { |
|||
LOG_ERROR(Kernel, "Core min is greater than core max! core_num_min={}, core_num_max={}", |
|||
core_num_min, core_num_max); |
|||
return ResultInvalidCombination; |
|||
} |
|||
|
|||
const u32 priority_min = (flags >> 10) & 0x3F; |
|||
const u32 priority_max = (flags >> 4) & 0x3F; |
|||
if (priority_min > priority_max) { |
|||
LOG_ERROR(Kernel, |
|||
"Priority min is greater than priority max! priority_min={}, priority_max={}", |
|||
core_num_min, priority_max); |
|||
return ResultInvalidCombination; |
|||
} |
|||
|
|||
// The switch only has 4 usable cores.
|
|||
if (core_num_max >= 4) { |
|||
LOG_ERROR(Kernel, "Invalid max cores specified! core_num_max={}", core_num_max); |
|||
return ResultInvalidCoreId; |
|||
} |
|||
|
|||
const auto make_mask = [](u64 min, u64 max) { |
|||
const u64 range = max - min + 1; |
|||
const u64 mask = (1ULL << range) - 1; |
|||
|
|||
return mask << min; |
|||
}; |
|||
|
|||
core_mask = make_mask(core_num_min, core_num_max); |
|||
priority_mask = make_mask(priority_min, priority_max); |
|||
return ResultSuccess; |
|||
} |
|||
|
|||
Result ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { |
|||
const u32 index = flags >> 29; |
|||
const u32 svc_bit = 1U << index; |
|||
|
|||
// If we've already set this svc before, bail.
|
|||
if ((set_svc_bits & svc_bit) != 0) { |
|||
return ResultInvalidCombination; |
|||
} |
|||
set_svc_bits |= svc_bit; |
|||
|
|||
const u32 svc_mask = (flags >> 5) & 0xFFFFFF; |
|||
for (u32 i = 0; i < 24; ++i) { |
|||
const u32 svc_number = index * 24 + i; |
|||
|
|||
if ((svc_mask & (1U << i)) == 0) { |
|||
continue; |
|||
} |
|||
|
|||
svc_capabilities[svc_number] = true; |
|||
} |
|||
|
|||
return ResultSuccess; |
|||
} |
|||
|
|||
Result ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, |
|||
KPageTable& page_table) { |
|||
// TODO(Lioncache): Implement once the memory manager can handle this.
|
|||
return ResultSuccess; |
|||
} |
|||
|
|||
Result ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) { |
|||
// TODO(Lioncache): Implement once the memory manager can handle this.
|
|||
return ResultSuccess; |
|||
} |
|||
|
|||
Result ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) { |
|||
// TODO(Lioncache): Implement once the memory manager can handle this.
|
|||
return ResultSuccess; |
|||
} |
|||
|
|||
Result ProcessCapabilities::HandleInterruptFlags(u32 flags) { |
|||
constexpr u32 interrupt_ignore_value = 0x3FF; |
|||
const u32 interrupt0 = (flags >> 12) & 0x3FF; |
|||
const u32 interrupt1 = (flags >> 22) & 0x3FF; |
|||
|
|||
for (u32 interrupt : {interrupt0, interrupt1}) { |
|||
if (interrupt == interrupt_ignore_value) { |
|||
continue; |
|||
} |
|||
|
|||
// NOTE:
|
|||
// This should be checking a generic interrupt controller value
|
|||
// as part of the calculation, however, given we don't currently
|
|||
// emulate that, it's sufficient to mark every interrupt as defined.
|
|||
|
|||
if (interrupt >= interrupt_capabilities.size()) { |
|||
LOG_ERROR(Kernel, "Process interrupt capability is out of range! svc_number={}", |
|||
interrupt); |
|||
return ResultOutOfRange; |
|||
} |
|||
|
|||
interrupt_capabilities[interrupt] = true; |
|||
} |
|||
|
|||
return ResultSuccess; |
|||
} |
|||
|
|||
Result ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { |
|||
const u32 reserved = flags >> 17; |
|||
if (reserved != 0) { |
|||
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); |
|||
return ResultReservedUsed; |
|||
} |
|||
|
|||
program_type = static_cast<ProgramType>((flags >> 14) & 0b111); |
|||
return ResultSuccess; |
|||
} |
|||
|
|||
Result ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { |
|||
// Yes, the internal member variable is checked in the actual kernel here.
|
|||
// This might look odd for options that are only allowed to be initialized
|
|||
// just once, however the kernel has a separate initialization function for
|
|||
// kernel processes and userland processes. The kernel variant sets this
|
|||
// member variable ahead of time.
|
|||
|
|||
const u32 major_version = kernel_version >> 19; |
|||
|
|||
if (major_version != 0 || flags < 0x80000) { |
|||
LOG_ERROR(Kernel, |
|||
"Kernel version is non zero or flags are too small! major_version={}, flags={}", |
|||
major_version, flags); |
|||
return ResultInvalidArgument; |
|||
} |
|||
|
|||
kernel_version = flags; |
|||
return ResultSuccess; |
|||
} |
|||
|
|||
Result ProcessCapabilities::HandleHandleTableFlags(u32 flags) { |
|||
const u32 reserved = flags >> 26; |
|||
if (reserved != 0) { |
|||
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); |
|||
return ResultReservedUsed; |
|||
} |
|||
|
|||
handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF); |
|||
return ResultSuccess; |
|||
} |
|||
|
|||
Result ProcessCapabilities::HandleDebugFlags(u32 flags) { |
|||
const u32 reserved = flags >> 19; |
|||
if (reserved != 0) { |
|||
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved); |
|||
return ResultReservedUsed; |
|||
} |
|||
|
|||
is_debuggable = (flags & 0x20000) != 0; |
|||
can_force_debug = (flags & 0x40000) != 0; |
|||
return ResultSuccess; |
|||
} |
|||
|
|||
} // namespace Kernel
|
|||
@ -1,266 +0,0 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <bitset> |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
union Result; |
|||
|
|||
namespace Kernel { |
|||
|
|||
class KPageTable; |
|||
|
|||
/// The possible types of programs that may be indicated |
|||
/// by the program type capability descriptor. |
|||
enum class ProgramType { |
|||
SysModule, |
|||
Application, |
|||
Applet, |
|||
}; |
|||
|
|||
/// Handles kernel capability descriptors that are provided by |
|||
/// application metadata. These descriptors provide information |
|||
/// that alters certain parameters for kernel process instance |
|||
/// that will run said application (or applet). |
|||
/// |
|||
/// Capabilities are a sequence of flag descriptors, that indicate various |
|||
/// configurations and constraints for a particular process. |
|||
/// |
|||
/// Flag types are indicated by a sequence of set low bits. E.g. the |
|||
/// types are indicated with the low bits as follows (where x indicates "don't care"): |
|||
/// |
|||
/// - Priority and core mask : 0bxxxxxxxxxxxx0111 |
|||
/// - Allowed service call mask: 0bxxxxxxxxxxx01111 |
|||
/// - Map physical memory : 0bxxxxxxxxx0111111 |
|||
/// - Map IO memory : 0bxxxxxxxx01111111 |
|||
/// - Interrupts : 0bxxxx011111111111 |
|||
/// - Application type : 0bxx01111111111111 |
|||
/// - Kernel version : 0bx011111111111111 |
|||
/// - Handle table size : 0b0111111111111111 |
|||
/// - Debugger flags : 0b1111111111111111 |
|||
/// |
|||
/// These are essentially a bit offset subtracted by 1 to create a mask. |
|||
/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000) |
|||
/// subtracted by one (7 -> 0b0111) |
|||
/// |
|||
/// An example of a bit layout (using the map physical layout): |
|||
/// <example> |
|||
/// The MapPhysical type indicates a sequence entry pair of: |
|||
/// |
|||
/// [initial, memory_flags], where: |
|||
/// |
|||
/// initial: |
|||
/// bits: |
|||
/// 7-24: Starting page to map memory at. |
|||
/// 25 : Indicates if the memory should be mapped as read only. |
|||
/// |
|||
/// memory_flags: |
|||
/// bits: |
|||
/// 7-20 : Number of pages to map |
|||
/// 21-25: Seems to be reserved (still checked against though) |
|||
/// 26 : Whether or not the memory being mapped is IO memory, or physical memory |
|||
/// </example> |
|||
/// |
|||
class ProcessCapabilities { |
|||
public: |
|||
using InterruptCapabilities = std::bitset<1024>; |
|||
using SyscallCapabilities = std::bitset<192>; |
|||
|
|||
ProcessCapabilities() = default; |
|||
ProcessCapabilities(const ProcessCapabilities&) = delete; |
|||
ProcessCapabilities(ProcessCapabilities&&) = default; |
|||
|
|||
ProcessCapabilities& operator=(const ProcessCapabilities&) = delete; |
|||
ProcessCapabilities& operator=(ProcessCapabilities&&) = default; |
|||
|
|||
/// Initializes this process capabilities instance for a kernel process. |
|||
/// |
|||
/// @param capabilities The capabilities to parse |
|||
/// @param num_capabilities The number of capabilities to parse. |
|||
/// @param page_table The memory manager to use for handling any mapping-related |
|||
/// operations (such as mapping IO memory, etc). |
|||
/// |
|||
/// @returns ResultSuccess if this capabilities instance was able to be initialized, |
|||
/// otherwise, an error code upon failure. |
|||
/// |
|||
Result InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, |
|||
KPageTable& page_table); |
|||
|
|||
/// Initializes this process capabilities instance for a userland process. |
|||
/// |
|||
/// @param capabilities The capabilities to parse. |
|||
/// @param num_capabilities The total number of capabilities to parse. |
|||
/// @param page_table The memory manager to use for handling any mapping-related |
|||
/// operations (such as mapping IO memory, etc). |
|||
/// |
|||
/// @returns ResultSuccess if this capabilities instance was able to be initialized, |
|||
/// otherwise, an error code upon failure. |
|||
/// |
|||
Result InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, |
|||
KPageTable& page_table); |
|||
|
|||
/// Initializes this process capabilities instance for a process that does not |
|||
/// have any metadata to parse. |
|||
/// |
|||
/// This is necessary, as we allow running raw executables, and the internal |
|||
/// kernel process capabilities also determine what CPU cores the process is |
|||
/// allowed to run on, and what priorities are allowed for threads. It also |
|||
/// determines the max handle table size, what the program type is, whether or |
|||
/// not the process can be debugged, or whether it's possible for a process to |
|||
/// forcibly debug another process. |
|||
/// |
|||
/// Given the above, this essentially enables all capabilities across the board |
|||
/// for the process. It allows the process to: |
|||
/// |
|||
/// - Run on any core |
|||
/// - Use any thread priority |
|||
/// - Use the maximum amount of handles a process is allowed to. |
|||
/// - Be debuggable |
|||
/// - Forcibly debug other processes. |
|||
/// |
|||
/// Note that this is not a behavior that the kernel allows a process to do via |
|||
/// a single function like this. This is yuzu-specific behavior to handle |
|||
/// executables with no capability descriptors whatsoever to derive behavior from. |
|||
/// It being yuzu-specific is why this is also not the default behavior and not |
|||
/// done by default in the constructor. |
|||
/// |
|||
void InitializeForMetadatalessProcess(); |
|||
|
|||
/// Gets the allowable core mask |
|||
u64 GetCoreMask() const { |
|||
return core_mask; |
|||
} |
|||
|
|||
/// Gets the allowable priority mask |
|||
u64 GetPriorityMask() const { |
|||
return priority_mask; |
|||
} |
|||
|
|||
/// Gets the SVC access permission bits |
|||
const SyscallCapabilities& GetServiceCapabilities() const { |
|||
return svc_capabilities; |
|||
} |
|||
|
|||
/// Gets the valid interrupt bits. |
|||
const InterruptCapabilities& GetInterruptCapabilities() const { |
|||
return interrupt_capabilities; |
|||
} |
|||
|
|||
/// Gets the program type for this process. |
|||
ProgramType GetProgramType() const { |
|||
return program_type; |
|||
} |
|||
|
|||
/// Gets the number of total allowable handles for the process' handle table. |
|||
s32 GetHandleTableSize() const { |
|||
return handle_table_size; |
|||
} |
|||
|
|||
/// Gets the kernel version value. |
|||
u32 GetKernelVersion() const { |
|||
return kernel_version; |
|||
} |
|||
|
|||
/// Whether or not this process can be debugged. |
|||
bool IsDebuggable() const { |
|||
return is_debuggable; |
|||
} |
|||
|
|||
/// Whether or not this process can forcibly debug another |
|||
/// process, even if that process is not considered debuggable. |
|||
bool CanForceDebug() const { |
|||
return can_force_debug; |
|||
} |
|||
|
|||
private: |
|||
/// Attempts to parse a given sequence of capability descriptors. |
|||
/// |
|||
/// @param capabilities The sequence of capability descriptors to parse. |
|||
/// @param num_capabilities The number of descriptors within the given sequence. |
|||
/// @param page_table The memory manager that will perform any memory |
|||
/// mapping if necessary. |
|||
/// |
|||
/// @return ResultSuccess if no errors occur, otherwise an error code. |
|||
/// |
|||
Result ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, |
|||
KPageTable& page_table); |
|||
|
|||
/// Attempts to parse a capability descriptor that is only represented by a |
|||
/// single flag set. |
|||
/// |
|||
/// @param set_flags Running set of flags that are used to catch |
|||
/// flags being initialized more than once when they shouldn't be. |
|||
/// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask. |
|||
/// @param flag The flag to attempt to parse. |
|||
/// @param page_table The memory manager that will perform any memory |
|||
/// mapping if necessary. |
|||
/// |
|||
/// @return ResultSuccess if no errors occurred, otherwise an error code. |
|||
/// |
|||
Result ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, |
|||
KPageTable& page_table); |
|||
|
|||
/// Clears the internal state of this process capability instance. Necessary, |
|||
/// to have a sane starting point due to us allowing running executables without |
|||
/// configuration metadata. We assume a process is not going to have metadata, |
|||
/// and if it turns out that the process does, in fact, have metadata, then |
|||
/// we attempt to parse it. Thus, we need this to reset data members back to |
|||
/// a good state. |
|||
/// |
|||
/// DO NOT ever make this a public member function. This isn't an invariant |
|||
/// anything external should depend upon (and if anything comes to rely on it, |
|||
/// you should immediately be questioning the design of that thing, not this |
|||
/// class. If the kernel itself can run without depending on behavior like that, |
|||
/// then so can yuzu). |
|||
/// |
|||
void Clear(); |
|||
|
|||
/// Handles flags related to the priority and core number capability flags. |
|||
Result HandlePriorityCoreNumFlags(u32 flags); |
|||
|
|||
/// Handles flags related to determining the allowable SVC mask. |
|||
Result HandleSyscallFlags(u32& set_svc_bits, u32 flags); |
|||
|
|||
/// Handles flags related to mapping physical memory pages. |
|||
Result HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table); |
|||
|
|||
/// Handles flags related to mapping IO pages. |
|||
Result HandleMapIOFlags(u32 flags, KPageTable& page_table); |
|||
|
|||
/// Handles flags related to mapping physical memory regions. |
|||
Result HandleMapRegionFlags(u32 flags, KPageTable& page_table); |
|||
|
|||
/// Handles flags related to the interrupt capability flags. |
|||
Result HandleInterruptFlags(u32 flags); |
|||
|
|||
/// Handles flags related to the program type. |
|||
Result HandleProgramTypeFlags(u32 flags); |
|||
|
|||
/// Handles flags related to the handle table size. |
|||
Result HandleHandleTableFlags(u32 flags); |
|||
|
|||
/// Handles flags related to the kernel version capability flags. |
|||
Result HandleKernelVersionFlags(u32 flags); |
|||
|
|||
/// Handles flags related to debug-specific capabilities. |
|||
Result HandleDebugFlags(u32 flags); |
|||
|
|||
SyscallCapabilities svc_capabilities; |
|||
InterruptCapabilities interrupt_capabilities; |
|||
|
|||
u64 core_mask = 0; |
|||
u64 priority_mask = 0; |
|||
|
|||
s32 handle_table_size = 0; |
|||
u32 kernel_version = 0; |
|||
|
|||
ProgramType program_type = ProgramType::SysModule; |
|||
|
|||
bool is_debuggable = false; |
|||
bool can_force_debug = false; |
|||
}; |
|||
|
|||
} // namespace Kernel |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue