mesosphere: Implement kernelldr through first page table mapping

This commit is contained in:
Michael Scire 2019-12-13 01:21:43 -08:00 committed by SciresM
parent b5becba8ff
commit 2866cb5fe6
24 changed files with 1520 additions and 8 deletions

View file

@ -4,7 +4,7 @@
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
*/ */
/* /*
* Declare the exception vector table, enforcing it is aligned on a * Declare the exception vector table, enforcing it is aligned on a
@ -92,7 +92,7 @@ vector_entry fiq_spx
vector_entry serror_spx vector_entry serror_spx
b unknown_exception b unknown_exception
check_vector_size serror_spx check_vector_size serror_spx
/* Lower EL, A64 */ /* Lower EL, A64 */
vector_entry synch_a64 vector_entry synch_a64
stp x29, x30, [sp, #-0x10]! stp x29, x30, [sp, #-0x10]!

View file

@ -23,6 +23,13 @@
/* Primitive types. */ /* Primitive types. */
#include "mesosphere/kern_k_typed_address.hpp" #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. */ /* Core functionality. */
#include "mesosphere/kern_select_interrupts.hpp"
#include "mesosphere/kern_select_k_system_control.hpp" #include "mesosphere/kern_select_k_system_control.hpp"

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
/*
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 <vapours.hpp>
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);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
/*
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 <vapours.hpp>
#include <mesosphere/kern_panic.hpp>
#include <mesosphere/kern_k_typed_address.hpp>
#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<u64>(perm) | static_cast<u64>(AccessFlag_Accessed) | static_cast<u64>(p_a) | static_cast<u64>(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<AccessFlag>(this->GetBits(10, 1)); }
constexpr ALWAYS_INLINE Shareable GetShareable() const { return static_cast<Shareable>(this->GetBits(8, 2)); }
constexpr ALWAYS_INLINE PageAttribute GetPageAttribute() const { return static_cast<PageAttribute>(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<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L1PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(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<u64>(pxn) << 59) | GetInteger(phys_addr) | 0x3)
{
/* ... */
}
constexpr ALWAYS_INLINE L2PageTableEntry(KPhysicalAddress phys_addr, const PageTableEntry &attr, bool contig)
: PageTableEntry(attr, (static_cast<u64>(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<u64>(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<KPhysicalAddress>; }
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<L1PageTableEntry *>(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<L2PageTableEntry *>(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<L3PageTableEntry *>(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<void *>(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 */
}
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
/*
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 <vapours.hpp>
namespace ams::kern::hw {
}

View file

@ -21,6 +21,10 @@ namespace ams::kern {
class KSystemControl { class KSystemControl {
public: public:
/* Initialization. */
static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address);
static bool ShouldIncreaseResourceRegionSize();
/* Panic. */ /* Panic. */
static NORETURN void StopSystem(); static NORETURN void StopSystem();
}; };

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#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? */
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
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<KernelLayout>::value);
static_assert(sizeof(KernelLayout) == 0x30);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef ATMOSPHERE_ARCH_ARM64
#include "../arch/arm64/init/kern_k_init_page_table.hpp"
#else
#error "Unknown architecutre for KInitialPageTable"
#endif

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#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;
};
}

View file

