kern: implement KProcess::Run

This commit is contained in:
Michael Scire
2020-02-19 19:38:20 -08:00
parent 28ea0b12a8
commit c568788609
25 changed files with 516 additions and 33 deletions

View File

@@ -184,8 +184,20 @@ namespace ams::kern::arch::arm64 {
}
}
Result KPageTable::Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, const KPageGroup *page_group, const KPageProperties properties, OperationType operation, bool reuse_ll) {
MESOSPHERE_TODO_IMPLEMENT();
Result KPageTable::Operate(PageLinkedList *page_list, KProcessAddress virt_addr, size_t num_pages, const KPageGroup &page_group, const KPageProperties properties, OperationType operation, bool reuse_ll) {
/* Check validity of parameters. */
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(virt_addr), PageSize));
MESOSPHERE_ASSERT(num_pages > 0);
MESOSPHERE_ASSERT(num_pages == page_group.GetNumPages());
/* Map the page group. */
auto entry_template = this->GetEntryTemplate(properties);
switch (operation) {
case OperationType_MapGroup:
return this->MapGroup(virt_addr, page_group, num_pages, entry_template, page_list, reuse_ll);
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}
Result KPageTable::Map(KProcessAddress virt_addr, KPhysicalAddress phys_addr, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll) {
@@ -510,6 +522,7 @@ namespace ams::kern::arch::arm64 {
}
}
/* We successfully mapped, so cancel our guard. */
map_guard.Cancel();
}
@@ -527,6 +540,50 @@ namespace ams::kern::arch::arm64 {
return ResultSuccess();
}
Result KPageTable::MapGroup(KProcessAddress virt_addr, const KPageGroup &pg, size_t num_pages, PageTableEntry entry_template, PageLinkedList *page_list, bool reuse_ll) {
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
/* We want to maintain a new reference to every page in the group. */
KScopedPageGroup spg(pg);
/* Cache initial address for use on cleanup. */
const KProcessAddress orig_virt_addr = virt_addr;
size_t mapped_pages = 0;
/* Map the pages, using a guard to ensure we don't leak. */
{
auto map_guard = SCOPE_GUARD { MESOSPHERE_R_ABORT_UNLESS(this->Unmap(orig_virt_addr, num_pages, page_list, true, true)); };
if (num_pages < ContiguousPageSize / PageSize) {
for (const auto &block : pg) {
const KPhysicalAddress block_phys_addr = GetLinearPhysicalAddress(block.GetAddress());
const size_t cur_pages = block.GetNumPages();
R_TRY(this->Map(virt_addr, block_phys_addr, cur_pages, entry_template, L3BlockSize, page_list, reuse_ll));
virt_addr += cur_pages * PageSize;
mapped_pages += cur_pages;
}
} else {
MESOSPHERE_TODO("Large page group map");
}
/* We successfully mapped, so cancel our guard. */
map_guard.Cancel();
}
MESOSPHERE_ASSERT(mapped_pages == num_pages);
/* Perform what coalescing we can. */
this->MergePages(orig_virt_addr, page_list);
if (num_pages > 1) {
this->MergePages(orig_virt_addr + (num_pages - 1) * PageSize, page_list);
}
/* We succeeded! We want to persist the reference to the pages. */
spg.CancelClose();
return ResultSuccess();
}
bool KPageTable::MergePages(KProcessAddress virt_addr, PageLinkedList *page_list) {
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());

View File

@@ -137,6 +137,12 @@ namespace ams::kern::arch::arm64 {
return ResultSuccess();
}
void KThreadContext::SetArguments(uintptr_t arg0, uintptr_t arg1) {
u64 *stack = reinterpret_cast<u64 *>(this->sp);
stack[0] = arg0;
stack[1] = arg1;
}
void KThreadContext::FpuContextSwitchHandler(KThread *thread) {
MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled());
MESOSPHERE_ASSERT(!IsFpuEnabled());
@@ -148,9 +154,9 @@ namespace ams::kern::arch::arm64 {
KProcess *process = thread->GetOwnerProcess();
MESOSPHERE_ASSERT(process != nullptr);
if (process->Is64Bit()) {
RestoreFpuRegisters64(*thread->GetContext());
RestoreFpuRegisters64(thread->GetContext());
} else {
RestoreFpuRegisters32(*thread->GetContext());
RestoreFpuRegisters32(thread->GetContext());
}
}

View File

@@ -171,7 +171,7 @@ namespace ams::kern {
/* Run the processes. */
for (size_t i = 0; i < g_initial_process_binary_header.num_processes; i++) {
MESOSPHERE_TODO("infos[i].process->Run(infos[i].priority, infos[i].stack_size);");
MESOSPHERE_R_ABORT_UNLESS(infos[i].process->Run(infos[i].priority, infos[i].stack_size));
}
}

View File

