commit
40d4e9543b
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
|
Loading…
Reference in New Issue