diff --git a/libmesosphere/include/mesosphere.hpp b/libmesosphere/include/mesosphere.hpp index 77e798d9..0da2f63e 100644 --- a/libmesosphere/include/mesosphere.hpp +++ b/libmesosphere/include/mesosphere.hpp @@ -23,6 +23,13 @@ /* Primitive types. */ #include "mesosphere/kern_k_typed_address.hpp" +#include "mesosphere/kern_initial_process.hpp" + +/* Initialization headers. */ +#include "mesosphere/init/kern_init_elf.hpp" +#include "mesosphere/init/kern_init_layout.hpp" +#include "mesosphere/init/kern_init_page_table_select.hpp" /* Core functionality. */ +#include "mesosphere/kern_select_interrupts.hpp" #include "mesosphere/kern_select_k_system_control.hpp" diff --git a/libmesosphere/include/mesosphere/arch/arm64/init/kern_init_elf64.hpp b/libmesosphere/include/mesosphere/arch/arm64/init/kern_init_elf64.hpp new file mode 100644 index 00000000..33ef192f --- /dev/null +++ b/libmesosphere/include/mesosphere/arch/arm64/init/kern_init_elf64.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +/* +From musl include/elf.h + +Copyright © 2005-2014 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#pragma once +#include + +namespace ams::kern::init::Elf::Elf64 { + + /* Type declarations required to perform relocations */ + using Half = u16; + using Word = u32; + using Sword = s32; + using Xword = u64; + using SXword = s64; + + using Addr = u64; + using Off = u64; + + class Dyn { + private: + SXword tag; + union { + Xword value; + Addr ptr; + }; + public: + constexpr ALWAYS_INLINE SXword GetTag() const { + return this->tag; + } + + constexpr ALWAYS_INLINE Xword GetValue() const { + return this->value; + } + + constexpr ALWAYS_INLINE Addr GetPtr() const { + return this->ptr; + } + }; + + class Rel { + private: + Addr offset; + Xword info; + public: + constexpr ALWAYS_INLINE Addr GetOffset() const { + return this->offset; + } + + constexpr ALWAYS_INLINE Xword GetSym() const { + return this->info >> 32; + } + + constexpr ALWAYS_INLINE Xword GetType() const { + return this->info & 0xFFFFFFFF; + } + }; + + class Rela { + private: + Addr offset; + Xword info; + SXword addend; + public: + constexpr ALWAYS_INLINE Addr GetOffset() const { + return this->offset; + } + + constexpr ALWAYS_INLINE Xword GetSym() const { + return this->info >> 32; + } + + constexpr ALWAYS_INLINE Xword GetType() const { + return this->info & 0xFFFFFFFF; + } + + constexpr ALWAYS_INLINE SXword GetAddend() const { + return this->addend; + } + }; + + enum DynamicTag { + DT_NULL = 0, + DT_RELA = 7, + DT_RELAENT = 9, + DT_REL = 17, + DT_RELENT = 19, + + DT_RELACOUNT = 0x6ffffff9, + DT_RELCOUNT = 0x6ffffffa + }; + + enum RelocationType { + R_AARCH64_RELATIVE = 0x403, + }; + + /* API to apply relocations or call init array. */ + void ApplyRelocations(uintptr_t base_address, const Dyn *dynamic); + void CallInitArrayFuncs(uintptr_t init_array_start, uintptr_t init_array_end); + +} \ No newline at end of file diff --git a/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp b/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp new file mode 100644 index 00000000..2d2c6fc3 --- /dev/null +++ b/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +/* +From musl include/elf.h + +Copyright © 2005-2014 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#pragma once +#include +#include +#include +#include "../kern_hardware_registers.hpp" + +namespace ams::kern::init { + + constexpr size_t PageSize = 0x1000; + constexpr size_t L1BlockSize = 0x40000000; + constexpr size_t L2BlockSize = 0x200000; + constexpr size_t L2ContiguousBlockSize = 0x10 * L2BlockSize; + constexpr size_t L3BlockSize = 0x1000; + constexpr size_t L3ContiguousBlockSize = 0x10 * L3BlockSize; + + class PageTableEntry { + public: + enum Permission : u64 { + Permission_KernelRWX = ((0ul << 53) | (1ul << 54) | (0ul << 6)), + Permission_KernelRX = ((0ul << 53) | (1ul << 54) | (2ul << 6)), + Permission_KernelR = ((1ul << 53) | (1ul << 54) | (2ul << 6)), + Permission_KernelRW = ((1ul << 53) | (1ul << 54) | (0ul << 6)), + + Permission_UserRX = ((1ul << 53) | (0ul << 54) | (3ul << 6)), + Permission_UserR = ((1ul << 53) | (1ul << 54) | (3ul << 6)), + Permission_UserRW = ((1ul << 53) | (1ul << 54) | (1ul << 6)), + }; + + enum Shareable : u64 { + Shareable_NonShareable = (0 << 8), + Shareable_OuterShareable = (2 << 8), + Shareable_InnerShareable = (3 << 8), + }; + + /* Official attributes are: */ + /* 0x00, 0x04, 0xFF, 0x44. 4-7 are unused. */ + enum PageAttribute : u64 { + PageAttribute_Device_nGnRnE = (0 << 2), + PageAttribute_Device_nGnRE = (1 << 2), + PageAttribute_NormalMemory = (2 << 2), + PageAttribute_NormalMemoryNotCacheable = (3 << 2), + }; + + enum AccessFlag : u64 { + AccessFlag_NotAccessed = (0 << 10), + AccessFlag_Accessed = (1 << 10), + }; + protected: + u64 attributes; + public: + /* Take in a raw attribute. */ + constexpr ALWAYS_INLINE PageTableEntry(u64 attr) : attributes(attr) { /* ... */ } + + /* Extend a previous attribute. */ + constexpr ALWAYS_INLINE PageTableEntry(const PageTableEntry &rhs, u64 new_attr) : attributes(rhs.attributes | new_attr) { /* ... */ } + + /* Construct a new attribute. */ + constexpr ALWAYS_INLINE PageTableEntry(Permission perm, PageAttribute p_a, Shareable share) + : attributes(static_cast(perm) | static_cast(AccessFlag_Accessed) | static_cast(p_a) | static_cast(share)) + { + /* ... */ + } + protected: + constexpr ALWAYS_INLINE u64 GetBits(size_t offset, size_t count) const { + return (this->attributes >> offset) & ((1 << count) - 1); + } + + constexpr ALWAYS_INLINE u64 SelectBits(size_t offset, size_t count) const { + return this->attributes & (((1 << count) - 1) << offset); + } + public: + constexpr ALWAYS_INLINE bool IsUserExecuteNever() const { return this->GetBits(54, 1) != 0; } + constexpr ALWAYS_INLINE bool IsPrivilegedExecuteNever() const { return this->GetBits(53, 1) != 0; } + constexpr ALWAYS_INLINE bool IsContiguous() const { return this->GetBits(52, 1) != 0; } + constexpr ALWAYS_INLINE AccessFlag GetAccessFlag() const { return static_cast(this->GetBits(10, 1)); } + constexpr ALWAYS_INLINE Shareable GetShareable() const { return static_cast(this->GetBits(8, 2)); } + constexpr ALWAYS_INLINE PageAttribute GetPageAttribute() const { return static_cast(this->GetBits(2, 3)); } + constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBits(5, 1) != 0; } + constexpr ALWAYS_INLINE bool IsBlock() const { return this->GetBits(0, 2) == 0x1; } + constexpr ALWAYS_INLINE bool IsTable() const { return this->GetBits(0, 2) == 0x3; } + }; + + static_assert(sizeof(PageTableEntry) == sizeof(u64)); + + constexpr size_t MaxPageTableEntries = PageSize / sizeof(PageTableEntry); + + class L1PageTableEntry : public PageTableEntry { + public: + constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, bool pxn) + : PageTableEntry((0x3ul << 60) | (static_cast(pxn) << 59) | GetInteger(phys_addr) | 0x3) + { + /* ... */ + } + constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig) + : PageTableEntry(attr, (static_cast(contig) << 52) | GetInteger(phys_addr) | 0x1) + { + /* ... */ + } + + constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const { + return this->SelectBits(30, 18); + } + constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const { + return this->SelectBits(12, 36); + } + }; + + class L2PageTableEntry : public PageTableEntry { + public: + constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, bool pxn) + : PageTableEntry((0x3ul << 60) | (static_cast(pxn) << 59) | GetInteger(phys_addr) | 0x3) + { + /* ... */ + } + constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig) + : PageTableEntry(attr, (static_cast(contig) << 52) | GetInteger(phys_addr) | 0x1) + { + /* ... */ + } + + constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const { + return this->SelectBits(21, 27); + } + constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const { + return this->SelectBits(12, 36); + } + }; + + class L3PageTableEntry : public PageTableEntry { + public: + constexpr ALWAYS_INLINE L3PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig) + : PageTableEntry(attr, (static_cast(contig) << 52) | GetInteger(phys_addr) | 0x1) + { + /* ... */ + } + + constexpr ALWAYS_INLINE KPhysicalAddress GetBlock() const { + return this->SelectBits(21, 27); + } + constexpr ALWAYS_INLINE KPhysicalAddress GetTable() const { + return this->SelectBits(12, 36); + } + }; + + + class KInitialPageTable { + public: + class IPageAllocator { + public: + virtual KPhysicalAddress Allocate() { return Null; } + virtual void Free(KPhysicalAddress phys_addr) { /* Nothing to do here. */ (void)(phys_addr); } + }; + private: + KPhysicalAddress l1_table; + public: + constexpr ALWAYS_INLINE KInitialPageTable(KPhysicalAddress l1) : l1_table(l1) { + ClearNewPageTable(this->l1_table); + } + private: + static constexpr ALWAYS_INLINE L1PageTableEntry *GetL1Entry(KPhysicalAddress _l1_table, KVirtualAddress address) { + L1PageTableEntry *l1_table = reinterpret_cast(GetInteger(_l1_table)); + return l1_table + ((GetInteger(address) >> 30) & (MaxPageTableEntries - 1)); + } + + static constexpr ALWAYS_INLINE L2PageTableEntry *GetL2Entry(const L1PageTableEntry *entry, KVirtualAddress address) { + L2PageTableEntry *l2_table = reinterpret_cast(GetInteger(entry->GetTable())); + return l2_table + ((GetInteger(address) >> 21) & (MaxPageTableEntries - 1)); + } + + static constexpr ALWAYS_INLINE L3PageTableEntry *GetL3Entry(const L2PageTableEntry *entry, KVirtualAddress address) { + L3PageTableEntry *l3_table = reinterpret_cast(GetInteger(entry->GetTable())); + return l3_table + ((GetInteger(address) >> 12) & (MaxPageTableEntries - 1)); + } + + static ALWAYS_INLINE void ClearNewPageTable(KPhysicalAddress address) { + /* This Physical Address -> void * conversion is valid, because this is page table code. */ + /* The MMU is necessarily not yet turned on, if we are creating an initial page table. */ + std::memset(reinterpret_cast(GetInteger(address)), 0, PageSize); + } + public: + void Map(KVirtualAddress virt_addr, size_t size, KPhysicalAddress phys_addr, const PageTableEntry &attr, IPageAllocator &allocator) { + /* Ensure that addresses and sizes are page aligned. */ + MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(virt_addr), PageSize)); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(phys_addr), PageSize)); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize)); + + /* Iteratively map pages until the requested region is mapped. */ + while (size > 0) { + L1PageTableEntry *l1_entry = GetL1Entry(this->l1_table, virt_addr); + + /* Can we make an L1 block? */ + if (util::IsAligned(GetInteger(virt_addr), L1BlockSize) && util::IsAligned(GetInteger(phys_addr), L1BlockSize) && util::IsAligned(size, L1BlockSize)) { + *l1_entry = L1PageTableEntry(phys_addr, attr, false); + /* TODO: DataSynchronizationBarrier */ + virt_addr += L1BlockSize; + phys_addr += L1BlockSize; + size -= L1BlockSize; + continue; + } + + /* If we don't already have an L2 table, we need to make a new one. */ + if (!l1_entry->IsTable()) { + KPhysicalAddress new_table = allocator.Allocate(); + ClearNewPageTable(new_table); + *l1_entry = L1PageTableEntry(phys_addr, attr.IsPrivilegedExecuteNever()); + } + + L2PageTableEntry *l2_entry = GetL2Entry(l1_entry, virt_addr); + + /* Can we make a contiguous L2 block? */ + if (util::IsAligned(GetInteger(virt_addr), L2ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L2ContiguousBlockSize) && util::IsAligned(size, L2ContiguousBlockSize)) { + for (size_t i = 0; i < L2ContiguousBlockSize / L2BlockSize; i++) { + l2_entry[i] = L2PageTableEntry(phys_addr, attr, true); + /* TODO: DataSynchronizationBarrier */ + virt_addr += L2ContiguousBlockSize; + phys_addr += L2ContiguousBlockSize; + size -= L2ContiguousBlockSize; + } + continue; + } + + /* Can we make an L2 block? */ + if (util::IsAligned(GetInteger(virt_addr), L2BlockSize) && util::IsAligned(GetInteger(phys_addr), L2BlockSize) && util::IsAligned(size, L2BlockSize)) { + *l2_entry = L2PageTableEntry(phys_addr, attr, false); + /* TODO: DataSynchronizationBarrier */ + virt_addr += L2BlockSize; + phys_addr += L2BlockSize; + size -= L2BlockSize; + continue; + } + + /* If we don't already have an L3 table, we need to make a new one. */ + if (!l2_entry->IsTable()) { + KPhysicalAddress new_table = allocator.Allocate(); + ClearNewPageTable(new_table); + *l2_entry = L2PageTableEntry(phys_addr, attr.IsPrivilegedExecuteNever()); + } + + L3PageTableEntry *l3_entry = GetL3Entry(l2_entry, virt_addr); + + /* Can we make a contiguous L3 block? */ + if (util::IsAligned(GetInteger(virt_addr), L3ContiguousBlockSize) && util::IsAligned(GetInteger(phys_addr), L3ContiguousBlockSize) && util::IsAligned(size, L3ContiguousBlockSize)) { + for (size_t i = 0; i < L3ContiguousBlockSize / L3BlockSize; i++) { + l3_entry[i] = L3PageTableEntry(phys_addr, attr, true); + /* TODO: DataSynchronizationBarrier */ + virt_addr += L3ContiguousBlockSize; + phys_addr += L3ContiguousBlockSize; + size -= L3ContiguousBlockSize; + } + continue; + } + + /* Make an L3 block. */ + *l3_entry = L3PageTableEntry(phys_addr, attr, false); + /* TODO: DataSynchronizationBarrier */ + virt_addr += L3BlockSize; + phys_addr += L3BlockSize; + size -= L3BlockSize; + } + } + + bool IsFree(KVirtualAddress virt_addr, size_t size) { + /* TODO */ + return false; + } + + void ReprotectFromReadWriteToRead(KVirtualAddress virt_addr, size_t size) { + /* TODO */ + } + + }; + +} \ No newline at end of file diff --git a/libmesosphere/include/mesosphere/arch/arm64/kern_hardware_registers.hpp b/libmesosphere/include/mesosphere/arch/arm64/kern_hardware_registers.hpp new file mode 100644 index 00000000..74da7387 --- /dev/null +++ b/libmesosphere/include/mesosphere/arch/arm64/kern_hardware_registers.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +/* +From musl include/elf.h + +Copyright © 2005-2014 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#pragma once +#include + +namespace ams::kern::hw { + + + +} diff --git a/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp b/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp index d28a1230..3426f274 100644 --- a/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp +++ b/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp @@ -21,6 +21,10 @@ namespace ams::kern { class KSystemControl { public: + /* Initialization. */ + static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address); + static bool ShouldIncreaseResourceRegionSize(); + /* Panic. */ static NORETURN void StopSystem(); }; diff --git a/libmesosphere/include/mesosphere/init/kern_init_elf.hpp b/libmesosphere/include/mesosphere/init/kern_init_elf.hpp new file mode 100644 index 00000000..2ee1e001 --- /dev/null +++ b/libmesosphere/include/mesosphere/init/kern_init_elf.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include + +#ifdef ATMOSPHERE_ARCH_ARM64 + + #include "../arch/arm64/init/kern_init_elf64.hpp" + +#else + + #error "Unknown Architecture" + +#endif + +namespace ams::kern::init::Elf { + + /* TODO: Anything we want inside this namespace? */ + +} \ No newline at end of file diff --git a/libmesosphere/include/mesosphere/init/kern_init_layout.hpp b/libmesosphere/include/mesosphere/init/kern_init_layout.hpp new file mode 100644 index 00000000..1e3bfce8 --- /dev/null +++ b/libmesosphere/include/mesosphere/init/kern_init_layout.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include + +namespace ams::kern::init { + + struct KernelLayout { + u32 rx_offset; + u32 rx_end_offset; + u32 ro_offset; + u32 ro_end_offset; + u32 rw_offset; + u32 rw_end_offset; + u32 bss_offset; + u32 bss_end_offset; + u32 ini_end_offset; + u32 dynamic_end_offset; + u32 init_array_offset; + u32 init_array_end_offset; + }; + static_assert(std::is_pod::value); + static_assert(sizeof(KernelLayout) == 0x30); + +} \ No newline at end of file diff --git a/libmesosphere/include/mesosphere/init/kern_init_page_table_select.hpp b/libmesosphere/include/mesosphere/init/kern_init_page_table_select.hpp new file mode 100644 index 00000000..e0e6b423 --- /dev/null +++ b/libmesosphere/include/mesosphere/init/kern_init_page_table_select.hpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once + +#ifdef ATMOSPHERE_ARCH_ARM64 + #include "../arch/arm64/init/kern_k_init_page_table.hpp" +#else + #error "Unknown architecutre for KInitialPageTable" +#endif diff --git a/libmesosphere/include/mesosphere/kern_initial_process.hpp b/libmesosphere/include/mesosphere/kern_initial_process.hpp new file mode 100644 index 00000000..e901dd4c --- /dev/null +++ b/libmesosphere/include/mesosphere/kern_initial_process.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include +#include "kern_panic.hpp" + +namespace ams::kern { + + constexpr u32 InitialProcessBinaryMagic = util::FourCC<'I','N','I','1'>::Code; + constexpr size_t InitialProcessBinarySizeMax = 0xC00000; + + struct InitialProcessBinaryHeader { + u32 magic; + u32 size; + u32 num_processes; + u32 reserved; + }; + +} diff --git a/libmesosphere/include/mesosphere/kern_panic.hpp b/libmesosphere/include/mesosphere/kern_panic.hpp index 49a6e7b3..27c54cf4 100644 --- a/libmesosphere/include/mesosphere/kern_panic.hpp +++ b/libmesosphere/include/mesosphere/kern_panic.hpp @@ -32,7 +32,7 @@ namespace ams::kern { #ifdef MESOSPHERE_ENABLE_ASSERTIONS #define MESOSPHERE_ASSERT_IMPL(expr, ...) \ ({ \ - if (AMS_UNLIKELY(!expr)) { \ + if (AMS_UNLIKELY(!(expr))) { \ MESOSPHERE_PANIC(__VA_ARGS__); \ } \ }) @@ -47,7 +47,7 @@ namespace ams::kern { #define MESOSPHERE_ABORT_UNLESS(expr) \ ({ \ - if (AMS_UNLIKELY(!expr)) { \ + if (AMS_UNLIKELY(!(expr))) { \ MESOSPHERE_PANIC("Abort(): %s", #expr); \ } \ }) diff --git a/libmesosphere/include/mesosphere/kern_select_interrupts.hpp b/libmesosphere/include/mesosphere/kern_select_interrupts.hpp new file mode 100644 index 00000000..63a5c739 --- /dev/null +++ b/libmesosphere/include/mesosphere/kern_select_interrupts.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-2019 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 . + */ +#pragma once +#include +#include "kern_panic.hpp" + +namespace ams::kern { + + /* TODO: Actually select between architecture-specific interrupt code. */ + + + /* Enable or disable interrupts for the lifetime of an object. */ + class KScopedInterruptDisable { + NON_COPYABLE(KScopedInterruptDisable); + NON_MOVEABLE(KScopedInterruptDisable); + public: + KScopedInterruptDisable(); + ~KScopedInterruptDisable(); + }; + + class KScopedInterruptEnable { + NON_COPYABLE(KScopedInterruptEnable); + NON_MOVEABLE(KScopedInterruptEnable); + public: + KScopedInterruptEnable(); + ~KScopedInterruptEnable(); + }; + +} diff --git a/libmesosphere/include/mesosphere/kern_select_k_system_control.hpp b/libmesosphere/include/mesosphere/kern_select_k_system_control.hpp index 56ddc9f6..3c6b5b6d 100644 --- a/libmesosphere/include/mesosphere/kern_select_k_system_control.hpp +++ b/libmesosphere/include/mesosphere/kern_select_k_system_control.hpp @@ -19,4 +19,4 @@ #include "board/nintendo/switch/kern_k_system_control.hpp" #else #error "Unknown board for KSystemControl" -#endif \ No newline at end of file +#endif diff --git a/libmesosphere/source/arch/arm64/init/kern_init_elf64.cpp b/libmesosphere/source/arch/arm64/init/kern_init_elf64.cpp new file mode 100644 index 00000000..8e60b51e --- /dev/null +++ b/libmesosphere/source/arch/arm64/init/kern_init_elf64.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2019 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::init::Elf::Elf64 { + + /* API to apply relocations or call init array. */ + void ApplyRelocations(uintptr_t base_address, const Dyn *dynamic) { + uintptr_t dyn_rel = 0; + uintptr_t dyn_rela = 0; + uintptr_t rel_count = 0; + uintptr_t rela_count = 0; + uintptr_t rel_ent = 0; + uintptr_t rela_ent = 0; + + /* Iterate over all tags, identifying important extents. */ + for (const Dyn *cur_entry = dynamic; cur_entry->GetTag() != DT_NULL; cur_entry++) { + switch (cur_entry->GetTag()) { + case DT_REL: + dyn_rel = base_address + cur_entry->GetPtr(); + break; + case DT_RELA: + dyn_rela = base_address + cur_entry->GetPtr(); + break; + case DT_RELENT: + rel_ent = cur_entry->GetValue(); + break; + case DT_RELAENT: + rela_ent = cur_entry->GetValue(); + break; + case DT_RELCOUNT: + rel_count = cur_entry->GetValue(); + break; + case DT_RELACOUNT: + rela_count = cur_entry->GetValue(); + break; + } + } + + /* Apply all Rel relocations */ + for (size_t i = 0; i < rel_count; i++) { + const auto &rel = *reinterpret_cast(dyn_rel + rel_ent * i); + + /* Only allow R_AARCH64_RELATIVE relocations. */ + while (rel.GetType() != R_AARCH64_RELATIVE) { /* ... */ } + + /* Apply the relocation. */ + Elf64::Addr *target_address = reinterpret_cast(base_address + rel.GetOffset()); + *target_address += base_address; + } + + /* Apply all Rela relocations. */ + for (size_t i = 0; i < rela_count; i++) { + const auto &rela = *reinterpret_cast(dyn_rela + rela_ent * i); + + /* Only allow R_AARCH64_RELATIVE relocations. */ + while (rela.GetType() != R_AARCH64_RELATIVE) { /* ... */ } + + /* Apply the relocation. */ + Elf64::Addr *target_address = reinterpret_cast(base_address + rela.GetOffset()); + *target_address = base_address + rela.GetAddend(); + } + } + + void CallInitArrayFuncs(uintptr_t init_array_start, uintptr_t init_array_end) { + + } + +} \ No newline at end of file diff --git a/libmesosphere/source/arch/arm64/kern_init_elf64.cpp b/libmesosphere/source/arch/arm64/kern_init_elf64.cpp new file mode 100644 index 00000000..6f846763 --- /dev/null +++ b/libmesosphere/source/arch/arm64/kern_init_elf64.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018-2019 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::init::Elf::Elf64 { + + /* API to apply relocations or call init array. */ + void ApplyRelocations(uintptr_t base_address, const Dyn *dynamic) { + uintptr_t dyn_rel = 0; + uintptr_t dyn_rela = 0; + uintptr_t rel_count = 0; + uintptr_t rela_count = 0; + uintptr_t rel_ent = 0; + uintptr_t rela_ent = 0; + + /* Iterate over all tags, identifying important extents. */ + for (const Dyn *cur_entry = dynamic; cur_entry->GetTag() != DT_NULL; cur_entry++) { + switch (cur_entry->GetTag()) { + case DT_REL: + dyn_rel = base_address + cur_entry->GetPtr(); + break; + case DT_RELA: + dyn_rela = base_address + cur_entry->GetPtr(); + break; + case DT_RELENT: + rel_ent = cur_entry->GetValue(); + break; + case DT_RELAENT: + rela_ent = cur_entry->GetValue(); + break; + case DT_RELCOUNT: + rel_count = cur_entry->GetValue(); + break; + case DT_RELACOUNT: + rela_count = cur_entry->GetValue(); + break; + } + } + + /* Apply all Rel relocations */ + for (size_t i = 0; i < rel_count; i++) { + const auto &rel = *reinterpret_cast(dyn_rel + rel_ent * i); + + /* Only allow R_AARCH64_RELATIVE relocations. */ + while (rel.GetType() != R_AARCH64_RELATIVE) { /* ... */ } + + /* Apply the relocation. */ + Elf64::Addr *target_address = reinterpret_cast(base_address + rel.GetOffset()); + *target_address += base_address; + } + + /* Apply all Rela relocations. */ + for (size_t i = 0; i < rela_count; i++) { + const auto &rela = *reinterpret_cast(dyn_rela + rela_ent * i); + + /* Only allow R_AARCH64_RELATIVE relocations. */ + while (rela.GetType() != R_AARCH64_RELATIVE) { /* ... */ } + + /* Apply the relocation. */ + Elf64::Addr *target_address = reinterpret_cast(base_address + rela.GetOffset()); + *target_address = base_address + rela.GetAddend(); + } + } + + void CallInitArrayFuncs(uintptr_t init_array_start, uintptr_t init_array_end) { + for (uintptr_t cur_entry = init_array_start; cur_entry < init_array_end; cur_entry += sizeof(void *)) { + (*(void (**)())(cur_entry))(); + } + } + +} \ No newline at end of file diff --git a/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp b/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp index 0bc82900..f5cd9fc7 100644 --- a/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp +++ b/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp @@ -18,8 +18,64 @@ namespace ams::kern { + namespace { + + /* Convenience definitions. */ + constexpr size_t FourGigabytes = 0x100000000ul; + constexpr size_t SixGigabytes = 0x180000000ul; + constexpr size_t EightGigabytes = 0x200000000ul; + + size_t GetRealMemorySize() { + /* TODO: Move this into a header for the MC in general. */ + constexpr u32 MemoryControllerConfigurationRegister = 0x70019050; + u32 config_value; + MESOSPHERE_ABORT_UNLESS(smc::ReadWriteRegister(&config_value, MemoryControllerConfigurationRegister, 0, 0)); + return static_cast(config_value & 0x3FFF) << 20; + } + + inline u64 GetKernelConfiguration() { + u64 value = 0; + smc::GetConfig(&value, 1, smc::ConfigItem::KernelConfiguration); + return value; + } + + inline smc::MemoryMode GetMemoryMode() { + return static_cast((GetKernelConfiguration() >> 10) & 0x3); + } + + size_t GetIntendedMemorySize() { + const smc::MemoryMode memory_mode = GetMemoryMode(); + switch (memory_mode) { + case smc::MemoryMode_4GB: + default: /* All invalid modes should go to 4GB. */ + return FourGigabytes; + case smc::MemoryMode_6GB: + return SixGigabytes; + case smc::MemoryMode_8GB: + return EightGigabytes; + } + } + + } + + /* Initialization. */ + KPhysicalAddress KSystemControl::GetKernelPhysicalBaseAddress(uintptr_t base_address) { + const size_t real_dram_size = GetRealMemorySize(); + const size_t intended_dram_size = GetIntendedMemorySize(); + if (intended_dram_size * 2 < real_dram_size) { + return base_address; + } else { + return base_address + ((real_dram_size - intended_dram_size) / 2); + } + } + + bool KSystemControl::ShouldIncreaseResourceRegionSize() { + return (GetKernelConfiguration() >> 3) & 1; + } + void KSystemControl::StopSystem() { - /* TODO: smc::Panic(0xF00); */ + /* Display a panic screen via exosphere. */ + smc::Panic(0xF00); while (true) { /* ... */ } } diff --git a/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp b/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp new file mode 100644 index 00000000..b56b8371 --- /dev/null +++ b/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018-2019 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 +#include "kern_secure_monitor.hpp" + +namespace ams::kern::smc { + + namespace { + + struct SecureMonitorArguments { + u64 x[8]; + }; + + enum FunctionId : u32 { + FunctionId_CpuSuspend = 0xC4000001, + FunctionId_CpuOff = 0x84000002, + FunctionId_CpuOn = 0xC4000003, + FunctionId_GetConfig = 0xC3000004, + FunctionId_GenerateRandomBytes = 0xC3000005, + FunctionId_Panic = 0xC3000006, + FunctionId_ConfigureCarveout = 0xC3000007, + FunctionId_ReadWriteRegister = 0xC3000008, + }; + + void CallPrivilegedSecureMonitorFunction(SecureMonitorArguments &args) { + /* Load arguments into registers. */ + register u64 x0 asm("x0") = args.x[0]; + register u64 x1 asm("x1") = args.x[1]; + register u64 x2 asm("x2") = args.x[2]; + register u64 x3 asm("x3") = args.x[3]; + register u64 x4 asm("x4") = args.x[4]; + register u64 x5 asm("x5") = args.x[5]; + register u64 x6 asm("x6") = args.x[6]; + register u64 x7 asm("x7") = args.x[7]; + + /* Actually make the call. */ + { + /* Disable interrupts while making the call. */ + KScopedInterruptDisable intr_disable; + __asm__ __volatile__("smc #1" + : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7) + : + : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory" + ); + /* TODO: Restore X18 */ + } + + /* Store arguments to output. */ + args.x[0] = x0; + args.x[1] = x1; + args.x[2] = x2; + args.x[3] = x3; + args.x[4] = x4; + args.x[5] = x5; + args.x[6] = x6; + args.x[7] = x7; + } + + } + + void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { + SecureMonitorArguments args = { FunctionId_GetConfig, static_cast(config_item) }; + CallPrivilegedSecureMonitorFunction(args); + MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + for (size_t i = 0; i < num_qwords && i < 7; i++) { + out[i] = args.x[1 + i]; + } + } + + void NORETURN Panic(u32 color) { + SecureMonitorArguments args = { FunctionId_Panic, color }; + CallPrivilegedSecureMonitorFunction(args); + while (true) { /* ... */ } + } + + bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value) { + SecureMonitorArguments args = { FunctionId_ReadWriteRegister, address, mask, value }; + CallPrivilegedSecureMonitorFunction(args); + *out = args.x[1]; + return static_cast(args.x[0]) == SmcResult::Success; + } + +} \ No newline at end of file diff --git a/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp b/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp index 4c5e144f..8100a120 100644 --- a/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp +++ b/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp @@ -18,6 +18,54 @@ namespace ams::kern::smc { - /* TODO: Secure Monitor API. */ + /* Types. */ + enum MemoryMode { + MemoryMode_4GB = 0, + MemoryMode_6GB = 1, + MemoryMode_8GB = 2, + }; + + enum class ConfigItem : u32 { + /* Standard config items. */ + DisableProgramVerification = 1, + DramId = 2, + SecurityEngineIrqNumber = 3, + Version = 4, + HardwareType = 5, + IsRetail = 6, + IsRecoveryBoot = 7, + DeviceId = 8, + BootReason = 9, + MemoryArrange = 10, + IsDebugMode = 11, + KernelConfiguration = 12, + IsChargerHiZModeEnabled = 13, + IsKiosk = 14, + NewHardwareType = 15, + NewKeyGeneration = 16, + Package2Hash = 17, + + /* Extension config items for exosphere. */ + ExosphereApiVersion = 65000, + ExosphereNeedsReboot = 65001, + ExosphereNeedsShutdown = 65002, + ExosphereGitCommitHash = 65003, + ExosphereHasRcmBugPatch = 65004, + }; + + enum class SmcResult { + Success = 0, + NotImplemented = 1, + InvalidArgument = 2, + InProgress = 3, + NoAsyncOperation = 4, + InvalidAsyncOperation = 5, + NotPermitted = 6, + }; + + /* TODO: Rest of Secure Monitor API. */ + void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item); + void NORETURN Panic(u32 color); + bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value); } \ No newline at end of file diff --git a/libmesosphere/source/kern_k_scoped_interrupt.cpp b/libmesosphere/source/kern_k_scoped_interrupt.cpp new file mode 100644 index 00000000..1e7c0cce --- /dev/null +++ b/libmesosphere/source/kern_k_scoped_interrupt.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2019 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 { + + WEAK_SYMBOL KScopedInterruptDisable::KScopedInterruptDisable() { + /* TODO: Disable interrupts. */ + } + + WEAK_SYMBOL KScopedInterruptDisable::~KScopedInterruptDisable() { + /* TODO: un-disable interrupts. */ + } + + WEAK_SYMBOL KScopedInterruptEnable::KScopedInterruptEnable() { + /* TODO: Enable interrupts. */ + } + + WEAK_SYMBOL KScopedInterruptEnable::~KScopedInterruptEnable() { + /* TODO: un-enable interrupts. */ + } + +}