@@ -509,7 +509,7 @@ namespace ams::kern {
}
/* Map the pages. */
return this->Operate(page_list, address, num_pages, std::addressof(pg), properties, OperationType_MapGroup, false);
return this->Operate(page_list, address, num_pages, pg, properties, OperationType_MapGroup, false);
}
Result KPageTableBase::MapPageGroupImpl(PageLinkedList *page_list, KProcessAddress address, const KPageGroup &pg, const KPageProperties properties, bool reuse_ll) {
@@ -749,6 +749,22 @@ namespace ams::kern {
return ResultSuccess();
}
Result KPageTableBase::SetHeapSize(KProcessAddress *out, size_t size) {
MESOSPHERE_TODO_IMPLEMENT();
}
Result KPageTableBase::SetMaxHeapSize(size_t size) {
/* Lock the table. */
KScopedLightLock lk(this->general_lock);
/* Only process page tables are allowed to set heap size. */
MESOSPHERE_ASSERT(!this->IsKernel());
this->max_heap_size = size;
return ResultSuccess();
}
Result KPageTableBase::MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) {
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(phys_addr), PageSize));
MESOSPHERE_ASSERT(util::IsAligned(size, PageSize));

View File

@@ -222,6 +222,122 @@ namespace ams::kern {
return static_cast<u8 *>(tlp->GetPointer()) + (GetInteger(addr) & (PageSize - 1));
}
void KProcess::IncrementThreadCount() {
MESOSPHERE_ASSERT(this->num_threads >= 0);
++this->num_created_threads;
if (const auto count = ++this->num_threads; count > this->peak_num_threads) {
this->peak_num_threads = count;
}
}
void KProcess::DecrementThreadCount() {
MESOSPHERE_ASSERT(this->num_threads > 0);
if (const auto count = --this->num_threads; count == 0) {
MESOSPHERE_TODO("this->Terminate();");
}
}
void KProcess::RegisterThread(KThread *thread) {
KScopedLightLock lk(this->list_lock);
this->thread_list.push_back(*thread);
}
void KProcess::UnregisterThread(KThread *thread) {
KScopedLightLock lk(this->list_lock);
this->thread_list.erase(this->thread_list.iterator_to(*thread));
}
Result KProcess::Run(s32 priority, size_t stack_size) {
MESOSPHERE_ASSERT_THIS();
/* Lock ourselves, to prevent concurrent access. */
KScopedLightLock lk(this->lock);
/* Validate that we're in a state where we can initialize. */
const auto state = this->state;
R_UNLESS(state == State_Created || state == State_CreatedAttached, svc::ResultInvalidState());
/* Place a tentative reservation of a thread for this process. */
KScopedResourceReservation thread_reservation(this, ams::svc::LimitableResource_ThreadCountMax);
R_UNLESS(thread_reservation.Succeeded(), svc::ResultLimitReached());
/* Ensure that we haven't already allocated stack. */
MESOSPHERE_ABORT_UNLESS(this->main_thread_stack_size == 0);
/* Ensure that we're allocating a valid stack. */
stack_size = util::AlignUp(stack_size, PageSize);
R_UNLESS(stack_size + this->code_size <= this->max_process_memory, svc::ResultOutOfMemory());
R_UNLESS(stack_size + this->code_size >= this->code_size, svc::ResultOutOfMemory());
/* Place a tentative reservation of memory for our new stack. */
KScopedResourceReservation mem_reservation(this, ams::svc::LimitableResource_PhysicalMemoryMax);
R_UNLESS(mem_reservation.Succeeded(), svc::ResultLimitReached());
/* Allocate and map our stack. */
KProcessAddress stack_top = Null<KProcessAddress>;
if (stack_size) {
KProcessAddress stack_bottom;
R_TRY(this->page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize, KMemoryState_Stack, KMemoryPermission_UserReadWrite));
stack_top = stack_bottom + stack_size;
this->main_thread_stack_size = stack_size;
}
/* Ensure our stack is safe to clean up on exit. */
auto stack_guard = SCOPE_GUARD {
if (this->main_thread_stack_size) {
MESOSPHERE_R_ABORT_UNLESS(this->page_table.UnmapPages(stack_top - this->main_thread_stack_size, this->main_thread_stack_size / PageSize, KMemoryState_Stack));
this->main_thread_stack_size = 0;
}
};
/* Set our maximum heap size. */
R_TRY(this->page_table.SetMaxHeapSize(this->max_process_memory - (this->main_thread_stack_size + this->code_size)));
/* Initialize our handle table. */
R_TRY(this->handle_table.Initialize(this->capabilities.GetHandleTableSize()));
auto ht_guard = SCOPE_GUARD { this->handle_table.Finalize(); };
/* Create a new thread for the process. */
KThread *main_thread = KThread::Create();
R_UNLESS(main_thread != nullptr, svc::ResultOutOfResource());
auto thread_guard = SCOPE_GUARD { main_thread->Close(); };
/* Initialize the thread. */
R_TRY(KThread::InitializeUserThread(main_thread, reinterpret_cast<KThreadFunction>(GetVoidPointer(this->GetEntryPoint())), 0, stack_top, priority, this->ideal_core_id, this));
/* Register the thread, and commit our reservation. */
KThread::Register(main_thread);
thread_reservation.Commit();
/* Add the thread to our handle table. */
ams::svc::Handle thread_handle;
R_TRY(this->handle_table.Add(std::addressof(thread_handle), main_thread));
/* Set the thread arguments. */
main_thread->GetContext().SetArguments(0, thread_handle);
/* Update our state. */
this->ChangeState((state == State_Created) ? State_Running : State_RunningAttached);
auto state_guard = SCOPE_GUARD { this->ChangeState(state); };
/* Run our thread. */
R_TRY(main_thread->Run());
/* We succeeded! Cancel our guards. */
state_guard.Cancel();
thread_guard.Cancel();
ht_guard.Cancel();
stack_guard.Cancel();
mem_reservation.Commit();
return ResultSuccess();
}
void KProcess::SetPreemptionState() {
MESOSPHERE_TODO_IMPLEMENT();
}

