From 05a3e958344201fba502039931c34dc1502ab635 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 19 Feb 2020 01:22:27 -0800 Subject: [PATCH] kern: implement process(pagetable) init --- .../arch/arm64/kern_k_page_table.hpp | 1 + .../arch/arm64/kern_k_page_table_impl.hpp | 1 + .../arch/arm64/kern_k_process_page_table.hpp | 13 + .../mesosphere/kern_k_capabilities.hpp | 4 +- .../mesosphere/kern_k_page_table_base.hpp | 26 +- .../include/mesosphere/kern_k_process.hpp | 4 + .../include/mesosphere/kern_svc.hpp | 1 - .../mesosphere/svc/kern_svc_prototypes.hpp | 2 + .../mesosphere/svc/kern_svc_tables.hpp | 1 - .../source/arch/arm64/kern_k_page_table.cpp | 110 ++++++++ .../arch/arm64/kern_k_page_table_impl.cpp | 6 + .../nintendo/nx/kern_k_device_page_table.cpp | 12 +- .../source/kern_initial_process.cpp | 4 +- .../source/kern_k_capabilities.cpp | 24 ++ .../source/kern_k_page_table_base.cpp | 255 +++++++++++++++++- .../libmesosphere/source/kern_k_process.cpp | 62 +++++ 16 files changed, 502 insertions(+), 24 deletions(-) create mode 100644 libraries/libmesosphere/source/kern_k_capabilities.cpp diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp index 5436e362b..ac0292d08 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table.hpp @@ -172,6 +172,7 @@ namespace ams::kern::arch::arm64 { } NOINLINE Result InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end); + NOINLINE Result InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager); Result Finalize(); private: Result Map(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll); diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table_impl.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table_impl.hpp index d8ad73d92..c1597bf22 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table_impl.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table_impl.hpp @@ -103,6 +103,7 @@ namespace ams::kern::arch::arm64 { constexpr KPageTableImpl() : table(), is_kernel(), num_entries() { /* ... */ } NOINLINE void InitializeForKernel(void *tb, KVirtualAddress start, KVirtualAddress end); + NOINLINE void InitializeForProcess(void *tb, KVirtualAddress start, KVirtualAddress end); L1PageTableEntry *Finalize(); bool BeginTraversal(TraversalEntry *out_entry, TraversalContext *out_context, KProcessAddress address) const; diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index 5d71ff8ea..25d1c999c 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp @@ -24,6 +24,19 @@ namespace ams::kern::arch::arm64 { KPageTable page_table; public: constexpr KProcessPageTable() : page_table() { /* ... */ } + + Result Initialize(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) { + return this->page_table.InitializeForProcess(id, as_type, enable_aslr, from_back, pool, code_address, code_size, mem_block_slab_manager, block_info_manager, pt_manager); + } + + void Finalize() { this->page_table.Finalize(); } + + Result MapPageGroup(KProcessAddress addr, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm) { + return this->page_table.MapPageGroup(addr, pg, state, perm); + } + + bool CanContain(KProcessAddress addr, size_t size) const { return this->page_table.CanContain(addr, size); } + bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { return this->page_table.CanContain(addr, size, state); } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp index 54b6796c0..c29c8bc10 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp @@ -34,7 +34,9 @@ namespace ams::kern { util::BitPack32 intended_kernel_version; u32 program_type{}; public: - constexpr KCapabilities() : svc_access_flags(), debug_capabilities(0), intended_kernel_version(0) { /* ... */ } + constexpr KCapabilities() : debug_capabilities(0), intended_kernel_version(0) { /* ... */ } + + Result Initialize(const u32 *caps, s32 num_caps, KProcessPageTable *page_table); constexpr u64 GetCoreMask() const { return this->core_mask; } constexpr u64 GetPriorityMask() const { return this->priority_mask; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index 006017426..f68e67936 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -53,6 +53,7 @@ namespace ams::kern { /* TODO: perm/attr operations */ }; + static constexpr size_t RegionAlignment = KernelAslrAlignment; struct PageLinkedList { private: @@ -87,6 +88,19 @@ namespace ams::kern { static_assert(std::is_trivially_destructible::value); static constexpr u32 DefaultMemoryIgnoreAttr = KMemoryAttribute_DontCareMask | KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared; + + static constexpr size_t GetAddressSpaceWidth(ams::svc::CreateProcessFlag as_type) { + switch (static_cast(as_type & ams::svc::CreateProcessFlag_AddressSpaceMask)) { + case ams::svc::CreateProcessFlag_AddressSpace64Bit: + return 39; + case ams::svc::CreateProcessFlag_AddressSpace64BitDeprecated: + return 36; + case ams::svc::CreateProcessFlag_AddressSpace32Bit: + case ams::svc::CreateProcessFlag_AddressSpace32BitWithoutAlias: + return 32; + MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); + } + } private: class KScopedPageTableUpdater { private: @@ -122,7 +136,7 @@ namespace ams::kern { KPageTableImpl impl; KMemoryBlockManager memory_block_manager; u32 allocate_option; - u32 address_space_size; + u32 address_space_width; bool is_kernel; bool enable_aslr; KMemoryBlockSlabManager *memory_block_slab_manager; @@ -139,7 +153,7 @@ namespace ams::kern { alias_region_start(), alias_region_end(), stack_region_start(), stack_region_end(), kernel_map_region_start(), kernel_map_region_end(), alias_code_region_start(), alias_code_region_end(), code_region_start(), code_region_end(), max_heap_size(), max_physical_memory_size(), general_lock(), map_physical_memory_lock(), impl(), memory_block_manager(), - allocate_option(), address_space_size(), is_kernel(), enable_aslr(), memory_block_slab_manager(), block_info_manager(), + allocate_option(), address_space_width(), is_kernel(), enable_aslr(), memory_block_slab_manager(), block_info_manager(), cached_physical_linear_region(), cached_physical_heap_region(), cached_virtual_heap_region(), heap_fill_value(), ipc_fill_value(), stack_fill_value() { @@ -147,23 +161,24 @@ namespace ams::kern { } NOINLINE Result InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end); + NOINLINE Result InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager); void Finalize(); constexpr bool IsKernel() const { return this->is_kernel; } constexpr bool IsAslrEnabled() const { return this->enable_aslr; } - constexpr bool Contains(KProcessAddress addr) const { + constexpr bool CanContain(KProcessAddress addr) const { return this->address_space_start <= addr && addr <= this->address_space_end - 1; } - constexpr bool Contains(KProcessAddress addr, size_t size) const { + constexpr bool CanContain(KProcessAddress addr, size_t size) const { return this->address_space_start <= addr && addr < addr + size && addr + size - 1 <= this->address_space_end - 1; } KProcessAddress GetRegionAddress(KMemoryState state) const; size_t GetRegionSize(KMemoryState state) const; - bool Contains(KProcessAddress addr, size_t size, KMemoryState state) const; + bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const; protected: virtual 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) = 0; virtual Result Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, const KPageGroup *page_group, const KPageProperties properties, OperationType operation, bool reuse_ll) = 0; @@ -231,6 +246,7 @@ namespace ams::kern { 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); public: static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 3b3d4ce55..a4a075f5d 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -115,10 +115,14 @@ namespace ams::kern { KMemoryBlockSlabManager memory_block_slab_manager{}; KBlockInfoManager block_info_manager{}; KPageTableManager page_table_manager{}; + private: + Result Initialize(const ams::svc::CreateProcessParameter ¶ms); public: constexpr KProcess() { /* ... */ } virtual ~KProcess() { /* ... */ } + Result Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool); + constexpr u64 GetProcessId() const { return this->process_id; } constexpr u64 GetCoreMask() const { return this->capabilities.GetCoreMask(); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_svc.hpp b/libraries/libmesosphere/include/mesosphere/kern_svc.hpp index 56073530c..251fcd02b 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_svc.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_svc.hpp @@ -17,4 +17,3 @@ #include #include #include -#include diff --git a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp index 66798bd8d..a67df3951 100644 --- a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp +++ b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_prototypes.hpp @@ -20,6 +20,8 @@ namespace ams::kern::svc { + static constexpr size_t NumSupervisorCalls = 0x80; + #define AMS_KERN_SVC_DECLARE_PROTOTYPE_64(ID, RETURN_TYPE, NAME, ...) \ NOINLINE RETURN_TYPE NAME##64(__VA_ARGS__); #define AMS_KERN_SVC_DECLARE_PROTOTYPE_64_FROM_32(ID, RETURN_TYPE, NAME, ...) \ diff --git a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_tables.hpp b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_tables.hpp index 39975c0ac..3119ba8b1 100644 --- a/libraries/libmesosphere/include/mesosphere/svc/kern_svc_tables.hpp +++ b/libraries/libmesosphere/include/mesosphere/svc/kern_svc_tables.hpp @@ -19,7 +19,6 @@ namespace ams::kern::svc { - static constexpr size_t NumSupervisorCalls = 0x80; using SvcTableEntry = void (*)(); /* TODO: 32-bit ABI */ diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp index 41dd67942..10828702c 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp @@ -17,6 +17,85 @@ namespace ams::kern::arch::arm64 { + namespace { + + constexpr u64 EncodeTtbr(KPhysicalAddress table, u8 asid) { + return (static_cast(asid) << 48) | (static_cast(GetInteger(table))); + } + + class KPageTableAsidManager { + private: + using WordType = u32; + static constexpr u8 ReservedAsids[] = { 0 }; + static constexpr size_t NumReservedAsids = util::size(ReservedAsids); + static constexpr size_t BitsPerWord = BITSIZEOF(WordType); + static constexpr size_t AsidCount = 0x100; + static constexpr size_t NumWords = AsidCount / BitsPerWord; + static constexpr WordType FullWord = ~WordType(0u); + private: + WordType state[NumWords]; + KLightLock lock; + u8 hint; + private: + constexpr bool TestImpl(u8 asid) const { + return this->state[asid / BitsPerWord] & (1u << (asid % BitsPerWord)); + } + constexpr void ReserveImpl(u8 asid) { + MESOSPHERE_ASSERT(!this->TestImpl(asid)); + this->state[asid / BitsPerWord] |= (1u << (asid % BitsPerWord)); + } + + constexpr void ReleaseImpl(u8 asid) { + MESOSPHERE_ASSERT(this->TestImpl(asid)); + this->state[asid / BitsPerWord] &= ~(1u << (asid % BitsPerWord)); + } + + constexpr u8 FindAvailable() const { + for (size_t i = 0; i < util::size(this->state); i++) { + if (this->state[i] == FullWord) { + continue; + } + const WordType clear_bit = (this->state[i] + 1) ^ (this->state[i]); + return BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit); + } + if (this->state[util::size(this->state)-1] == FullWord) { + MESOSPHERE_PANIC("Unable to reserve ASID"); + } + __builtin_unreachable(); + } + + static constexpr ALWAYS_INLINE WordType ClearLeadingZero(WordType value) { + return __builtin_clzll(value) - (BITSIZEOF(unsigned long long) - BITSIZEOF(WordType)); + } + public: + constexpr KPageTableAsidManager() : state(), lock(), hint() { + for (size_t i = 0; i < NumReservedAsids; i++) { + this->ReserveImpl(ReservedAsids[i]); + } + } + + u8 Reserve() { + KScopedLightLock lk(this->lock); + + if (this->TestImpl(this->hint)) { + this->hint = this->FindAvailable(); + } + + this->ReserveImpl(this->hint); + + return this->hint++; + } + + void Release(u8 asid) { + KScopedLightLock lk(this->lock); + this->ReleaseImpl(asid); + } + }; + + KPageTableAsidManager g_asid_manager; + + } + void KPageTable::Initialize(s32 core_id) { /* Nothing actually needed here. */ } @@ -39,6 +118,37 @@ namespace ams::kern::arch::arm64 { return ResultSuccess(); } + Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) { + /* Convert the address space type to a width. */ + + /* Get an ASID */ + this->asid = g_asid_manager.Reserve(); + auto asid_guard = SCOPE_GUARD { g_asid_manager.Release(this->asid); }; + + /* Set our manager. */ + this->manager = pt_manager; + + /* Allocate a new table, and set our ttbr value. */ + const KVirtualAddress new_table = this->manager->Allocate(); + R_UNLESS(new_table != Null, svc::ResultOutOfResource()); + this->ttbr = EncodeTtbr(GetPageTablePhysicalAddress(new_table), asid); + auto table_guard = SCOPE_GUARD { this->manager->Free(new_table); }; + + /* Initialize our base table. */ + const size_t as_width = GetAddressSpaceWidth(as_type); + const KProcessAddress as_start = 0; + const KProcessAddress as_end = (1ul << as_width); + R_TRY(KPageTableBase::InitializeForProcess(as_type, enable_aslr, from_back, pool, GetVoidPointer(new_table), as_start, as_end, code_address, code_size, mem_block_slab_manager, block_info_manager)); + + /* We succeeded! */ + table_guard.Cancel(); + asid_guard.Cancel(); + + /* Note that we've updated the table (since we created it). */ + this->NoteUpdated(); + return ResultSuccess(); + } + Result KPageTable::Finalize() { MESOSPHERE_TODO_IMPLEMENT(); } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table_impl.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table_impl.cpp index d83ce6774..e232942ee 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table_impl.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table_impl.cpp @@ -23,6 +23,12 @@ namespace ams::kern::arch::arm64 { this->num_entries = util::AlignUp(end - start, L1BlockSize) / L1BlockSize; } + void KPageTableImpl::InitializeForProcess(void *tb, KVirtualAddress start, KVirtualAddress end) { + this->table = static_cast(tb); + this->is_kernel = false; + this->num_entries = util::AlignUp(end - start, L1BlockSize) / L1BlockSize; + } + L1PageTableEntry *KPageTableImpl::Finalize() { return this->table; } diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp index d0943016a..874d234f7 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp @@ -217,7 +217,7 @@ namespace ams::kern::board::nintendo::nx { } }; - class KAsidManager { + class KDeviceAsidManager { private: using WordType = u32; static constexpr u8 ReservedAsids[] = { 0, 1, 2, 3 }; @@ -241,7 +241,7 @@ namespace ams::kern::board::nintendo::nx { return __builtin_clzll(value) - (BITSIZEOF(unsigned long long) - BITSIZEOF(WordType)); } public: - constexpr KAsidManager() : state(), lock() { + constexpr KDeviceAsidManager() : state(), lock() { for (size_t i = 0; i < NumReservedAsids; i++) { this->ReserveImpl(ReservedAsids[i]); } @@ -254,7 +254,7 @@ namespace ams::kern::board::nintendo::nx { size_t num_reserved = 0; for (size_t i = 0; i < NumWords; i++) { while (this->state[i] != FullWord) { - WordType clear_bit = (this->state[i] + 1) ^ (this->state[i]); + const WordType clear_bit = (this->state[i] + 1) ^ (this->state[i]); this->state[i] |= clear_bit; out[num_reserved++] = static_cast(BitsPerWord * i + BitsPerWord - 1 - ClearLeadingZero(clear_bit)); R_UNLESS(num_reserved != num_desired, ResultSuccess()); @@ -277,9 +277,9 @@ namespace ams::kern::board::nintendo::nx { /* Globals. */ KLightLock g_lock; u8 g_reserved_asid; - KPhysicalAddress g_memory_controller_address; - KPhysicalAddress g_reserved_table_phys_addr; - KAsidManager g_asid_manager; + KPhysicalAddress g_memory_controller_address; + KPhysicalAddress g_reserved_table_phys_addr; + KDeviceAsidManager g_asid_manager; /* Memory controller access functionality. */ void WriteMcRegister(size_t offset, u32 value) { diff --git a/libraries/libmesosphere/source/kern_initial_process.cpp b/libraries/libmesosphere/source/kern_initial_process.cpp index 7d18a96d7..8b7b12838 100644 --- a/libraries/libmesosphere/source/kern_initial_process.cpp +++ b/libraries/libmesosphere/source/kern_initial_process.cpp @@ -85,7 +85,7 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(new_process != nullptr); /* Initialize the process. */ - MESOSPHERE_TODO("Initialize the process"); + MESOSPHERE_R_ABORT_UNLESS(new_process->Initialize(params, pg, reader.GetCapabilities(), reader.GetNumCapabilities(), std::addressof(Kernel::GetSystemResourceLimit()), pool)); } } @@ -93,7 +93,7 @@ namespace ams::kern { MESOSPHERE_TODO("Set process's memory permissions"); /* Register the process. */ - MESOSPHERE_TODO("Register the process"); + KProcess::Register(new_process); /* Save the process info. */ infos[i].process = new_process; diff --git a/libraries/libmesosphere/source/kern_k_capabilities.cpp b/libraries/libmesosphere/source/kern_k_capabilities.cpp new file mode 100644 index 000000000..258332896 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_capabilities.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::kern { + + Result KCapabilities::Initialize(const u32 *caps, s32 num_caps, KProcessPageTable *page_table) { + MESOSPHERE_TODO_IMPLEMENT(); + } + +} diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index fd10073f6..8db3a060b 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -20,7 +20,7 @@ namespace ams::kern { Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) { /* Initialize our members. */ - this->address_space_size = (is_64_bit) ? BITSIZEOF(u64) : BITSIZEOF(u32); + this->address_space_width = (is_64_bit) ? BITSIZEOF(u64) : BITSIZEOF(u32); this->address_space_start = KProcessAddress(GetInteger(start)); this->address_space_end = KProcessAddress(GetInteger(end)); this->is_kernel = true; @@ -63,6 +63,213 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::InitializeForProcess(ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, void *table, KProcessAddress start, KProcessAddress end, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager) { + /* Validate the region. */ + MESOSPHERE_ABORT_UNLESS(start <= code_address); + MESOSPHERE_ABORT_UNLESS(code_address < code_address + code_size); + MESOSPHERE_ABORT_UNLESS(code_address + code_size - 1 <= end - 1); + + /* Declare variables to hold our region sizes. */ + + /* Define helpers. */ + auto GetSpaceStart = [&](KAddressSpaceInfo::Type type) ALWAYS_INLINE_LAMBDA { + return KAddressSpaceInfo::GetAddressSpaceStart(this->address_space_width, type); + }; + auto GetSpaceSize = [&](KAddressSpaceInfo::Type type) ALWAYS_INLINE_LAMBDA { + return KAddressSpaceInfo::GetAddressSpaceSize(this->address_space_width, type); + }; + + /* Set our width and heap/alias sizes. */ + this->address_space_width = GetAddressSpaceWidth(as_type); + size_t alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Alias); + size_t heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Heap); + + /* Adjust heap/alias size if we don't have an alias region. */ + if ((as_type & ams::svc::CreateProcessFlag_AddressSpaceMask) == ams::svc::CreateProcessFlag_AddressSpace32BitWithoutAlias) { + heap_region_size += alias_region_size; + alias_region_size = 0; + } + + /* Set code regions and determine remaining sizes. */ + KProcessAddress process_code_start; + KProcessAddress process_code_end; + size_t stack_region_size; + size_t kernel_map_region_size; + if (this->address_space_width == 39) { + alias_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Alias); + heap_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Heap); + stack_region_size = GetSpaceSize(KAddressSpaceInfo::Type_Stack); + kernel_map_region_size = GetSpaceSize(KAddressSpaceInfo::Type_32Bit); + this->code_region_start = GetSpaceStart(KAddressSpaceInfo::Type_Large64Bit); + this->code_region_end = this->code_region_start + GetSpaceSize(KAddressSpaceInfo::Type_Large64Bit); + this->alias_code_region_start = this->code_region_start; + this->alias_code_region_end = this->code_region_end; + process_code_start = util::AlignDown(GetInteger(code_address), RegionAlignment); + process_code_end = util::AlignUp(GetInteger(code_address) + code_size, RegionAlignment); + } else { + stack_region_size = 0; + kernel_map_region_size = 0; + this->code_region_start = GetSpaceStart(KAddressSpaceInfo::Type_32Bit); + this->code_region_end = this->code_region_start + GetSpaceSize(KAddressSpaceInfo::Type_32Bit); + this->stack_region_start = this->code_region_start; + this->alias_code_region_start = this->code_region_start; + this->alias_code_region_end = GetSpaceStart(KAddressSpaceInfo::Type_Small64Bit) + GetSpaceSize(KAddressSpaceInfo::Type_Small64Bit); + this->stack_region_end = this->code_region_end; + this->kernel_map_region_start = this->code_region_start; + this->kernel_map_region_end = this->code_region_end; + process_code_start = this->code_region_start; + process_code_end = this->code_region_end; + } + + /* Set other basic fields. */ + this->enable_aslr = enable_aslr; + this->address_space_start = start; + this->address_space_end = end; + this->is_kernel = false; + this->memory_block_slab_manager = mem_block_slab_manager; + this->block_info_manager = block_info_manager; + + /* Determine the region we can place our undetermineds in. */ + KProcessAddress alloc_start; + size_t alloc_size; + if ((GetInteger(process_code_start) - GetInteger(this->code_region_start)) >= (GetInteger(end) - GetInteger(process_code_end))) { + alloc_start = this->code_region_start; + alloc_size = this->code_region_end - alloc_start; + } else { + alloc_start = process_code_end; + alloc_size = GetInteger(end) - GetInteger(process_code_end); + } + const size_t needed_size = (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size); + R_UNLESS(alloc_size >= needed_size, svc::ResultOutOfMemory()); + + const size_t remaining_size = alloc_size - needed_size; + + /* Determine random placements for each region. */ + size_t alias_rnd = 0, heap_rnd = 0, stack_rnd = 0, kmap_rnd = 0; + if (enable_aslr) { + alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment; + heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment; + stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment; + kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment; + } + + /* Setup heap and alias regions. */ + this->alias_region_start = alloc_start + alias_rnd; + this->alias_region_end = this->alias_region_start + alias_region_size; + this->heap_region_start = alloc_start + heap_rnd; + this->heap_region_end = this->heap_region_start + heap_region_size; + + if (alias_rnd <= heap_rnd) { + this->heap_region_start += alias_region_size; + this->heap_region_end += alias_region_size; + } else { + this->alias_region_start += heap_region_size; + this->alias_region_end += heap_region_size; + } + + /* Setup stack region. */ + if (stack_region_size) { + this->stack_region_start = alloc_start + stack_rnd; + this->stack_region_end = this->stack_region_start + stack_region_size; + + if (alias_rnd < stack_rnd) { + this->stack_region_start += alias_region_size; + this->stack_region_end += alias_region_size; + } else { + this->alias_region_start += stack_region_size; + this->alias_region_end += stack_region_size; + } + + if (heap_rnd < stack_rnd) { + this->stack_region_start += heap_region_size; + this->stack_region_end += heap_region_size; + } else { + this->heap_region_start += stack_region_size; + this->heap_region_end += stack_region_size; + } + } + + /* Setup kernel map region. */ + if (kernel_map_region_size) { + this->kernel_map_region_start = alloc_start + kmap_rnd; + this->kernel_map_region_end = this->kernel_map_region_start + kernel_map_region_size; + + if (alias_rnd < kmap_rnd) { + this->kernel_map_region_start += alias_region_size; + this->kernel_map_region_end += alias_region_size; + } else { + this->alias_region_start += kernel_map_region_size; + this->alias_region_end += kernel_map_region_size; + } + + if (heap_rnd < kmap_rnd) { + this->kernel_map_region_start += heap_region_size; + this->kernel_map_region_end += heap_region_size; + } else { + this->heap_region_start += kernel_map_region_size; + this->heap_region_end += kernel_map_region_size; + } + + if (stack_region_size) { + if (stack_rnd < kmap_rnd) { + this->kernel_map_region_start += stack_region_size; + this->kernel_map_region_end += stack_region_size; + } else { + this->stack_region_start += kernel_map_region_size; + this->stack_region_end += kernel_map_region_size; + } + } + } + + /* Set heap and fill members. */ + this->current_heap_end = this->heap_region_start; + this->max_heap_size = 0; + this->max_physical_memory_size = 0; + + const bool fill_memory = KTargetSystem::IsDebugMemoryFillEnabled(); + this->heap_fill_value = fill_memory ? MemoryFillValue_Heap : MemoryFillValue_Zero; + this->ipc_fill_value = fill_memory ? MemoryFillValue_Ipc : MemoryFillValue_Zero; + this->stack_fill_value = fill_memory ? MemoryFillValue_Stack : MemoryFillValue_Zero; + + /* Set allocation option. */ + this->allocate_option = KMemoryManager::EncodeOption(pool, from_back ? KMemoryManager::Direction_FromBack : KMemoryManager::Direction_FromFront); + + /* Ensure that we regions inside our address space. */ + auto IsInAddressSpace = [&](KProcessAddress addr) ALWAYS_INLINE_LAMBDA { return this->address_space_start <= addr && addr <= this->address_space_end; }; + MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->alias_region_start)); + MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->alias_region_end)); + MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->heap_region_start)); + MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->heap_region_end)); + MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->stack_region_start)); + MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->stack_region_end)); + MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->kernel_map_region_start)); + MESOSPHERE_ABORT_UNLESS(IsInAddressSpace(this->kernel_map_region_end)); + + /* Ensure that we selected regions that don't overlap. */ + const KProcessAddress alias_start = this->alias_region_start; + const KProcessAddress alias_last = this->alias_region_end - 1; + const KProcessAddress heap_start = this->heap_region_start; + const KProcessAddress heap_last = this->heap_region_end - 1; + const KProcessAddress stack_start = this->stack_region_start; + const KProcessAddress stack_last = this->stack_region_end - 1; + const KProcessAddress kmap_start = this->kernel_map_region_start; + const KProcessAddress kmap_last = this->kernel_map_region_end - 1; + MESOSPHERE_ABORT_UNLESS(alias_last < heap_start || heap_last < alias_start); + MESOSPHERE_ABORT_UNLESS(alias_last < stack_start || stack_last < alias_start); + MESOSPHERE_ABORT_UNLESS(alias_last < kmap_start || kmap_last < alias_start); + MESOSPHERE_ABORT_UNLESS(heap_last < stack_start || stack_last < heap_start); + MESOSPHERE_ABORT_UNLESS(heap_last < kmap_start || kmap_last < heap_start); + + /* Initialize our implementation. */ + this->impl.InitializeForProcess(table, GetInteger(start), GetInteger(end)); + + /* Initialize our memory block manager. */ + return this->memory_block_manager.Initialize(this->address_space_start, this->address_space_end, this->memory_block_slab_manager); + + return ResultSuccess(); + } + + void KPageTableBase::Finalize() { this->memory_block_manager.Finalize(this->memory_block_slab_manager); MESOSPHERE_TODO("cpu::InvalidateEntireInstructionCache();"); @@ -134,7 +341,7 @@ namespace ams::kern { } } - bool KPageTableBase::Contains(KProcessAddress addr, size_t size, KMemoryState state) const { + bool KPageTableBase::CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { const KProcessAddress end = addr + size; const KProcessAddress last = end - 1; @@ -426,7 +633,7 @@ namespace ams::kern { MESOSPHERE_ASSERT(util::IsAligned(alignment, PageSize) && alignment >= PageSize); /* Ensure this is a valid map request. */ - R_UNLESS(this->Contains(region_start, region_num_pages * PageSize, state), svc::ResultInvalidCurrentMemory()); + R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), svc::ResultInvalidCurrentMemory()); R_UNLESS(num_pages < region_num_pages, svc::ResultOutOfMemory()); /* Lock the table. */ @@ -436,7 +643,7 @@ namespace ams::kern { KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, this->GetNumGuardPages()); R_UNLESS(addr != Null, svc::ResultOutOfMemory()); MESOSPHERE_ASSERT(util::IsAligned(GetInteger(addr), alignment)); - MESOSPHERE_ASSERT(this->Contains(addr, num_pages * PageSize, state)); + MESOSPHERE_ASSERT(this->CanContain(addr, num_pages * PageSize, state)); MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ @@ -471,8 +678,8 @@ namespace ams::kern { /* Ensure this is a valid map request. */ const size_t num_pages = pg.GetNumPages(); - R_UNLESS(this->Contains(region_start, region_num_pages * PageSize, state), svc::ResultInvalidCurrentMemory()); - R_UNLESS(num_pages < region_num_pages, svc::ResultOutOfMemory()); + R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), svc::ResultInvalidCurrentMemory()); + R_UNLESS(num_pages < region_num_pages, svc::ResultOutOfMemory()); /* Lock the table. */ KScopedLightLock lk(this->general_lock); @@ -480,7 +687,7 @@ namespace ams::kern { /* Find a random address to map at. */ KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize, 0, this->GetNumGuardPages()); R_UNLESS(addr != Null, svc::ResultOutOfMemory()); - MESOSPHERE_ASSERT(this->Contains(addr, num_pages * PageSize, state)); + MESOSPHERE_ASSERT(this->CanContain(addr, num_pages * PageSize, state)); MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); /* Create an update allocator. */ @@ -502,13 +709,45 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::MapPageGroup(KProcessAddress addr, const KPageGroup &pg, KMemoryState state, KMemoryPermission perm) { + MESOSPHERE_ASSERT(!this->IsLockedByCurrentThread()); + + /* Ensure this is a valid map request. */ + const size_t num_pages = pg.GetNumPages(); + const size_t size = num_pages * PageSize; + R_UNLESS(this->CanContain(addr, size, state), svc::ResultInvalidCurrentMemory()); + + /* Lock the table. */ + KScopedLightLock lk(this->general_lock); + + /* Check if state allows us to map. */ + R_TRY(this->CheckMemoryState(addr, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); + + /* Create an update allocator. */ + KMemoryBlockManagerUpdateAllocator allocator(this->memory_block_slab_manager); + R_TRY(allocator.GetResult()); + + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + /* Perform mapping operation. */ + const KPageProperties properties = { perm, state == KMemoryState_Io, false, false }; + R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); + + /* Update the blocks. */ + this->memory_block_manager.Update(&allocator, addr, num_pages, state, perm, KMemoryAttribute_None); + + /* We successfully mapped the pages. */ + return ResultSuccess(); + } + Result KPageTableBase::UnmapPageGroup(KProcessAddress address, const KPageGroup &pg, KMemoryState state) { MESOSPHERE_ASSERT(!this->IsLockedByCurrentThread()); /* Ensure this is a valid unmap request. */ const size_t num_pages = pg.GetNumPages(); const size_t size = num_pages * PageSize; - R_UNLESS(this->Contains(address, size, state), svc::ResultInvalidCurrentMemory()); + R_UNLESS(this->CanContain(address, size, state), svc::ResultInvalidCurrentMemory()); /* Lock the table. */ KScopedLightLock lk(this->general_lock); diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 637c58d32..1e5d68520 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -17,10 +17,72 @@ namespace ams::kern { + namespace { + + constexpr u64 InitialProcessIdMin = 1; + constexpr u64 InitialProcessIdMax = 0x50; + std::atomic g_initial_process_id = InitialProcessIdMin; + + } + void KProcess::Finalize() { MESOSPHERE_TODO_IMPLEMENT(); } + Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms) { + MESOSPHERE_TODO_IMPLEMENT(); + } + + Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool) { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(res_limit != nullptr); + MESOSPHERE_ABORT_UNLESS((params.code_num_pages * PageSize) / PageSize == params.code_num_pages); + + /* Set members. */ + this->memory_pool = pool; + this->resource_limit = resource_limit; + this->system_resource_address = Null; + this->system_resource_num_pages = 0; + + /* Setup page table. */ + /* NOTE: Nintendo passes process ID despite not having set it yet. */ + /* This goes completely unused, but even so... */ + { + const auto as_type = static_cast(params.flags & ams::svc::CreateProcessFlag_AddressSpaceMask); + const bool enable_aslr = (params.flags & ams::svc::CreateProcessFlag_EnableAslr); + const bool is_app = (params.flags & ams::svc::CreateProcessFlag_IsApplication); + auto *mem_block_manager = std::addressof(is_app ? Kernel::GetApplicationMemoryBlockManager() : Kernel::GetSystemMemoryBlockManager()); + auto *block_info_manager = std::addressof(Kernel::GetBlockInfoManager()); + auto *pt_manager = std::addressof(Kernel::GetPageTableManager()); + R_TRY(this->page_table.Initialize(this->process_id, as_type, enable_aslr, !enable_aslr, pool, params.code_address, params.code_num_pages * PageSize, mem_block_manager, block_info_manager, pt_manager)); + } + auto pt_guard = SCOPE_GUARD { this->page_table.Finalize(); }; + + /* Ensure we can insert the code region. */ + R_UNLESS(this->page_table.CanContain(params.code_address, params.code_num_pages * PageSize, KMemoryState_Code), svc::ResultInvalidMemoryRegion()); + + /* Map the code region. */ + R_TRY(this->page_table.MapPageGroup(params.code_address, pg, KMemoryState_Code, KMemoryPermission_KernelRead)); + + /* Initialize capabilities. */ + R_TRY(this->capabilities.Initialize(caps, num_caps, std::addressof(this->page_table))); + + /* Initialize the process id. */ + this->process_id = g_initial_process_id++; + MESOSPHERE_ABORT_UNLESS(InitialProcessIdMin <= this->process_id); + MESOSPHERE_ABORT_UNLESS(this->process_id <= InitialProcessIdMax); + + /* Initialize the rest of the process. */ + R_TRY(this->Initialize(params)); + + /* Open a reference to the resource limit. */ + this->resource_limit->Open(); + + /* We succeeded! */ + pt_guard.Cancel(); + return ResultSuccess(); + } + void KProcess::DoWorkerTask() { MESOSPHERE_TODO_IMPLEMENT(); }