/* * 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 { namespace { struct InitialProcessInfo { KProcess *process; size_t stack_size; s32 priority; }; KVirtualAddress GetInitialProcessBinaryAddress() { return KMemoryLayout::GetPageTableHeapRegion().GetEndAddress() - InitialProcessBinarySizeMax; } void LoadInitialProcessBinaryHeader(InitialProcessBinaryHeader *header) { if (header->magic != InitialProcessBinaryMagic) { *header = *GetPointer(GetInitialProcessBinaryAddress()); } MESOSPHERE_ABORT_UNLESS(header->magic == InitialProcessBinaryMagic); MESOSPHERE_ABORT_UNLESS(header->num_processes <= init::GetSlabResourceCounts().num_KProcess); } void CreateProcesses(InitialProcessInfo *infos, KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) { u8 *current = GetPointer(binary_address + sizeof(InitialProcessBinaryHeader)); const u8 * const end = GetPointer(binary_address + header.size - sizeof(KInitialProcessHeader)); const size_t num_processes = header.num_processes; for (size_t i = 0; i < num_processes; i++) { /* Validate that we can read the current KIP. */ MESOSPHERE_ABORT_UNLESS(current <= end); KInitialProcessReader reader; MESOSPHERE_ABORT_UNLESS(reader.Attach(current)); /* Parse process parameters and reserve memory. */ ams::svc::CreateProcessParameter params; MESOSPHERE_R_ABORT_UNLESS(reader.MakeCreateProcessParameter(std::addressof(params), true)); MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, params.code_num_pages * PageSize)); /* Create the process. */ KProcess *new_process = nullptr; { /* Declare page group to use for process memory. */ KPageGroup pg(std::addressof(Kernel::GetBlockInfoManager())); /* Allocate memory for the process. */ auto &mm = Kernel::GetMemoryManager(); const auto pool = static_cast(reader.UsesSecureMemory() ? KMemoryManager::Pool_System : KSystemControl::GetInitialProcessBinaryPool()); MESOSPHERE_R_ABORT_UNLESS(mm.Allocate(std::addressof(pg), params.code_num_pages, KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront))); { /* Ensure that we do not leak pages. */ KScopedPageGroup spg(pg); /* Map the process's memory into the temporary region. */ const auto &temp_region = KMemoryLayout::GetTempRegion(); KProcessAddress temp_address = Null; MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().MapPageGroup(std::addressof(temp_address), pg, temp_region.GetAddress(), temp_region.GetSize() / PageSize, KMemoryState_Kernel, KMemoryPermission_KernelReadWrite)); /* Load the process. */ MESOSPHERE_R_ABORT_UNLESS(reader.Load(temp_address, params)); /* Unmap the temporary mapping. */ MESOSPHERE_R_ABORT_UNLESS(Kernel::GetKernelPageTable().UnmapPageGroup(temp_address, pg, KMemoryState_Kernel)); /* Create a KProcess object. */ MESOSPHERE_TODO("Create a KProcess"); /* Initialize the process. */ MESOSPHERE_TODO("Initialize the process"); } } /* Set the process's memory permissions. */ MESOSPHERE_TODO("Set process's memory permissions"); /* Register the process. */ MESOSPHERE_TODO("Register the process"); /* Save the process info. */ infos[i].process = new_process; infos[i].stack_size = reader.GetStackSize(); infos[i].priority = reader.GetPriority(); /* Advance the reader. */ current += reader.GetBinarySize(); } } KVirtualAddress g_initial_process_binary_address; InitialProcessBinaryHeader g_initial_process_binary_header; u64 g_initial_process_id_min = std::numeric_limits::max(); u64 g_initial_process_id_max = std::numeric_limits::min(); } u64 GetInitialProcessIdMin() { return g_initial_process_id_min; } u64 GetInitialProcessIdMax() { return g_initial_process_id_max; } void CopyInitialProcessBinaryToKernelMemory() { LoadInitialProcessBinaryHeader(&g_initial_process_binary_header); if (g_initial_process_binary_header.num_processes > 0) { /* Reserve pages for the initial process binary from the system resource limit. */ auto &mm = Kernel::GetMemoryManager(); const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize); const size_t num_pages = total_size / PageSize; MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, total_size)); /* Allocate memory for the image. */ const KMemoryManager::Pool pool = static_cast(KSystemControl::GetInitialProcessBinaryPool()); const auto allocate_option = KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront); KVirtualAddress allocated_memory = mm.AllocateContinuous(num_pages, 1, allocate_option); MESOSPHERE_ABORT_UNLESS(allocated_memory != Null); mm.Open(allocated_memory, num_pages); /* Relocate the image. */ std::memmove(GetVoidPointer(allocated_memory), GetVoidPointer(GetInitialProcessBinaryAddress()), g_initial_process_binary_header.size); std::memset(GetVoidPointer(GetInitialProcessBinaryAddress()), 0, g_initial_process_binary_header.size); g_initial_process_binary_address = allocated_memory; } } void CreateAndRunInitialProcesses() { /* Allocate space for the processes. */ InitialProcessInfo *infos = static_cast(__builtin_alloca(sizeof(InitialProcessInfo) * g_initial_process_binary_header.num_processes)); /* Create the processes. */ CreateProcesses(infos, g_initial_process_binary_address, g_initial_process_binary_header); /* Release the memory used by the image. */ { const size_t total_size = util::AlignUp(g_initial_process_binary_header.size, PageSize); const size_t num_pages = total_size / PageSize; Kernel::GetMemoryManager().Close(g_initial_process_binary_address, num_pages); Kernel::GetSystemResourceLimit().Release(ams::svc::LimitableResource_PhysicalMemoryMax, total_size); } /* Determine the initial process id range. */ for (size_t i = 0; i < g_initial_process_binary_header.num_processes; i++) { const auto pid = infos[i].process->GetId(); g_initial_process_id_min = std::min(g_initial_process_id_min, pid); g_initial_process_id_max = std::max(g_initial_process_id_max, pid); } /* 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);"); } } }