From 2866cb5fe60fac395dbe0739b4bdea6621b9c742 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 13 Dec 2019 01:21:43 -0800 Subject: [PATCH] mesosphere: Implement kernelldr through first page table mapping --- exosphere/src/exceptions.s | 4 +- .../libmesosphere/include/mesosphere.hpp | 7 + .../arch/arm64/init/kern_init_elf64.hpp | 136 ++++++++ .../arm64/init/kern_k_init_page_table.hpp | 312 ++++++++++++++++++ .../arch/arm64/kern_hardware_registers.hpp | 47 +++ .../nintendo/switch/kern_k_system_control.hpp | 4 + .../include/mesosphere/init/kern_init_elf.hpp | 33 ++ .../mesosphere/init/kern_init_layout.hpp | 38 +++ .../init/kern_init_page_table_select.hpp | 22 ++ .../mesosphere/kern_initial_process.hpp | 32 ++ .../include/mesosphere/kern_panic.hpp | 4 +- .../mesosphere/kern_select_interrupts.hpp | 42 +++ .../kern_select_k_system_control.hpp | 2 +- .../arch/arm64/init/kern_init_elf64.cpp | 82 +++++ .../source/arch/arm64/kern_init_elf64.cpp | 84 +++++ .../nintendo/switch/kern_k_system_control.cpp | 58 +++- .../nintendo/switch/kern_secure_monitor.cpp | 96 ++++++ .../nintendo/switch/kern_secure_monitor.hpp | 50 ++- .../source/kern_k_scoped_interrupt.cpp | 36 ++ mesosphere/kernel_ldr/kernel_ldr.ld | 10 + mesosphere/kernel_ldr/source/exceptions.s | 161 +++++++++ .../kernel_ldr/source/kern_init_loader.cpp | 159 +++++++++ .../source/kern_k_scoped_interrupt.cpp | 36 ++ mesosphere/kernel_ldr/source/start.s | 73 +++- 24 files changed, 1520 insertions(+), 8 deletions(-) create mode 100644 libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_init_elf64.hpp create mode 100644 libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp create mode 100644 libraries/libmesosphere/include/mesosphere/arch/arm64/kern_hardware_registers.hpp create mode 100644 libraries/libmesosphere/include/mesosphere/init/kern_init_elf.hpp create mode 100644 libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp create mode 100644 libraries/libmesosphere/include/mesosphere/init/kern_init_page_table_select.hpp create mode 100644 libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp create mode 100644 libraries/libmesosphere/include/mesosphere/kern_select_interrupts.hpp create mode 100644 libraries/libmesosphere/source/arch/arm64/init/kern_init_elf64.cpp create mode 100644 libraries/libmesosphere/source/arch/arm64/kern_init_elf64.cpp create mode 100644 libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp create mode 100644 libraries/libmesosphere/source/kern_k_scoped_interrupt.cpp create mode 100644 mesosphere/kernel_ldr/source/exceptions.s create mode 100644 mesosphere/kernel_ldr/source/kern_init_loader.cpp create mode 100644 mesosphere/kernel_ldr/source/kern_k_scoped_interrupt.cpp diff --git a/exosphere/src/exceptions.s b/exosphere/src/exceptions.s index a85fd0ed2..501efe0ae 100644 --- a/exosphere/src/exceptions.s +++ b/exosphere/src/exceptions.s @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: BSD-3-Clause */ - + /* * Declare the exception vector table, enforcing it is aligned on a @@ -92,7 +92,7 @@ vector_entry fiq_spx vector_entry serror_spx b unknown_exception check_vector_size serror_spx - + /* Lower EL, A64 */ vector_entry synch_a64 stp x29, x30, [sp, #-0x10]! diff --git a/libraries/libmesosphere/include/mesosphere.hpp b/libraries/libmesosphere/include/mesosphere.hpp index 77e798d98..0da2f63ee 100644 --- a/libraries/libmesosphere/include/mesosphere.hpp +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_init_elf64.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_init_elf64.hpp new file mode 100644 index 000000000..33ef192fe --- /dev/null +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp new file mode 100644 index 000000000..2d2c6fc38 --- /dev/null +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_hardware_registers.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_hardware_registers.hpp new file mode 100644 index 000000000..74da7387b --- /dev/null +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp index d28a1230b..3426f2746 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/switch/kern_k_system_control.hpp +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/init/kern_init_elf.hpp b/libraries/libmesosphere/include/mesosphere/init/kern_init_elf.hpp new file mode 100644 index 000000000..2ee1e001a --- /dev/null +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp b/libraries/libmesosphere/include/mesosphere/init/kern_init_layout.hpp new file mode 100644 index 000000000..1e3bfce84 --- /dev/null +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/init/kern_init_page_table_select.hpp b/libraries/libmesosphere/include/mesosphere/init/kern_init_page_table_select.hpp new file mode 100644 index 000000000..e0e6b423e --- /dev/null +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp new file mode 100644 index 000000000..e901dd4c5 --- /dev/null +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/kern_panic.hpp b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp index 49a6e7b34..27c54cf46 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/kern_select_interrupts.hpp b/libraries/libmesosphere/include/mesosphere/kern_select_interrupts.hpp new file mode 100644 index 000000000..63a5c7395 --- /dev/null +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/kern_select_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/kern_select_k_system_control.hpp index 56ddc9f6c..3c6b5b6de 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_select_k_system_control.hpp +++ b/libraries/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/libraries/libmesosphere/source/arch/arm64/init/kern_init_elf64.cpp b/libraries/libmesosphere/source/arch/arm64/init/kern_init_elf64.cpp new file mode 100644 index 000000000..8e60b51eb --- /dev/null +++ b/libraries/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/libraries/libmesosphere/source/arch/arm64/kern_init_elf64.cpp b/libraries/libmesosphere/source/arch/arm64/kern_init_elf64.cpp new file mode 100644 index 000000000..6f846763b --- /dev/null +++ b/libraries/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/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp index 0bc829002..f5cd9fc74 100644 --- a/libraries/libmesosphere/source/board/nintendo/switch/kern_k_system_control.cpp +++ b/libraries/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/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.cpp new file mode 100644 index 000000000..b56b8371b --- /dev/null +++ b/libraries/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/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp b/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp index 4c5e144f3..8100a1200 100644 --- a/libraries/libmesosphere/source/board/nintendo/switch/kern_secure_monitor.hpp +++ b/libraries/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/libraries/libmesosphere/source/kern_k_scoped_interrupt.cpp b/libraries/libmesosphere/source/kern_k_scoped_interrupt.cpp new file mode 100644 index 000000000..1e7c0cce1 --- /dev/null +++ b/libraries/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. */ + } + +} diff --git a/mesosphere/kernel_ldr/kernel_ldr.ld b/mesosphere/kernel_ldr/kernel_ldr.ld index 66b277e7e..64ab87fc0 100644 --- a/mesosphere/kernel_ldr/kernel_ldr.ld +++ b/mesosphere/kernel_ldr/kernel_ldr.ld @@ -49,6 +49,15 @@ SECTIONS . = ALIGN(8); } :krnlldr + /* .vectors. */ + . = ALIGN(2K); + __vectors_start__ = . ; + .vectors : + { + KEEP( *(.vectors) ) + . = ALIGN(8); + } :krnlldr + /* =========== RODATA section =========== */ . = ALIGN(8); __rodata_start = . ; @@ -64,6 +73,7 @@ SECTIONS .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } :krnlldr .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } : rodata + __dynamic__start__ = . ; .dynamic : { *(.dynamic) } :krnlldr :dyn .dynsym : { *(.dynsym) } :krnlldr .dynstr : { *(.dynstr) } :krnlldr diff --git a/mesosphere/kernel_ldr/source/exceptions.s b/mesosphere/kernel_ldr/source/exceptions.s new file mode 100644 index 000000000..55c843129 --- /dev/null +++ b/mesosphere/kernel_ldr/source/exceptions.s @@ -0,0 +1,161 @@ +/* + * 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 . + */ + +/* Some macros taken from https://github.com/ARM-software/arm-trusted-firmware/blob/master/include/common/aarch64/asm_macros.S */ +/* + * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Declare the exception vector table, enforcing it is aligned on a + * 2KB boundary, as required by the ARMv8 architecture. + * Use zero bytes as the fill value to be stored in the padding bytes + * so that it inserts illegal AArch64 instructions. This increases + * security, robustness and potentially facilitates debugging. + */ +.macro vector_base label, section_name=.vectors +.section \section_name, "ax" +.align 11, 0 +\label: +.endm + +/* + * Create an entry in the exception vector table, enforcing it is + * aligned on a 128-byte boundary, as required by the ARMv8 architecture. + * Use zero bytes as the fill value to be stored in the padding bytes + * so that it inserts illegal AArch64 instructions. This increases + * security, robustness and potentially facilitates debugging. + */ +.macro vector_entry label, section_name=.vectors +.cfi_sections .debug_frame +.section \section_name, "ax" +.align 7, 0 +.type \label, %function +.func \label +.cfi_startproc +\label: +.endm + +/* + * This macro verifies that the given vector doesnt exceed the + * architectural limit of 32 instructions. This is meant to be placed + * immediately after the last instruction in the vector. It takes the + * vector entry as the parameter + */ +.macro check_vector_size since + .endfunc + .cfi_endproc + .if (. - \since) > (32 * 4) + .error "Vector exceeds 32 instructions" + .endif +.endm + +/* Actual Vectors for KernelLdr. */ +.global kernelldr_vectors +vector_base kernelldr_vectors + +/* Current EL, SP0 */ +.global unknown_exception +unknown_exception: +vector_entry synch_sp0 + /* Just infinite loop. */ + b unknown_exception + check_vector_size synch_sp0 + +vector_entry irq_sp0 + b unknown_exception + check_vector_size irq_sp0 + +vector_entry fiq_sp0 + b unknown_exception + check_vector_size fiq_sp0 + +vector_entry serror_sp0 + b unknown_exception + check_vector_size serror_sp0 + +/* Current EL, SPx */ +vector_entry synch_spx + b restore_tpidr_el1 + check_vector_size synch_spx + +vector_entry irq_spx + b unknown_exception + check_vector_size irq_spx + +vector_entry fiq_spx + b unknown_exception + check_vector_size fiq_spx + +vector_entry serror_spx + b unknown_exception + check_vector_size serror_spx + +/* Lower EL, A64 */ +vector_entry synch_a64 + b unknown_exception + check_vector_size synch_a64 + +vector_entry irq_a64 + b unknown_exception + check_vector_size irq_a64 + +vector_entry fiq_a64 + b unknown_exception + check_vector_size fiq_a64 + +vector_entry serror_a64 + b unknown_exception + check_vector_size serror_a64 + +/* Lower EL, A32 */ +vector_entry synch_a32 + b unknown_exception + check_vector_size synch_a32 + +vector_entry irq_a32 + b unknown_exception + check_vector_size irq_a32 + +vector_entry fiq_a32 + b unknown_exception + check_vector_size fiq_a32 + +vector_entry serror_a32 + b unknown_exception + .endfunc + .cfi_endproc +/* To save space, insert in an unused vector segment. */ +.global restore_tpidr_el1 +.type restore_tpidr_el1, %function +restore_tpidr_el1: + mrs x0, tpidr_el1 + /* Make sure that TPIDR_EL1 can be dereferenced. */ + invalid_tpidr: + cbz x0, invalid_tpidr + /* Restore saved registers. */ + ldp x19, x20, [x0], #0x10 + ldp x21, x22, [x0], #0x10 + ldp x23, x24, [x0], #0x10 + ldp x25, x26, [x0], #0x10 + ldp x27, x28, [x0], #0x10 + ldp x29, x30, [x0], #0x10 + ldp x1, xzr, [x0], #0x10 + mov sp, x1 + mov x0, #0x1 + ret \ No newline at end of file diff --git a/mesosphere/kernel_ldr/source/kern_init_loader.cpp b/mesosphere/kernel_ldr/source/kern_init_loader.cpp new file mode 100644 index 000000000..681e5faa5 --- /dev/null +++ b/mesosphere/kernel_ldr/source/kern_init_loader.cpp @@ -0,0 +1,159 @@ +/* + * 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::loader { + + namespace { + + constexpr size_t KernelResourceRegionSize = 0x1728000; + constexpr size_t ExtraKernelResourceSize = 0x68000; + static_assert(ExtraKernelResourceSize + KernelResourceRegionSize == 0x1790000); + + constexpr size_t InitialPageTableRegionSize = 0x200000; + + class KInitialPageAllocator : public KInitialPageTable::IPageAllocator { + private: + uintptr_t next_address; + public: + constexpr ALWAYS_INLINE KInitialPageAllocator() : next_address(Null) { /* ... */ } + + ALWAYS_INLINE void Initialize(uintptr_t address) { + this->next_address = address; + } + + ALWAYS_INLINE void Finalize() { + this->next_address = Null; + } + public: + virtual KPhysicalAddress Allocate() override { + MESOSPHERE_ABORT_UNLESS(this->next_address != Null); + const uintptr_t allocated = this->next_address; + this->next_address += PageSize; + std::memset(reinterpret_cast(allocated), 0, PageSize); + return allocated; + } + + /* No need to override free. The default does nothing, and so would we. */ + }; + + /* Global Allocator. */ + KInitialPageAllocator g_initial_page_allocator; + + void RelocateKernelPhysically(uintptr_t &base_address, KernelLayout *&layout) { + /* TODO: Proper secure monitor call. */ + KPhysicalAddress correct_base = KSystemControl::GetKernelPhysicalBaseAddress(base_address); + if (correct_base != base_address) { + const uintptr_t diff = GetInteger(correct_base) - base_address; + const size_t size = layout->rw_end_offset; + + /* Conversion from KPhysicalAddress to void * is safe here, because MMU is not set up yet. */ + std::memmove(reinterpret_cast(GetInteger(correct_base)), reinterpret_cast(base_address), size); + base_address += diff; + layout = reinterpret_cast(reinterpret_cast(layout) + diff); + } + } + + void SetupInitialIdentityMapping(KInitialPageTable &ttbr1_table, uintptr_t base_address, uintptr_t kernel_size, uintptr_t page_table_region, size_t page_table_region_size, KInitialPageTable::IPageAllocator &allocator) { + /* Make a new page table for TTBR0_EL1. */ + KInitialPageTable ttbr0_table(allocator.Allocate()); + + /* Map in an RWX identity mapping for the kernel. */ + constexpr PageTableEntry KernelRWXIdentityAttribute(PageTableEntry::Permission_KernelRWX, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable); + ttbr0_table.Map(base_address, kernel_size, base_address, KernelRWXIdentityAttribute, allocator); + + /* Map in an RWX identity mapping for ourselves. */ + constexpr PageTableEntry KernelLdrRWXIdentityAttribute(PageTableEntry::Permission_KernelRWX, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable); + //ttbr0_table.Map(base_address, kernel_size, base_address, KernelRWXIdentityAttribute, allocator); + } + + } + + + uintptr_t Main(uintptr_t base_address, KernelLayout *layout, uintptr_t ini_base_address) { + /* Relocate the kernel to the correct physical base address. */ + /* Base address and layout are passed by reference and modified. */ + RelocateKernelPhysically(base_address, layout); + + /* Validate kernel layout. */ + /* TODO: constexpr 0x1000 definition somewhere. */ + /* In stratosphere, this is os::MemoryPageSize. */ + /* We don't have ams::os, this may go in hw:: or something. */ + const uintptr_t rx_offset = layout->rx_offset; + const uintptr_t rx_end_offset = layout->rx_end_offset; + const uintptr_t ro_offset = layout->rx_offset; + const uintptr_t ro_end_offset = layout->ro_end_offset; + const uintptr_t rw_offset = layout->rx_offset; + const uintptr_t rw_end_offset = layout->rw_end_offset; + const uintptr_t bss_end_offset = layout->bss_end_offset; + MESOSPHERE_ABORT_UNLESS(util::IsAligned(rx_offset, 0x1000)); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(rx_end_offset, 0x1000)); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(ro_offset, 0x1000)); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(ro_end_offset, 0x1000)); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(rw_offset, 0x1000)); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(rw_end_offset, 0x1000)); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(bss_end_offset, 0x1000)); + const uintptr_t bss_offset = layout->bss_offset; + const uintptr_t ini_end_offset = layout->ini_end_offset; + const uintptr_t dynamic_end_offset = layout->dynamic_end_offset; + const uintptr_t init_array_offset = layout->init_array_offset; + const uintptr_t init_array_end_offset = layout->init_array_end_offset; + + /* Decide if Kernel should have enlarged resource region. */ + const bool use_extra_resources = KSystemControl::ShouldIncreaseResourceRegionSize(); + const size_t resource_region_size = KernelResourceRegionSize + (use_extra_resources ? ExtraKernelResourceSize : 0); + + /* Setup the INI1 header in memory for the kernel. */ + const uintptr_t ini_end_address = base_address + ini_end_offset + resource_region_size; + const uintptr_t ini_load_address = ini_end_address - InitialProcessBinarySizeMax; + if (ini_base_address != ini_load_address) { + /* The INI is not at the correct address, so we need to relocate it. */ + const InitialProcessBinaryHeader *ini_header = reinterpret_cast(ini_base_address); + if (ini_header->magic == InitialProcessBinaryMagic && ini_header->size <= InitialProcessBinarySizeMax) { + /* INI is valid, relocate it. */ + std::memmove(reinterpret_cast(ini_load_address), ini_header, ini_header->size); + } else { + /* INI is invalid. Make the destination header invalid. */ + std::memset(reinterpret_cast(ini_load_address), 0, sizeof(InitialProcessBinaryHeader)); + } + } + + /* We want to start allocating page tables at ini_end_address. */ + g_initial_page_allocator.Initialize(ini_end_address); + + /* Make a new page table for TTBR1_EL1. */ + KInitialPageTable ttbr1_table(g_initial_page_allocator.Allocate()); + + /* Setup initial identity mapping. TTBR1 table passed by reference. */ + SetupInitialIdentityMapping(ttbr1_table, base_address, bss_end_offset, ini_end_address, InitialPageTableRegionSize, g_initial_page_allocator); + + /* TODO: Use these. */ + (void)(bss_offset); + (void)(ini_end_offset); + (void)(dynamic_end_offset); + (void)(init_array_offset); + (void)(init_array_end_offset); + + + /* TODO */ + return 0; + } + + void Finalize() { + g_initial_page_allocator.Finalize(); + } + +} \ No newline at end of file diff --git a/mesosphere/kernel_ldr/source/kern_k_scoped_interrupt.cpp b/mesosphere/kernel_ldr/source/kern_k_scoped_interrupt.cpp new file mode 100644 index 000000000..68b8a5d03 --- /dev/null +++ b/mesosphere/kernel_ldr/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 { + + inline KScopedInterruptDisable::KScopedInterruptDisable() { + /* Intentionally do nothing, KernelLdr doesn't have interrupts set up. */ + } + + inline KScopedInterruptDisable::~KScopedInterruptDisable() { + /* Intentionally do nothing, KernelLdr doesn't have interrupts set up. */ + } + + inline KScopedInterruptEnable::KScopedInterruptEnable() { + /* Intentionally do nothing, KernelLdr doesn't have interrupts set up. */ + } + + inline KScopedInterruptEnable::~KScopedInterruptEnable() { + /* Intentionally do nothing, KernelLdr doesn't have interrupts set up. */ + } + +} diff --git a/mesosphere/kernel_ldr/source/start.s b/mesosphere/kernel_ldr/source/start.s index 981504cba..4ab36aa58 100644 --- a/mesosphere/kernel_ldr/source/start.s +++ b/mesosphere/kernel_ldr/source/start.s @@ -21,4 +21,75 @@ .section .crt0.text.start, "ax", %progbits .global _start _start: - b _start \ No newline at end of file + /* KernelLdr_Main(uintptr_t kernel_base_address, KernelMap *kernel_map, uintptr_t ini1_base_address); */ + adr x18, _start + adr x16, __external_references + ldr x17, [x16, #0x8] /* bss end */ + ldr x16, [x16, #0x0] /* bss start */ + add x16, x16, x18 + add x17, x17, x18 + clear_bss: + cmp x16, x17 + b.cs clear_bss_done + str xzr, [x16],#0x8 + b clear_bss + clear_bss_done: + adr x17, __external_references + ldr x17, [x17, #0x10] /* stack top */ + add sp, x17, x18 + + /* Stack is now set up. */ + /* Apply relocations and call init array for KernelLdr. */ + sub sp, sp, #0x20 + stp x0, x1, [sp, #0x00] + stp x2, x30, [sp, #0x10] + adr x0, _start + adr x1, __external_references + ldr x1, [x1, #0x18] /* .dynamic. */ + add x1, x0, x1 + + /* branch to ams::kern::init::Elf::Elf64::ApplyRelocations(uintptr_t, const ams::kern::init::Elf::Elf64::Dyn *); */ + bl _ZN3ams4kern4init3Elf5Elf6416ApplyRelocationsEmPKNS3_3DynE + + /* branch to ams::kern::init::Elf::Elf64::CallInitArrayFuncs(uintptr_t, uintptr_t) */ + adr x2, _start + adr x1, __external_references + ldr x0, [x1, #0x20] /* init_array_start */ + ldr x1, [x1, #0x28] /* init_array_end */ + add x0, x0, x2 + add x1, x1, x2 + bl _ZN3ams4kern4init3Elf5Elf6418CallInitArrayFuncsEmm + + /* Setup system registers, for detection of errors during init later. */ + msr tpidr_el1, xzr /* Clear TPIDR_EL1 */ + adr x0, __external_references + adr x1, _start + ldr x0, [x0, #0x30] + add x0, x1, x0 + msr vbar_el1, x0 + isb + + /* Call ams::kern::init::loader::Main(uintptr_t, ams::kern::init::KernelLayout *, uintptr_t) */ + ldp x0, x1, [sp, #0x00] + ldr x2, [sp, #0x10] + bl _ZN3ams4kern4init6loader4MainEmPNS1_12KernelLayoutEm + str x0, [sp, #0x00] + + /* Call ams::kern::init::loader::Finalize() */ + bl _ZN3ams4kern4init6loader8FinalizeEv + + /* Return to the newly-relocated kernel. */ + ldr x1, [sp, #0x18] /* Return address to Kernel */ + ldr x2, [sp, #0x00] /* Relocated kernel base address. */ + add x1, x2, x1 + br x1 + + +__external_references: + .quad __bss_start__ - _start + .quad __bss_end__ - _start + .quad __stack_end - _start + .quad __dynamic__start__ - _start + .quad __init_array_start - _start + .quad __init_array_end - _start + .quad __vectors_start__ - _start \ No newline at end of file