View File

@@ -0,0 +1,59 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
namespace ams::kern {
Result KSynchronization::Wait(s32 *out_index, KSynchronizationObject **objects, const s32 num_objects, s64 timeout) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_TODO_IMPLEMENT();
}
void KSynchronization::OnAvailable(KSynchronizationObject *object) {
MESOSPHERE_ASSERT_THIS();
KScopedSchedulerLock sl;
/* If we're not signaled, we've nothing to notify. */
if (!object->IsSignaled()) {
return;
}
/* Iterate over each thread. */
for (auto &thread : *object) {
if (thread.GetState() == KThread::ThreadState_Waiting) {
thread.SetSyncedObject(object, ResultSuccess());
thread.SetState(KThread::ThreadState_Runnable);
}
}
}
void KSynchronization::OnAbort(KSynchronizationObject *object, Result abort_reason) {
MESOSPHERE_ASSERT_THIS();
KScopedSchedulerLock sl;
/* Iterate over each thread. */
for (auto &thread : *object) {
if (thread.GetState() == KThread::ThreadState_Waiting) {
thread.SetSyncedObject(object, abort_reason);
thread.SetState(KThread::ThreadState_Runnable);
}
}
}
}

View File

@@ -17,16 +17,16 @@
namespace ams::kern {
void NotifyAvailable() {
void KSynchronizationObject::NotifyAvailable() {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_TODO_IMPLEMENT();
Kernel::GetSynchronization().OnAvailable(this);
}
void NotifyAbort(Result abort_reason) {
void KSynchronizationObject::NotifyAbort(Result abort_reason) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_TODO_IMPLEMENT();
Kernel::GetSynchronization().OnAbort(this, abort_reason);
}
void KSynchronizationObject::Finalize() {

View File

@@ -154,8 +154,8 @@ namespace ams::kern {
/* Setup the TLS, if needed. */
if (type == ThreadType_User) {
MESOSPHERE_TODO("R_TRY(owner->CreateThreadLocalRegion(&this->tls_address));");
MESOSPHERE_TODO("this->tls_heap_address = owner->GetThreadLocalRegionAddress(this->tls_address);");
R_TRY(owner->CreateThreadLocalRegion(std::addressof(this->tls_address)));
this->tls_heap_address = owner->GetThreadLocalRegionPointer(this->tls_address);
std::memset(this->tls_heap_address, 0, ams::svc::ThreadLocalRegionSize);
}
@@ -163,7 +163,7 @@ namespace ams::kern {
if (owner != nullptr) {
this->parent = owner;
this->parent->Open();
MESOSPHERE_TODO("this->parent->IncrementThreadCount();");
this->parent->IncrementThreadCount();
}
/* Initialize thread context. */
@@ -176,7 +176,7 @@ namespace ams::kern {
/* Setup the stack parameters. */
StackParameters &sp = this->GetStackParameters();
if (this->parent != nullptr) {
MESOSPHERE_TODO("this->parent->CopySvcPermissionTo(sp.svc_permission);");
this->parent->CopySvcPermissionsTo(sp);
}
sp.context = std::addressof(this->thread_context);
sp.disable_count = 1;
@@ -190,8 +190,10 @@ namespace ams::kern {
/* Register ourselves with our parent process. */
if (this->parent != nullptr) {
MESOSPHERE_TODO("this->parent->RegisterThread(this);");
MESOSPHERE_TODO("if (this->parent->IsSuspended()) { this->RequestSuspend(SuspendType_Process);");
this->parent->RegisterThread(this);
if (this->parent->IsSuspended()) {
this->RequestSuspend(SuspendType_Process);
}
}
return ResultSuccess();
@@ -559,7 +561,7 @@ namespace ams::kern {
}
KThreadContext *KThread::GetContextForSchedulerLoop() {
return std::addressof(this->thread_context);
return std::addressof(this->GetContext());
}
}