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 a47b6f8c7..12ace117b 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 @@ -47,8 +47,32 @@ namespace ams::kern::arch::arm64 { return this->page_table.MapPageGroup(addr, pg, state, perm); } + Result MapPages(KProcessAddress *out_addr, size_t num_pages, size_t alignment, KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) { + return this->page_table.MapPages(out_addr, num_pages, alignment, phys_addr, state, perm); + } + + Result UnmapPages(KProcessAddress addr, size_t num_pages, KMemoryState state) { + return this->page_table.UnmapPages(addr, num_pages, state); + } + + bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const { + return this->page_table.GetPhysicalAddress(out, address); + } + 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); } + + KProcessAddress GetAddressSpaceStart() const { return this->page_table.GetAddressSpaceStart(); } + KProcessAddress GetHeapRegionStart() const { return this->page_table.GetHeapRegionStart(); } + KProcessAddress GetAliasRegionStart() const { return this->page_table.GetAliasRegionStart(); } + KProcessAddress GetStackRegionStart() const { return this->page_table.GetStackRegionStart(); } + KProcessAddress GetKernelMapRegionStart() const { return this->page_table.GetKernelMapRegionStart(); } + + size_t GetAddressSpaceSize() const { return this->page_table.GetAddressSpaceSize(); } + size_t GetHeapRegionSize() const { return this->page_table.GetHeapRegionSize(); } + size_t GetAliasRegionSize() const { return this->page_table.GetAliasRegionSize(); } + size_t GetStackRegionSize() const { return this->page_table.GetStackRegionSize(); } + size_t GetKernelMapRegionSize() const { return this->page_table.GetKernelMapRegionSize(); } }; } 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 4e557cf15..21512932b 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -250,10 +250,26 @@ namespace ams::kern { 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) { + return this->MapPages(out_addr, num_pages, alignment, phys_addr, true, this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize, state, 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); + public: + KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; } + KProcessAddress GetHeapRegionStart() const { return this->heap_region_start; } + KProcessAddress GetAliasRegionStart() const { return this->alias_region_start; } + KProcessAddress GetStackRegionStart() const { return this->stack_region_start; } + KProcessAddress GetKernelMapRegionStart() const { return this->kernel_map_region_start; } + + size_t GetAddressSpaceSize() const { return this->address_space_end - this->address_space_start; } + size_t GetHeapRegionSize() const { return this->heap_region_end - this->heap_region_start; } + size_t GetAliasRegionSize() const { return this->alias_region_end - this->alias_region_start; } + size_t GetStackRegionSize() const { return this->stack_region_end - this->stack_region_start; } + size_t GetKernelMapRegionSize() const { return this->kernel_map_region_end - this->kernel_map_region_start; } public: static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress addr) { return KMemoryLayout::GetLinearVirtualAddress(addr); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index a4a075f5d..847960c20 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -135,6 +135,12 @@ namespace ams::kern { return this->pinned_threads[core_id]; } + constexpr KProcessPageTable &GetPageTable() { return this->page_table; } + constexpr const KProcessPageTable &GetPageTable() const { return this->page_table; } + + Result CreateThreadLocalRegion(KProcessAddress *out); + void *GetThreadLocalRegionPointer(KProcessAddress addr); + void SetPreemptionState(); public: /* Overridden parent functions. */ diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread_local_page.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread_local_page.hpp index 9e8c3fe6e..8670bdb64 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread_local_page.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread_local_page.hpp @@ -42,6 +42,19 @@ namespace ams::kern { constexpr explicit KThreadLocalPage() : KThreadLocalPage(Null) { /* ... */ } constexpr ALWAYS_INLINE KProcessAddress GetAddress() const { return this->virt_addr; } + + static constexpr ALWAYS_INLINE int Compare(const KThreadLocalPage &lhs, const KThreadLocalPage &rhs) { + const KProcessAddress lval = lhs.GetAddress(); + const KProcessAddress rval = rhs.GetAddress(); + + if (lval < rval) { + return -1; + } else if (lval == rval) { + return 0; + } else { + return 1; + } + } private: constexpr ALWAYS_INLINE KProcessAddress GetRegionAddress(size_t i) { return this->GetAddress() + i * ams::svc::ThreadLocalRegionSize; @@ -63,6 +76,8 @@ namespace ams::kern { KProcessAddress Reserve(); void Release(KProcessAddress addr); + void *GetPointer() const; + bool IsAllUsed() const { for (size_t i = 0; i < RegionsPerPage; i++) { if (this->is_region_free[i]) { diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 1e5d68520..1071de434 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -30,7 +30,76 @@ namespace ams::kern { } Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms) { - MESOSPHERE_TODO_IMPLEMENT(); + /* TODO: Validate intended kernel version. */ + /* How should we do this? */ + + /* Create and clear the process local region. */ + R_TRY(this->CreateThreadLocalRegion(std::addressof(this->plr_address))); + std::memset(this->GetThreadLocalRegionPointer(this->plr_address), 0, ams::svc::ThreadLocalRegionSize); + + /* Copy in the name from parameters. */ + static_assert(sizeof(params.name) < sizeof(this->name)); + std::memcpy(this->name, params.name, sizeof(params.name)); + this->name[sizeof(params.name)] = 0; + + /* Set misc fields. */ + this->state = State_Created; + this->main_thread_stack_size = 0; + this->creation_time = KHardwareTimer::GetTick(); + this->used_kernel_memory_size = 0; + this->ideal_core_id = 0; + this->flags = params.flags; + this->version = params.version; + this->program_id = params.program_id; + this->code_address = params.code_address; + this->code_size = params.code_num_pages * PageSize; + this->is_application = (params.flags & ams::svc::CreateProcessFlag_IsApplication); + this->is_jit_debug = false; + + /* Set thread fields. */ + for (size_t i = 0; i < cpu::NumCores; i++) { + this->running_threads[i] = nullptr; + this->running_thread_idle_counts[i] = 0; + this->pinned_threads[i] = nullptr; + } + + /* Set max memory based on address space type. */ + switch ((params.flags & ams::svc::CreateProcessFlag_AddressSpaceMask)) { + case ams::svc::CreateProcessFlag_AddressSpace32Bit: + case ams::svc::CreateProcessFlag_AddressSpace64BitDeprecated: + case ams::svc::CreateProcessFlag_AddressSpace64Bit: + this->max_process_memory = this->page_table.GetHeapRegionSize(); + break; + case ams::svc::CreateProcessFlag_AddressSpace32BitWithoutAlias: + this->max_process_memory = this->page_table.GetHeapRegionSize() + this->page_table.GetAliasRegionSize(); + break; + MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); + } + + /* Generate random entropy. */ + KSystemControl::GenerateRandomBytes(this->entropy, sizeof(this->entropy)); + + /* Clear remaining fields. */ + this->num_threads = 0; + this->peak_num_threads = 0; + this->num_created_threads = 0; + this->num_process_switches = 0; + this->num_thread_switches = 0; + this->num_fpu_switches = 0; + this->num_supervisor_calls = 0; + this->num_ipc_messages = 0; + + this->is_signaled = false; + this->attached_object = nullptr; + this->exception_thread = nullptr; + this->is_suspended = false; + this->memory_release_hint = 0; + this->schedule_count = 0; + + /* We're initialized! */ + this->is_initialized = true; + + return ResultSuccess(); } Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms, const KPageGroup &pg, const u32 *caps, s32 num_caps, KResourceLimit *res_limit, KMemoryManager::Pool pool) { @@ -40,7 +109,7 @@ namespace ams::kern { /* Set members. */ this->memory_pool = pool; - this->resource_limit = resource_limit; + this->resource_limit = res_limit; this->system_resource_address = Null; this->system_resource_num_pages = 0; @@ -87,6 +156,72 @@ namespace ams::kern { MESOSPHERE_TODO_IMPLEMENT(); } + Result KProcess::CreateThreadLocalRegion(KProcessAddress *out) { + KThreadLocalPage *tlp = nullptr; + KProcessAddress tlr = Null; + + /* See if we can get a region from a partially used TLP. */ + { + KScopedSchedulerLock sl; + + if (auto it = this->partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) { + tlr = it->Reserve(); + MESOSPHERE_ABORT_UNLESS(tlr != Null); + + if (it->IsAllUsed()) { + tlp = std::addressof(*it); + this->partially_used_tlp_tree.erase(it); + this->fully_used_tlp_tree.insert(*tlp); + } + + *out = tlr; + return ResultSuccess(); + } + } + + /* Allocate a new page. */ + tlp = KThreadLocalPage::Allocate(); + R_UNLESS(tlp != nullptr, svc::ResultOutOfMemory()); + auto tlp_guard = SCOPE_GUARD { KThreadLocalPage::Free(tlp); }; + + /* Initialize the new page. */ + R_TRY(tlp->Initialize(this)); + + /* Reserve a TLR. */ + tlr = tlp->Reserve(); + MESOSPHERE_ABORT_UNLESS(tlr != Null); + + /* Insert into our tree. */ + { + KScopedSchedulerLock sl; + if (tlp->IsAllUsed()) { + this->fully_used_tlp_tree.insert(*tlp); + } else { + this->partially_used_tlp_tree.insert(*tlp); + } + } + + /* We succeeded! */ + tlp_guard.Cancel(); + *out = tlr; + return ResultSuccess(); + } + + void *KProcess::GetThreadLocalRegionPointer(KProcessAddress addr) { + KThreadLocalPage *tlp = nullptr; + { + KScopedSchedulerLock sl; + if (auto it = this->partially_used_tlp_tree.find(KThreadLocalPage(util::AlignDown(GetInteger(addr), PageSize))); it != this->partially_used_tlp_tree.end()) { + tlp = std::addressof(*it); + } else if (auto it = this->fully_used_tlp_tree.find(KThreadLocalPage(util::AlignDown(GetInteger(addr), PageSize))); it != this->fully_used_tlp_tree.end()) { + tlp = std::addressof(*it); + } else { + return nullptr; + } + } + return static_cast(tlp->GetPointer()) + (GetInteger(addr) & (PageSize - 1)); + } + void KProcess::SetPreemptionState() { MESOSPHERE_TODO_IMPLEMENT(); } diff --git a/libraries/libmesosphere/source/kern_k_thread_local_page.cpp b/libraries/libmesosphere/source/kern_k_thread_local_page.cpp index e19e4d4d9..d95811571 100644 --- a/libraries/libmesosphere/source/kern_k_thread_local_page.cpp +++ b/libraries/libmesosphere/source/kern_k_thread_local_page.cpp @@ -29,7 +29,7 @@ namespace ams::kern { auto page_buf_guard = SCOPE_GUARD { KPageBuffer::Free(page_buf); }; /* Map the address in. */ - MESOSPHERE_TODO("R_TRY(this->owner->GetPageTable().Map(...));"); + R_TRY(this->owner->GetPageTable().MapPages(std::addressof(this->virt_addr), 1, PageSize, page_buf->GetPhysicalAddress(), KMemoryState_ThreadLocal, KMemoryPermission_UserReadWrite)); /* We succeeded. */ page_buf_guard.Cancel(); @@ -41,10 +41,10 @@ namespace ams::kern { /* Get the physical address of the page. */ KPhysicalAddress phys_addr = Null; - MESOSPHERE_TODO("MESOSPHERE_ABORT_UNLESS(this->owner->GetPageTable().GetPhysicalAddress(&phys_addr, this->GetAddress()));"); + MESOSPHERE_ABORT_UNLESS(this->owner->GetPageTable().GetPhysicalAddress(&phys_addr, this->GetAddress())); /* Unmap the page. */ - MESOSPHERE_TODO("R_TRY(this->owner->GetPageTable().Unmap(...);"); + R_TRY(this->owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState_ThreadLocal)); /* Free the page. */ KPageBuffer::Free(KPageBuffer::FromPhysicalAddress(phys_addr)); @@ -70,4 +70,12 @@ namespace ams::kern { this->is_region_free[this->GetRegionIndex(addr)] = true; } + void *KThreadLocalPage::GetPointer() const { + MESOSPHERE_ASSERT_THIS(); + + KPhysicalAddress phys_addr; + MESOSPHERE_ABORT_UNLESS(this->owner->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), this->GetAddress())); + return static_cast(KPageBuffer::FromPhysicalAddress(phys_addr)); + } + }