kern: implement KProcess::Run
This commit is contained in:
@@ -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());
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
59
libraries/libmesosphere/source/kern_k_synchronization.cpp
Normal file
59
libraries/libmesosphere/source/kern_k_synchronization.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user