@ -32,7 +32,7 @@ namespace ams::kern {
#ifdef MESOSPHERE_ENABLE_ASSERTIONS #ifdef MESOSPHERE_ENABLE_ASSERTIONS
#define MESOSPHERE_ASSERT_IMPL(expr, ...) \ #define MESOSPHERE_ASSERT_IMPL(expr, ...) \
({ \ ({ \
if (AMS_UNLIKELY(!expr)) { \ if (AMS_UNLIKELY(!(expr))) { \
MESOSPHERE_PANIC(__VA_ARGS__); \ MESOSPHERE_PANIC(__VA_ARGS__); \
} \ } \
}) })
@ -47,7 +47,7 @@ namespace ams::kern {
#define MESOSPHERE_ABORT_UNLESS(expr) \ #define MESOSPHERE_ABORT_UNLESS(expr) \
({ \ ({ \
if (AMS_UNLIKELY(!expr)) { \ if (AMS_UNLIKELY(!(expr))) { \
MESOSPHERE_PANIC("Abort(): %s", #expr); \ MESOSPHERE_PANIC("Abort(): %s", #expr); \
} \ } \
}) })

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#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();
};
}

View file

@ -19,4 +19,4 @@
#include "board/nintendo/switch/kern_k_system_control.hpp" #include "board/nintendo/switch/kern_k_system_control.hpp"
#else #else
#error "Unknown board for KSystemControl" #error "Unknown board for KSystemControl"
#endif #endif

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
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<const Elf64::Rel *>(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<Elf64::Addr *>(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<const Elf64::Rela *>(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<Elf64::Addr *>(base_address + rela.GetOffset());
*target_address = base_address + rela.GetAddend();
}
}
void CallInitArrayFuncs(uintptr_t init_array_start, uintptr_t init_array_end) {
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
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<const Elf64::Rel *>(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<Elf64::Addr *>(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<const Elf64::Rela *>(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<Elf64::Addr *>(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))();
}
}
}

View file

@ -18,8 +18,64 @@
namespace ams::kern { 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<size_t>(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<smc::MemoryMode>((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() { void KSystemControl::StopSystem() {
/* TODO: smc::Panic(0xF00); */ /* Display a panic screen via exosphere. */
smc::Panic(0xF00);
while (true) { /* ... */ } while (true) { /* ... */ }
} }

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
#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<u32>(config_item) };
CallPrivilegedSecureMonitorFunction(args);
MESOSPHERE_ABORT_UNLESS((static_cast<SmcResult>(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<SmcResult>(args.x[0]) == SmcResult::Success;
}
}

View file

@ -18,6 +18,54 @@
namespace ams::kern::smc { 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);
} }

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
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. */
}
}

View file

@ -49,6 +49,15 @@ SECTIONS
. = ALIGN(8); . = ALIGN(8);
} :krnlldr } :krnlldr
/* .vectors. */
. = ALIGN(2K);
__vectors_start__ = . ;
.vectors :
{
KEEP( *(.vectors) )
. = ALIGN(8);
} :krnlldr
/* =========== RODATA section =========== */ /* =========== RODATA section =========== */
. = ALIGN(8); . = ALIGN(8);
__rodata_start = . ; __rodata_start = . ;
@ -64,6 +73,7 @@ SECTIONS
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } :krnlldr .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } :krnlldr
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } : rodata .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } : rodata
__dynamic__start__ = . ;
.dynamic : { *(.dynamic) } :krnlldr :dyn .dynamic : { *(.dynamic) } :krnlldr :dyn
.dynsym : { *(.dynsym) } :krnlldr .dynsym : { *(.dynsym) } :krnlldr
.dynstr : { *(.dynstr) } :krnlldr .dynstr : { *(.dynstr) } :krnlldr

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
/* 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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
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<uintptr_t>) { /* ... */ }
ALWAYS_INLINE void Initialize(uintptr_t address) {
this->next_address = address;
}
ALWAYS_INLINE void Finalize() {
this->next_address = Null<uintptr_t>;
}
public:
virtual KPhysicalAddress Allocate() override {
MESOSPHERE_ABORT_UNLESS(this->next_address != Null<uintptr_t>);
const uintptr_t allocated = this->next_address;
this->next_address += PageSize;
std::memset(reinterpret_cast<void *>(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<void *>(GetInteger(correct_base)), reinterpret_cast<void *>(base_address), size);
base_address += diff;
layout = reinterpret_cast<KernelLayout *>(reinterpret_cast<uintptr_t>(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<const InitialProcessBinaryHeader *>(ini_base_address);
if (ini_header->magic == InitialProcessBinaryMagic && ini_header->size <= InitialProcessBinarySizeMax) {
/* INI is valid, relocate it. */
std::memmove(reinterpret_cast<void *>(ini_load_address), ini_header, ini_header->size);
} else {
/* INI is invalid. Make the destination header invalid. */
std::memset(reinterpret_cast<void *>(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();
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
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. */
}
}

View file

@ -21,4 +21,75 @@
.section .crt0.text.start, "ax", %progbits .section .crt0.text.start, "ax", %progbits
.global _start .global _start
_start: _start:
b _start /* 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