kern: skeleton initial process loading

This commit is contained in:
Michael Scire 2020-02-15 03:44:41 -08:00
parent 30d6b359f9
commit cbc73f4407
7 changed files with 465 additions and 1 deletions

View file

@ -15,6 +15,7 @@
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_initial_process_reader.hpp>
namespace ams::kern {
@ -29,6 +30,7 @@ namespace ams::kern {
};
NOINLINE void CopyInitialProcessBinaryToKernelMemory();
NOINLINE void CreateAndRunInitialProcesses();
u64 GetInitialProcessIdMin();
u64 GetInitialProcessIdMax();

View file

@ -0,0 +1,51 @@
/*
* 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/>.
*/
#pragma once
namespace ams::kern {
struct KAddressSpaceInfo {
public:
enum Type {
Type_32Bit = 0,
Type_Small64Bit = 1,
Type_Large64Bit = 2,
Type_Heap = 3,
Type_Stack = 4,
Type_Alias = 5,
Type_Count,
};
private:
size_t bit_width;
size_t address;
size_t size;
Type type;
public:
static uintptr_t GetAddressSpaceStart(size_t width, Type type);
static size_t GetAddressSpaceSize(size_t width, Type type);
constexpr KAddressSpaceInfo(size_t bw, size_t a, size_t s, Type t) : bit_width(bw), address(a), size(s), type(t) { /* ... */ }
constexpr size_t GetWidth() const { return this->bit_width; }
constexpr size_t GetAddress() const { return this->address; }
constexpr size_t GetSize() const { return this->size; }
constexpr Type GetType() const { return this->type; }
};
}

View file

@ -0,0 +1,132 @@
/*
* 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/>.
*/
#pragma once
#include <mesosphere/kern_common.hpp>
#include <mesosphere/kern_k_address_space_info.hpp>
namespace ams::kern {
class KInitialProcessHeader {
private:
static constexpr u32 Magic = util::FourCC<'K','I','P','1'>::Code;
private:
u32 magic;
u8 name[12];
u64 program_id;
u32 version;
u8 priority;
u8 ideal_core_id;
u8 _1E;
u8 flags;
u32 rx_address;
u32 rx_size;
u32 rx_compressed_size;
u32 affinity_mask;
u32 ro_address;
u32 ro_size;
u32 ro_compressed_size;
u32 stack_size;
u32 rw_address;
u32 rw_size;
u32 rw_compressed_size;
u32 _4C;
u32 bss_address;
u32 bss_size;
u32 pad[(0x80 - 0x58) / sizeof(u32)];
u32 capabilities[0x80 / sizeof(u32)];
public:
constexpr bool IsValid() const { return this->magic == Magic; }
constexpr void GetName(char *dst, size_t size) const {
std::memset(dst, 0, size);
std::memcpy(dst, this->name, std::min(sizeof(this->name), size));
}
constexpr const u32 *GetCapabilities() const { return this->capabilities; }
constexpr size_t GetNumCapabilities() const { return util::size(this->capabilities); }
constexpr u64 GetProgramId() const { return this->program_id; }
constexpr u32 GetVersion() const { return this->version; }
constexpr u8 GetPriority() const { return this->priority; }
constexpr u8 GetIdealCore() const { return this->ideal_core_id; }
constexpr bool IsRxCompressed() const { return (this->flags & (1 << 0)); }
constexpr bool IsRoCompressed() const { return (this->flags & (1 << 1)); }
constexpr bool IsRwCompressed() const { return (this->flags & (1 << 2)); }
constexpr bool Is64Bit() const { return (this->flags & (1 << 3)); }
constexpr bool Is64BitAddressSpace() const { return (this->flags & (1 << 4)); }
constexpr bool UsesSecureMemory() const { return (this->flags & (1 << 5)); }
constexpr u32 GetRxAddress() const { return this->rx_address; }
constexpr u32 GetRxSize() const { return this->rx_size; }
constexpr u32 GetRxCompressedSize() const { return this->rx_compressed_size; }
constexpr u32 GetRoAddress() const { return this->ro_address; }
constexpr u32 GetRoSize() const { return this->ro_size; }
constexpr u32 GetRoCompressedSize() const { return this->ro_compressed_size; }
constexpr u32 GetRwAddress() const { return this->rw_address; }
constexpr u32 GetRwSize() const { return this->rw_size; }
constexpr u32 GetRwCompressedSize() const { return this->rw_compressed_size; }
constexpr u32 GetBssAddress() const { return this->bss_address; }
constexpr u32 GetBssSize() const { return this->bss_size; }
constexpr u32 GetAffinityMask() const { return this->affinity_mask; }
constexpr u32 GetStackSize() const { return this->stack_size; }
};
static_assert(sizeof(KInitialProcessHeader) == 0x100);
class KInitialProcessReader {
private:
KInitialProcessHeader *kip_header;
public:
constexpr KInitialProcessReader() : kip_header() { /* ... */ }
constexpr const u32 *GetCapabilities() const { return this->kip_header->GetCapabilities(); }
constexpr size_t GetNumCapabilities() const { return this->kip_header->GetNumCapabilities(); }
constexpr size_t GetBinarySize() const {
return sizeof(*kip_header) + this->kip_header->GetRxCompressedSize() + this->kip_header->GetRoCompressedSize() + this->kip_header->GetRwCompressedSize();
}
constexpr size_t GetSize() const {
if (const size_t bss_size = this->kip_header->GetBssSize(); bss_size != 0) {
return this->kip_header->GetBssAddress() + this->kip_header->GetBssSize();
} else {
return this->kip_header->GetRwAddress() + this->kip_header->GetRwSize();
}
}
constexpr u8 GetPriority() const { return this->kip_header->GetPriority(); }
constexpr u8 GetIdealCore() const { return this->kip_header->GetIdealCore(); }
constexpr u32 GetAffinityMask() const { return this->kip_header->GetAffinityMask(); }
constexpr u32 GetStackSize() const { return this->kip_header->GetStackSize(); }
constexpr bool Is64Bit() const { return this->kip_header->Is64Bit(); }
constexpr bool Is64BitAddressSpace() const { return this->kip_header->Is64BitAddressSpace(); }
constexpr bool UsesSecureMemory() const { return this->kip_header->UsesSecureMemory(); }
bool Attach(u8 *bin) {
if (KInitialProcessHeader *header = reinterpret_cast<KInitialProcessHeader *>(bin); header->IsValid()) {
this->kip_header = header;
return true;
} else {
return false;
}
}
Result MakeCreateProcessParameter(ams::svc::CreateProcessParameter *out, bool enable_aslr) const;
};
}

View file

@ -19,6 +19,12 @@ namespace ams::kern {
namespace {
struct InitialProcessInfo {
KProcess *process;
size_t stack_size;
s32 priority;
};
KVirtualAddress GetInitialProcessBinaryAddress() {
return KMemoryLayout::GetPageTableHeapRegion().GetEndAddress() - InitialProcessBinarySizeMax;
}
@ -32,6 +38,59 @@ namespace ams::kern {
MESOSPHERE_ABORT_UNLESS(header->num_processes <= init::GetSlabResourceCounts().num_KProcess);
}
void CreateProcesses(InitialProcessInfo *infos, KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) {
u8 *current = GetPointer<u8>(binary_address + sizeof(InitialProcessBinaryHeader));
const u8 * const end = GetPointer<u8>(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_TODO("Reserve memory");
/* Create the process, and ensure we don't leak pages. */
{
/* Allocate memory for the process. */
MESOSPHERE_TODO("Allocate memory for the process");
/* Map the process's memory into the temporary region. */
MESOSPHERE_TODO("Map the process's page group");
/* Load the process. */
MESOSPHERE_TODO("Load the process");
/* Unmap the temporary mapping. */
MESOSPHERE_TODO("Unmap the process's page group");
/* 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 = /* TODO */ nullptr;
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<u64>::max();
@ -71,4 +130,32 @@ namespace ams::kern {
}
}
void CreateAndRunInitialProcesses() {
/* Allocate space for the processes. */
InitialProcessInfo *infos = static_cast<InitialProcessInfo *>(__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);");
}
}
}

View file

@ -0,0 +1,103 @@
/*
* 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 {
namespace {
constexpr uintptr_t Invalid = std::numeric_limits<uintptr_t>::max();
constexpr KAddressSpaceInfo AddressSpaceInfos[] = {
{ .bit_width = 32, .address = 2_MB, .size = 1_GB - 2_MB, KAddressSpaceInfo::Type_32Bit, },
{ .bit_width = 32, .address = 1_GB, .size = 4_GB - 1_GB, KAddressSpaceInfo::Type_Small64Bit, },
{ .bit_width = 32, .address = Invalid, .size = 1_GB, KAddressSpaceInfo::Type_Heap, },
{ .bit_width = 32, .address = Invalid, .size = 1_GB, KAddressSpaceInfo::Type_Alias, },
{ .bit_width = 36, .address = 128_MB, .size = 2_GB - 128_MB, KAddressSpaceInfo::Type_32Bit, },
{ .bit_width = 36, .address = 2_GB, .size = 64_GB - 2_GB, KAddressSpaceInfo::Type_Small64Bit, },
{ .bit_width = 36, .address = Invalid, .size = 6_GB, KAddressSpaceInfo::Type_Heap, },
{ .bit_width = 36, .address = Invalid, .size = 6_GB, KAddressSpaceInfo::Type_Alias, },
{ .bit_width = 39, .address = 128_MB, .size = 512_GB - 128_MB, KAddressSpaceInfo::Type_Large64Bit, },
{ .bit_width = 39, .address = Invalid, .size = 64_GB, KAddressSpaceInfo::Type_32Bit, },
{ .bit_width = 39, .address = Invalid, .size = 6_GB, KAddressSpaceInfo::Type_Heap, },
{ .bit_width = 39, .address = Invalid, .size = 64_GB, KAddressSpaceInfo::Type_Alias, },
{ .bit_width = 39, .address = Invalid, .size = 2_GB, KAddressSpaceInfo::Type_Stack, },
};
constexpr bool IsAllowedIndexForAddress(size_t index) {
return index < util::size(AddressSpaceInfos) && AddressSpaceInfos[index].GetAddress() != Invalid;
}
constexpr size_t AddressSpaceIndices32Bit[KAddressSpaceInfo::Type_Count] = {
0, 1, 0, 2, 0, 3,
};
constexpr size_t AddressSpaceIndices36Bit[KAddressSpaceInfo::Type_Count] = {
4, 5, 4, 6, 4, 7,
};
constexpr size_t AddressSpaceIndices39Bit[KAddressSpaceInfo::Type_Count] = {
9, 8, 8, 10, 12, 11,
};
constexpr bool IsAllowed32BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type_Count && type != KAddressSpaceInfo::Type_Large64Bit && type != KAddressSpaceInfo::Type_Stack;
}
constexpr bool IsAllowed36BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type_Count && type != KAddressSpaceInfo::Type_Large64Bit && type != KAddressSpaceInfo::Type_Stack;
}
constexpr bool IsAllowed39BitType(KAddressSpaceInfo::Type type) {
return type < KAddressSpaceInfo::Type_Count && type != KAddressSpaceInfo::Type_Small64Bit;
}
}
uintptr_t KAddressSpaceInfo::GetAddressSpaceStart(size_t width, KAddressSpaceInfo::Type type) {
switch (width) {
case 32:
MESOSPHERE_ABORT_UNLESS(IsAllowed32BitType(type));
MESOSPHERE_ABORT_UNLESS(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[type]));
return AddressSpaceInfos[AddressSpaceIndices32Bit[type]].GetAddress();
case 36:
MESOSPHERE_ABORT_UNLESS(IsAllowed36BitType(type));
MESOSPHERE_ABORT_UNLESS(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[type]));
return AddressSpaceInfos[AddressSpaceIndices36Bit[type]].GetAddress();
case 39:
MESOSPHERE_ABORT_UNLESS(IsAllowed39BitType(type));
MESOSPHERE_ABORT_UNLESS(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[type]));
return AddressSpaceInfos[AddressSpaceIndices39Bit[type]].GetAddress();
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}
size_t KAddressSpaceInfo::GetAddressSpaceSize(size_t width, KAddressSpaceInfo::Type type) {
switch (width) {
case 32:
MESOSPHERE_ABORT_UNLESS(IsAllowed32BitType(type));
return AddressSpaceInfos[AddressSpaceIndices32Bit[type]].GetSize();
case 36:
MESOSPHERE_ABORT_UNLESS(IsAllowed36BitType(type));
return AddressSpaceInfos[AddressSpaceIndices36Bit[type]].GetSize();
case 39:
MESOSPHERE_ABORT_UNLESS(IsAllowed39BitType(type));
return AddressSpaceInfos[AddressSpaceIndices39Bit[type]].GetSize();
MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
}
}
}

View file

@ -0,0 +1,88 @@
/*
* 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 KInitialProcessReader::MakeCreateProcessParameter(ams::svc::CreateProcessParameter *out, bool enable_aslr) const {
/* Get and validate addresses/sizes. */
const uintptr_t rx_address = this->kip_header->GetRxAddress();
const size_t rx_size = this->kip_header->GetRxSize();
const uintptr_t ro_address = this->kip_header->GetRoAddress();
const size_t ro_size = this->kip_header->GetRoSize();
const uintptr_t rw_address = this->kip_header->GetRwAddress();
const size_t rw_size = this->kip_header->GetRwSize();
const uintptr_t bss_address = this->kip_header->GetBssAddress();
const size_t bss_size = this->kip_header->GetBssSize();
R_UNLESS(util::IsAligned(rx_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(util::IsAligned(ro_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(util::IsAligned(rw_address, PageSize), svc::ResultInvalidAddress());
R_UNLESS(rx_address <= rx_address + util::AlignUp(rx_size, PageSize), svc::ResultInvalidAddress());
R_UNLESS(ro_address <= ro_address + util::AlignUp(ro_size, PageSize), svc::ResultInvalidAddress());
R_UNLESS(rw_address <= rw_address + util::AlignUp(rw_size, PageSize), svc::ResultInvalidAddress());
R_UNLESS(bss_address <= bss_address + util::AlignUp(bss_size, PageSize), svc::ResultInvalidAddress());
R_UNLESS(rx_address + util::AlignUp(rx_size, PageSize) <= ro_address, svc::ResultInvalidAddress());
R_UNLESS(ro_address + util::AlignUp(ro_size, PageSize) <= rw_address, svc::ResultInvalidAddress());
R_UNLESS(rw_address + rw_size <= bss_address, svc::ResultInvalidAddress());
/* Validate the address space. */
if (this->Is64BitAddressSpace()) {
R_UNLESS(this->Is64Bit(), svc::ResultInvalidCombination());
}
using ASType = KAddressSpaceInfo::Type;
const uintptr_t start_address = rx_address;
const uintptr_t end_address = bss_size > 0 ? bss_address + bss_size : rw_address + rw_size;
const size_t as_width = this->Is64BitAddressSpace() ? 39 : 32;
const ASType as_type = this->Is64BitAddressSpace() ? KAddressSpaceInfo::Type_Large64Bit : KAddressSpaceInfo::Type_32Bit;
const uintptr_t map_start = KAddressSpaceInfo::GetAddressSpaceStart(as_width, as_type);
const size_t map_size = KAddressSpaceInfo::GetAddressSpaceSize(as_width, as_type);
const uintptr_t map_end = map_start + map_size;
MESOSPHERE_ABORT_UNLESS(start_address == 0);
/* Set fields in parameter. */
out->code_address = map_start + start_address;
out->code_num_pages = util::AlignUp(end_address - start_address, PageSize);
out->program_id = this->kip_header->GetProgramId();
out->version = this->kip_header->GetVersion();
out->flags = 0;
MESOSPHERE_ABORT_UNLESS((out->code_address / PageSize) + out->code_num_pages <= (map_end / PageSize));
/* Copy name field. */
this->kip_header->GetName(out->name, sizeof(out->name));
/* Apply ASLR, if needed. */
if (enable_aslr) {
const size_t choices = (map_end / KernelAslrAlignment) - (util::AlignUp(out->code_address + out->code_num_pages * PageSize, KernelAslrAlignment) / KernelAslrAlignment);
out->code_address += KSystemControl::GenerateRandomRange(0, choices) * KernelAslrAlignment;
out->flags |= ams::svc::CreateProcessFlag_EnableAslr;
}
/* Apply other flags. */
if (this->Is64Bit()) {
out->flags |= ams::svc::CreateProcessFlag_Is64Bit;
}
if (this->Is64BitAddressSpace()) {
out->flags |= ams::svc::CreateProcessFlag_AddressSpace64Bit;
} else {
out->flags |= ams::svc::CreateProcessFlag_AddressSpace32Bit;
}
return ResultSuccess();
}
}

View file

@ -116,7 +116,8 @@ namespace ams::kern {
/* Initialize the SMMU. */
KDeviceAddressSpace::Initialize();
MESOSPHERE_TODO("CreateAndRunInitialProcesses();");
/* Load the initial processes. */
CreateAndRunInitialProcesses();
/* We're done initializing! */
Kernel::SetState(Kernel::State::Initialized);