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