diff --git a/.gitignore b/.gitignore
index 6b4b5309f..54b77b4ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@
# Executables
*.exe
+*.lz4
*.out
*.app
*.i*86
diff --git a/exosphere/src/start.s b/exosphere/src/start.s
index 7ac4fe26d..84eb1c2cd 100644
--- a/exosphere/src/start.s
+++ b/exosphere/src/start.s
@@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-
+
/* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */
#define cpuactlr_el1 s3_1_c15_c2_0
#define cpuectlr_el1 s3_1_c15_c2_1
@@ -109,7 +109,7 @@ __start_cold:
stp x3, x4, [x0], #0x10
cmp x0, x2
blo 1b
-
+
adr x19, __start_cold
adr x20, g_coldboot_crt0_relocation_list
sub x20, x20, x19
@@ -125,7 +125,7 @@ _post_cold_crt0_reloc:
bl get_coldboot_crt0_stack_address
mov sp, x0
mov fp, #0
-
+
/* Relocate Exosphere image to free DRAM, clearing the image in IRAM. */
ldr x0, =0x80010000
add x20, x20, x0
@@ -147,7 +147,7 @@ _post_cold_crt0_reloc:
ldr x1, =0x80010000
/* Set size in coldboot relocation list. */
str x21, [x0, #0x8]
-
+
bl coldboot_init
ldr x16, =__jump_to_main_cold
diff --git a/exosphere2/Makefile b/exosphere2/Makefile
new file mode 100644
index 000000000..9b72ff1ec
--- /dev/null
+++ b/exosphere2/Makefile
@@ -0,0 +1,38 @@
+TARGETS := exosphere.bin program.lz4
+CLEAN_TARGETS := exosphere-clean program-clean boot_code-clean
+
+SUBFOLDERS := $(MODULES)
+
+all: exosphere.bin
+
+clean: $(CLEAN_TARGETS)
+ @rm -f exosphere.bin
+
+exosphere.bin: program.lz4 boot_code.lz4
+ $(MAKE) -C loader_stub
+ @cp loader_stub/loader_stub.bin exosphere.bin
+ @echo "Built exosphere.bin..."
+
+program.lz4: check_libexo
+ $(MAKE) -C program
+ @cp program/program.lz4 program.lz4
+ @cp program/boot_code.lz4 boot_code.lz4
+
+boot_code.lz4: program.lz4
+
+check_libexo:
+ @$(MAKE) --no-print-directory -C ../libraries/libexosphere
+
+exosphere-clean:
+ $(MAKE) -C loader_stub clean
+ @rm -f exosphere.bin
+
+program-clean:
+ $(MAKE) -C program clean
+ @rm -f program.lz4
+
+boot_code-clean:
+ $(MAKE) -C boot_code clean
+ @rm -f boot_code.lz4
+
+.PHONY: all clean $(CLEAN_TARGETS)
diff --git a/exosphere2/loader_stub/Makefile b/exosphere2/loader_stub/Makefile
new file mode 100644
index 000000000..a5725e57e
--- /dev/null
+++ b/exosphere2/loader_stub/Makefile
@@ -0,0 +1,130 @@
+#---------------------------------------------------------------------------------
+# Define the atmosphere board and cpu
+#---------------------------------------------------------------------------------
+export ATMOSPHERE_BOARD := nx-hac-001
+export ATMOSPHERE_CPU := arm-cortex-a57
+
+#---------------------------------------------------------------------------------
+# pull in common atmosphere configuration
+#---------------------------------------------------------------------------------
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/exosphere.mk
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(BUILD),$(notdir $(CURDIR)))
+#---------------------------------------------------------------------------------
+
+export OUTPUT := $(CURDIR)/$(TARGET)
+export TOPDIR := $(CURDIR)
+export DEPSDIR := $(CURDIR)/$(BUILD)
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
+ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
+ $(TOPDIR)/../program
+
+CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \
+ $(notdir $(wildcard $(dir)/*.c))))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c)))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c)))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c)))
+
+CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \
+ $(notdir $(wildcard $(dir)/*.cpp))))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp)))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp)))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp)))
+
+SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \
+ $(notdir $(wildcard $(dir)/*.s))))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s)))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s)))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s)))
+
+BINFILES := program.lz4 boot_code.lz4
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES_BIN := $(addsuffix .o,$(BINFILES))
+export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+export OFILES := $(OFILES_BIN) $(OFILES_SRC)
+export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES))))
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ -I.
+
+export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
+
+.PHONY: $(BUILD) clean all check_program
+
+#---------------------------------------------------------------------------------
+all: $(BUILD) check_program
+
+$(BUILD): check_program
+ @[ -d $@ ] || mkdir -p $@
+ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+
+check_program:
+ @$(MAKE) -C ../program all
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf
+
+#---------------------------------------------------------------------------------
+else
+.PHONY: all
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+all : $(OUTPUT).bin
+
+$(OUTPUT).bin : $(OUTPUT).elf
+ $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@
+ @echo built ... $(notdir $@)
+
+$(OUTPUT).elf : $(OFILES) ../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a
+
+%.elf:
+ @echo linking $(notdir $@)
+ $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
+ @$(NM) -CSn $@ > $(notdir $*.lst)
+
+$(OFILES_SRC) : $(HFILES_BIN)
+
+#---------------------------------------------------------------------------------
+# you need a rule like this for each extension you use as binary data
+#---------------------------------------------------------------------------------
+%.bin.o %_bin.h: %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+%.lz4.o %_lz4.h: %.lz4
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
diff --git a/exosphere2/loader_stub/loader_stub.ld b/exosphere2/loader_stub/loader_stub.ld
new file mode 100644
index 000000000..bb8239d81
--- /dev/null
+++ b/exosphere2/loader_stub/loader_stub.ld
@@ -0,0 +1,182 @@
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+
+MEMORY
+{
+ NULL : ORIGIN = 0, LENGTH = 4K
+ ldr_stub : ORIGIN = 0x040030000, LENGTH = 128K
+}
+
+SECTIONS
+{
+ /* =========== CODE section =========== */
+ PROVIDE(__start__ = 0x040030000);
+ . = __start__;
+ __code_start = . ;
+
+ .crt0 :
+ {
+ KEEP (*(.crt0 .crt0.*))
+ . = ALIGN(8);
+ } >ldr_stub
+
+ .text :
+ {
+ *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+ *(.text.exit .text.exit.*)
+ *(.text.startup .text.startup.*)
+ *(.text.hot .text.hot.*)
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ . = ALIGN(8);
+ } >ldr_stub
+
+ .init :
+ {
+ KEEP( *(.init) )
+ . = ALIGN(8);
+ } >ldr_stub
+
+ .plt :
+ {
+ *(.plt)
+ *(.iplt)
+ . = ALIGN(8);
+ } >ldr_stub
+
+ .fini :
+ {
+ KEEP( *(.fini) )
+ . = ALIGN(8);
+ } >ldr_stub
+
+
+ /* =========== RODATA section =========== */
+ . = ALIGN(8);
+ __rodata_start = . ;
+
+ .rodata :
+ {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ . = ALIGN(8);
+ } >ldr_stub
+
+ .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >ldr_stub
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ldr_stub
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >ldr_stub
+ .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >ldr_stub
+
+ .hash : { *(.hash) } >ldr_stub
+
+ /* =========== DATA section =========== */
+ . = ALIGN(8);
+ __data_start = . ;
+
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ldr_stub
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >ldr_stub
+ .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >ldr_stub
+ .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >ldr_stub
+
+ .preinit_array ALIGN(8) :
+ {
+ PROVIDE (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE (__preinit_array_end = .);
+ } >ldr_stub
+
+ .init_array ALIGN(8) :
+ {
+ PROVIDE (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE (__init_array_end = .);
+ } >ldr_stub
+
+ .fini_array ALIGN(8) :
+ {
+ PROVIDE (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ PROVIDE (__fini_array_end = .);
+ } >ldr_stub
+
+ .ctors ALIGN(8) :
+ {
+ KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ } >ldr_stub
+
+ .dtors ALIGN(8) :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ } >ldr_stub
+
+ __got_start__ = .;
+
+ .got : { *(.got) *(.igot) } >ldr_stub
+ .got.plt : { *(.got.plt) *(.igot.plt) } >ldr_stub
+
+ __got_end__ = .;
+
+ .data ALIGN(8) :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ } >ldr_stub
+
+ __bss_start__ = .;
+ .bss ALIGN(8) :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ . = ALIGN(16);
+ } >ldr_stub
+ __bss_end__ = .;
+
+ __end__ = ABSOLUTE(.) ;
+
+ /* ==================
+ ==== Metadata ====
+ ================== */
+
+ /* Discard sections that difficult post-processing */
+ /DISCARD/ : { *(.group .comment .note .interp) }
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+}
\ No newline at end of file
diff --git a/exosphere2/loader_stub/loader_stub.specs b/exosphere2/loader_stub/loader_stub.specs
new file mode 100644
index 000000000..03984762f
--- /dev/null
+++ b/exosphere2/loader_stub/loader_stub.specs
@@ -0,0 +1,7 @@
+%rename link old_link
+
+*link:
+%(old_link) -T %:getenv(TOPDIR /loader_stub.ld) --gc-sections --nmagic -nostdlib -nostartfiles
+
+*startfile:
+crti%O%s crtbegin%O%s
diff --git a/exosphere2/loader_stub/source/secmon_loader_error.cpp b/exosphere2/loader_stub/source/secmon_loader_error.cpp
new file mode 100644
index 000000000..2f67b507f
--- /dev/null
+++ b/exosphere2/loader_stub/source/secmon_loader_error.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_loader_error.hpp"
+
+namespace ams::diag {
+
+ NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
+ ams::secmon::loader::ErrorReboot();
+ }
+
+ NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
+ ams::secmon::loader::ErrorReboot();
+ }
+
+ NORETURN void AbortImpl() {
+ ams::secmon::loader::ErrorReboot();
+ }
+
+}
+
+namespace ams::secmon::loader {
+
+ NORETURN void ErrorReboot() {
+ /* Invalidate the security engine. */
+ /* TODO */
+
+ /* Reboot. */
+ while (true) {
+ wdt::Reboot();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/loader_stub/source/secmon_loader_error.hpp b/exosphere2/loader_stub/source/secmon_loader_error.hpp
new file mode 100644
index 000000000..611990740
--- /dev/null
+++ b/exosphere2/loader_stub/source/secmon_loader_error.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#pragma once
+
+namespace ams::secmon::loader {
+
+ NORETURN void ErrorReboot();
+
+}
\ No newline at end of file
diff --git a/exosphere2/loader_stub/source/secmon_loader_main.cpp b/exosphere2/loader_stub/source/secmon_loader_main.cpp
new file mode 100644
index 000000000..70dc8555a
--- /dev/null
+++ b/exosphere2/loader_stub/source/secmon_loader_main.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_loader_uncompress.hpp"
+#include "program_lz4.h"
+#include "boot_code_lz4.h"
+
+namespace ams::secmon::loader {
+
+ NORETURN void UncompressAndExecute() {
+ /* Uncompress the program image. */
+ Uncompress(secmon::MemoryRegionPhysicalTzramFullProgramImage.GetPointer(), secmon::MemoryRegionPhysicalTzramFullProgramImage.GetSize(), program_lz4, program_lz4_size);
+
+ /* Copy the boot image to the end of IRAM */
+ u8 *relocated_boot_code = secmon::MemoryRegionPhysicalIramBootCodeImage.GetEndPointer() - boot_code_lz4_size;
+ std::memcpy(relocated_boot_code, boot_code_lz4, boot_code_lz4_size);
+
+ /* Uncompress the boot image. */
+ Uncompress(secmon::MemoryRegionPhysicalIramBootCodeImage.GetPointer(), secmon::MemoryRegionPhysicalIramBootCodeImage.GetSize(), relocated_boot_code, boot_code_lz4_size);
+
+ /* Jump to the boot image. */
+ reinterpret_cast(secmon::MemoryRegionPhysicalIramBootCodeImage.GetAddress())();
+
+ /* We will never reach this point. */
+ __builtin_unreachable();
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp b/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp
new file mode 100644
index 000000000..a58e3c5a1
--- /dev/null
+++ b/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_loader_uncompress.hpp"
+
+namespace ams::secmon::loader {
+
+ namespace {
+
+ class Lz4Uncompressor {
+ private:
+ const u8 *src;
+ size_t src_size;
+ size_t src_offset;
+ u8 *dst;
+ size_t dst_size;
+ size_t dst_offset;
+ public:
+ Lz4Uncompressor(void *dst, size_t dst_size, const void *src, size_t src_size) : src(static_cast(src)), src_size(src_size), src_offset(0), dst(static_cast(dst)), dst_size(dst_size), dst_offset(0) {
+ /* ... */
+ }
+
+ void Uncompress() {
+ while (true) {
+ /* Read a control byte. */
+ u8 control = this->ReadByte();
+
+ /* Copy what it specifies we should copy. */
+ this->Copy(this->GetCopySize(control >> 4));
+
+ /* If we've exceeded size, we're done. */
+ if (this->src_offset >= this->src_size) {
+ break;
+ }
+
+ /* Read the wide copy offset. */
+ u16 wide_offset = this->ReadByte();
+ AMS_ABORT_UNLESS(this->CanRead());
+ wide_offset |= (this->ReadByte() << 8);
+
+ /* Determine the copy size. */
+ const size_t wide_copy_size = this->GetCopySize(control & 0xF);
+
+ /* Copy bytes. */
+ const size_t end_offset = this->dst_offset + wide_copy_size + 4;
+ for (size_t cur_offset = this->dst_offset; cur_offset < end_offset; this->dst_offset = (++cur_offset)) {
+ AMS_ABORT_UNLESS(wide_offset <= cur_offset);
+
+ this->dst[cur_offset] = this->dst[cur_offset - wide_offset];
+ }
+ }
+ }
+ private:
+ u8 ReadByte() {
+ return this->src[this->src_offset++];
+ }
+
+ bool CanRead() const {
+ return this->src_offset < this->src_size;
+ }
+
+ size_t GetCopySize(u8 control) {
+ size_t size = control;
+
+ if (control >= 0xF) {
+ do {
+ AMS_ABORT_UNLESS(this->CanRead());
+ control = this->ReadByte();
+ size += control;
+ } while (control == 0xFF);
+ }
+
+ return size;
+ }
+
+ void Copy(size_t size) {
+ __builtin_memcpy(this->dst + this->dst_offset, this->src + this->src_offset, size);
+ this->dst_offset += size;
+ this->src_offset += size;
+ }
+ };
+
+ }
+
+ void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size) {
+ /* Create an execute a decompressor. */
+ Lz4Uncompressor(dst, dst_size, src, src_size).Uncompress();
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp b/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp
new file mode 100644
index 000000000..b820209d5
--- /dev/null
+++ b/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#pragma once
+
+namespace ams::secmon::loader {
+
+ void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size);
+
+}
\ No newline at end of file
diff --git a/exosphere2/loader_stub/source/start.s b/exosphere2/loader_stub/source/start.s
new file mode 100644
index 000000000..2f432aaa7
--- /dev/null
+++ b/exosphere2/loader_stub/source/start.s
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */
+#define cpuactlr_el1 s3_1_c15_c2_0
+#define cpuectlr_el1 s3_1_c15_c2_1
+
+.macro RESET_CORE
+ mov x0, #(1 << 63)
+ msr cpuactlr_el1, x0 /* disable regional clock gating */
+ isb
+ mov x0, #3
+ msr rmr_el3, x0
+ isb
+ dsb sy
+ /* Nintendo forgot to copy-paste the branch instruction below. */
+ 1:
+ wfi
+ b 1b
+.endm
+
+.macro ERRATUM_INVALIDATE_BTB_AT_BOOT
+/* Nintendo copy-pasted https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/nvidia/tegra/common/aarch64/tegra_helpers.S#L312 */
+ /*
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+ /* The following comments are mine. */
+
+ /*
+ Enable invalidates of branch target buffer, then flush
+ the entire instruction cache at the local level, and
+ with the reg change, the branch target buffer, then disable
+ invalidates of the branch target buffer again.
+ */
+ mrs x0, cpuactlr_el1
+ orr x0, x0, #1
+ msr cpuactlr_el1, x0
+
+ dsb sy
+ isb
+ ic iallu
+ dsb sy
+ isb
+
+ mrs x0, cpuactlr_el1
+ bic x0, x0, #1
+ msr cpuactlr_el1, x0
+
+.rept 7
+ nop /* wait long enough for the write to cpuactlr_el1 to have completed */
+.endr
+
+ /* if the OS lock is set, disable it and request a warm reset */
+ mrs x0, oslsr_el1
+ ands x0, x0, #2
+ b.eq 2f
+ mov x0, xzr
+ msr oslar_el1, x0
+
+ RESET_CORE
+
+.rept 65
+ nop /* guard against speculative excecution */
+.endr
+
+ 2:
+ /* set the OS lock */
+ mov x0, #1
+ msr oslar_el1, x0
+.endm
+
+.section .crt0.text.start, "ax", %progbits
+.align 6
+.global _start
+_start:
+ /* mask all interrupts */
+ msr daifset, #0xF
+
+ /* Fixup hardware erratum */
+ ERRATUM_INVALIDATE_BTB_AT_BOOT
+
+ /* Set the stack pointer to a temporary location. */
+ ldr x20, =0x7C010800
+ mov sp, x20
+
+ /* Call our init array functions. */
+ bl __libc_init_array
+
+ /* Uncompress the program and iram boot code images. */
+ b _ZN3ams6secmon6loader20UncompressAndExecuteEv
diff --git a/exosphere2/program/Makefile b/exosphere2/program/Makefile
new file mode 100644
index 000000000..078b88fd9
--- /dev/null
+++ b/exosphere2/program/Makefile
@@ -0,0 +1,128 @@
+#---------------------------------------------------------------------------------
+# Define the atmosphere board and cpu
+#---------------------------------------------------------------------------------
+export ATMOSPHERE_BOARD := nx-hac-001
+export ATMOSPHERE_CPU := arm-cortex-a57
+
+#---------------------------------------------------------------------------------
+# pull in common atmosphere configuration
+#---------------------------------------------------------------------------------
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/exosphere.mk
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(BUILD),$(notdir $(CURDIR)))
+#---------------------------------------------------------------------------------
+
+export OUTPUT := $(CURDIR)/$(TARGET)
+export TOPDIR := $(CURDIR)
+export DEPSDIR := $(CURDIR)/$(BUILD)
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
+ $(foreach dir,$(DATA),$(CURDIR)/$(dir))
+
+CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \
+ $(notdir $(wildcard $(dir)/*.c))))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c)))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c)))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c)))
+
+CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \
+ $(notdir $(wildcard $(dir)/*.cpp))))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp)))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp)))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp)))
+
+SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \
+ $(notdir $(wildcard $(dir)/*.s))))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s)))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s)))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s)))
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES_BIN := $(addsuffix .o,$(BINFILES))
+export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+export OFILES := $(OFILES_BIN) $(OFILES_SRC)
+export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES))))
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ -I.
+
+export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
+
+.PHONY: $(BUILD) clean all
+
+#---------------------------------------------------------------------------------
+all: $(BUILD) check_libexo
+
+$(BUILD): check_libexo
+ @[ -d $@ ] || mkdir -p $@
+ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+
+check_libexo:
+ @$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm64
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4
+
+#---------------------------------------------------------------------------------
+else
+.PHONY: all
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+all : $(OUTPUT).lz4
+
+$(OUTPUT).lz4 : $(OUTPUT).bin
+ @python ../split_program.py $(OUTPUT).bin $(dir $(OUTPUT))
+ @echo built ... $(notdir $@)
+
+$(OUTPUT).bin : $(OUTPUT).elf
+ $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@
+ @echo built ... $(notdir $@)
+
+$(OUTPUT).elf : $(OFILES) ../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a
+
+secmon_crt0_cpp.o secmon_make_page_table.o : CFLAGS += -fno-builtin
+
+%.elf:
+ @echo linking $(notdir $@)
+ $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
+ @$(NM) -CSn $@ > $(notdir $*.lst)
+
+$(OFILES_SRC) : $(HFILES_BIN)
+
+#---------------------------------------------------------------------------------
+# you need a rule like this for each extension you use as binary data
+#---------------------------------------------------------------------------------
+%.bin.o %_bin.h: %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
diff --git a/exosphere2/program/program.ld b/exosphere2/program/program.ld
new file mode 100644
index 000000000..61e8720dc
--- /dev/null
+++ b/exosphere2/program/program.ld
@@ -0,0 +1,277 @@
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+
+MEMORY
+{
+ NULL : ORIGIN = 0, LENGTH = 4K
+ unused_region : ORIGIN = 0x1000, LENGTH = 4K
+ iram_boot_code : ORIGIN = 0x040032000, LENGTH = 48K
+ tzram : ORIGIN = 0x07C010000, LENGTH = 64K
+
+ /* Warmboot code follows the vectors in memory. */
+ /* However, we can't know for sure how big warmboot is, so we'll just say it's 2K. */
+ warmboot_text : ORIGIN = ORIGIN(tzram) + 10K, LENGTH = 2K
+
+ main : ORIGIN = 0x1F00C0000, LENGTH = 48K
+ tzram_boot_code : ORIGIN = 0x1F01C0800, LENGTH = 6K
+
+ glob : ORIGIN = 0x040032000, LENGTH = 64K
+}
+
+SECTIONS
+{
+ .metadata :
+ {
+ . = ALIGN(8);
+ KEEP (*(.metadata .metadata.*))
+ . = ALIGN(8);
+ } >unused_region AT>glob
+
+ PROVIDE(__glob_start__ = ORIGIN(glob));
+ . = __glob_start__;
+
+ __bootcode_start__ = ABSOLUTE(.);
+
+ .crt0 :
+ {
+ KEEP (*(.crt0 .crt0.*))
+ KEEP (secmon_crt0_cpp.o(.text*))
+ KEEP (secmon_boot_cache.o(.text*))
+ KEEP (secmon_make_page_table.o(.text*))
+ *(.crt0.rodata*)
+ secmon_crt0_cpp.o(.rodata*)
+ secmon_boot_cache.o(.rodata*)
+ secmon_make_page_table.o(.rodata*)
+ *(.crt0.data*)
+ secmon_crt0_cpp.o(.data*)
+ secmon_boot_cache.o(.data*)
+ secmon_make_page_table.o(.data*)
+ . = ALIGN(8);
+ } >iram_boot_code AT>glob
+
+ .preinit_array ALIGN(8) :
+ {
+ PROVIDE (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE (__preinit_array_end = .);
+ } >iram_boot_code AT>glob
+
+ .init_array ALIGN(8) :
+ {
+ PROVIDE (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE (__init_array_end = .);
+ } >iram_boot_code AT>glob
+
+ .fini_array ALIGN(8) :
+ {
+ PROVIDE (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ PROVIDE (__fini_array_end = .);
+ } >iram_boot_code AT>glob
+
+ .ctors ALIGN(8) :
+ {
+ KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ } >iram_boot_code AT>glob
+
+ .dtors ALIGN(8) :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ } >iram_boot_code AT>glob
+
+ __bootcode_end__ = ABSOLUTE(.);
+
+ __program_start__ = ABSOLUTE(.);
+ .tzram_boot_code :
+ {
+ KEEP(secmon_main.o(.text*))
+ KEEP(secmon_boot_functions.o(.text*))
+ KEEP(secmon_boot_config.o(.text*))
+ KEEP(secmon_boot_setup.o(.text*))
+ KEEP(secmon_package2.o(.text*))
+ KEEP(secmon_key_data.o(.text*))
+ secmon_main.o(.rodata*)
+ secmon_boot_functions.o(.rodata*)
+ secmon_boot_config.o(.rodata*)
+ secmon_boot_setup.o(.rodata*)
+ secmon_package2.o(.rodata*)
+ secmon_key_data.o(.rodata*)
+ secmon_main.o(.data*)
+ secmon_boot_functions.o(.data*)
+ secmon_boot_config.o(.data*)
+ secmon_boot_setup.o(.data*)
+ secmon_package2.o(.data*)
+ secmon_key_data.o(.data*)
+ . = ALIGN(8);
+ } >tzram_boot_code AT>glob
+
+ .tzram_boot_code.bss :
+ {
+ __boot_bss_start__ = ABSOLUTE(.);
+ secmon_main.o(.bss* COMMON)
+ secmon_boot_functions.o(.bss* COMMON)
+ secmon_boot_config.o(.bss* COMMON)
+ secmon_boot_setup.o(.bss* COMMON)
+ secmon_package2.o(.bss* COMMON)
+ secmon_key_data.o(.bss* COMMON)
+ __boot_bss_end__ = ABSOLUTE(.);
+ } >tzram_boot_code AT>glob
+
+ .tzram_boot_code.fill :
+ {
+ FILL(0x00000000);
+ . = ORIGIN(tzram_boot_code) + LENGTH(tzram_boot_code) - 1;
+ BYTE(0x00);
+ } > tzram_boot_code AT>glob
+
+ .vectors :
+ {
+ KEEP (*(.vectors*))
+ . = ALIGN(0x100);
+ } >main AT>glob
+
+
+ .warmboot :
+ {
+ KEEP (*(.warmboot.text.start)) /* Should be first */
+ KEEP (*(.warmboot.text*))
+ KEEP(secmon_setup_warm.o(.text*))
+ KEEP(tsec_*.o(.text*))
+ KEEP (*(.warmboot.rodata*))
+ KEEP(secmon_setup_warm.o(.rodata*))
+ KEEP(tsec_*.o(.rodata*))
+ KEEP (*(.warmboot.data*))
+ KEEP(secmon_setup_warm.o(.data*))
+ KEEP(tsec_*.o(.data*))
+ } >warmboot_text AT>glob
+
+ .text :
+ {
+ *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+ *(.text.exit .text.exit.*)
+ *(.text.startup .text.startup.*)
+ *(.text.hot .text.hot.*)
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ . = ALIGN(8);
+ } >main AT>glob
+
+ .init :
+ {
+ KEEP( *(.init) )
+ . = ALIGN(8);
+ } >main AT>glob
+
+ .plt :
+ {
+ *(.plt)
+ *(.iplt)
+ . = ALIGN(8);
+ } >main AT>glob
+
+ .fini :
+ {
+ KEEP( *(.fini) )
+ . = ALIGN(8);
+ } >main AT>glob
+
+
+ /* =========== RODATA section =========== */
+ . = ALIGN(8);
+ __rodata_start = . ;
+
+ .rodata :
+ {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ . = ALIGN(8);
+ } >main AT>glob
+
+ .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >main AT>glob
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >main AT>glob
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >main AT>glob
+ .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >main AT>glob
+
+ .hash : { *(.hash) } >main AT>glob
+
+ /* =========== DATA section =========== */
+ . = ALIGN(8);
+ __data_start = . ;
+
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >main AT>glob
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >main AT>glob
+ .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >main AT>glob
+ .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >main AT>glob
+
+ __got_start__ = .;
+
+ .got : { *(.got) *(.igot) } >main AT>glob
+ .got.plt : { *(.got.plt) *(.igot.plt) } >main AT>glob
+
+ __got_end__ = .;
+
+ .data ALIGN(8) :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ } >main AT>glob
+
+ .bss ALIGN(8) (NOLOAD) :
+ {
+ __bss_start__ = ABSOLUTE(.);
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ . = ALIGN(16);
+ __bss_end__ = ABSOLUTE(.);
+ } >main AT>glob
+
+ __program_end__ = ABSOLUTE(.);
+
+ /* ==================
+ ==== Metadata ====
+ ================== */
+
+ /* Discard sections that difficult post-processing */
+ /DISCARD/ : { *(.group .comment .note .interp) }
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+}
\ No newline at end of file
diff --git a/exosphere2/program/program.specs b/exosphere2/program/program.specs
new file mode 100644
index 000000000..22b789960
--- /dev/null
+++ b/exosphere2/program/program.specs
@@ -0,0 +1,4 @@
+%rename link old_link
+
+*link:
+%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic -nostdlib -nostartfiles
\ No newline at end of file
diff --git a/exosphere2/program/sc7fw/Makefile b/exosphere2/program/sc7fw/Makefile
new file mode 100644
index 000000000..31439e834
--- /dev/null
+++ b/exosphere2/program/sc7fw/Makefile
@@ -0,0 +1,122 @@
+#---------------------------------------------------------------------------------
+# Define the atmosphere board and cpu
+#---------------------------------------------------------------------------------
+export ATMOSPHERE_BOARD := nx-hac-001
+export ATMOSPHERE_CPU := arm7tdmi
+
+#---------------------------------------------------------------------------------
+# pull in common atmosphere configuration
+#---------------------------------------------------------------------------------
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../../libraries/config/templates/exosphere.mk
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(BUILD),$(notdir $(CURDIR)))
+#---------------------------------------------------------------------------------
+
+export OUTPUT := $(CURDIR)/$(TARGET)
+export TOPDIR := $(CURDIR)
+export DEPSDIR := $(CURDIR)/$(BUILD)
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
+ $(foreach dir,$(DATA),$(CURDIR)/$(dir))
+
+CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \
+ $(notdir $(wildcard $(dir)/*.c))))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c)))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c)))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c)))
+
+CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \
+ $(notdir $(wildcard $(dir)/*.cpp))))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp)))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp)))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp)))
+
+SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \
+ $(notdir $(wildcard $(dir)/*.s))))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s)))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s)))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s)))
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES_BIN := $(addsuffix .o,$(BINFILES))
+export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+export OFILES := $(OFILES_BIN) $(OFILES_SRC)
+export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES))))
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ -I.
+
+export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
+
+.PHONY: $(BUILD) clean all
+
+#---------------------------------------------------------------------------------
+all: $(BUILD) check_libexo
+
+$(BUILD): check_libexo
+ @[ -d $@ ] || mkdir -p $@
+ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+
+check_libexo:
+ @$(MAKE) --no-print-directory -C ../../../libraries/libexosphere arm
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4
+
+#---------------------------------------------------------------------------------
+else
+.PHONY: all
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+all : $(OUTPUT).bin
+
+$(OUTPUT).bin : $(OUTPUT).elf
+ $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@
+ @echo built ... $(notdir $@)
+
+$(OUTPUT).elf : $(OFILES) ../../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a
+
+%.elf:
+ @echo linking $(notdir $@)
+ $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
+ @$(NM) -CSn $@ > $(notdir $*.lst)
+
+$(OFILES_SRC) : $(HFILES_BIN)
+
+#---------------------------------------------------------------------------------
+# you need a rule like this for each extension you use as binary data
+#---------------------------------------------------------------------------------
+%.bin.o %_bin.h: %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
diff --git a/exosphere2/program/sc7fw/sc7fw.ld b/exosphere2/program/sc7fw/sc7fw.ld
new file mode 100644
index 000000000..e8a097dbb
--- /dev/null
+++ b/exosphere2/program/sc7fw/sc7fw.ld
@@ -0,0 +1,183 @@
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+
+MEMORY
+{
+ NULL : ORIGIN = 0, LENGTH = 4K
+ sc7fw : ORIGIN = 0x40003000, LENGTH = 4K
+}
+
+
+SECTIONS
+{
+ /* =========== CODE section =========== */
+ PROVIDE(__start__ = ORIGIN(sc7fw));
+ . = __start__;
+ __code_start = . ;
+
+ .vectors :
+ {
+ KEEP (*(.vectors .vectors.*))
+ . = ALIGN(8);
+ } >sc7fw
+
+ .text :
+ {
+ *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+ *(.text.exit .text.exit.*)
+ *(.text.startup .text.startup.*)
+ *(.text.hot .text.hot.*)
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ . = ALIGN(8);
+ } >sc7fw
+
+ .init :
+ {
+ KEEP( *(.init) )
+ . = ALIGN(8);
+ } >sc7fw
+
+ .plt :
+ {
+ *(.plt)
+ *(.iplt)
+ . = ALIGN(8);
+ } >sc7fw
+
+ .fini :
+ {
+ KEEP( *(.fini) )
+ . = ALIGN(8);
+ } >sc7fw
+
+
+ /* =========== RODATA section =========== */
+ . = ALIGN(8);
+ __rodata_start = . ;
+
+ .rodata :
+ {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ . = ALIGN(8);
+ } >sc7fw
+
+ .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >sc7fw
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >sc7fw
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >sc7fw
+ .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >sc7fw
+
+ .hash : { *(.hash) } >sc7fw
+
+ /* =========== DATA section =========== */
+ . = ALIGN(8);
+ __data_start = . ;
+
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >sc7fw
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >sc7fw
+ .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >sc7fw
+ .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >sc7fw
+
+ .preinit_array ALIGN(8) :
+ {
+ PROVIDE (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE (__preinit_array_end = .);
+ } >sc7fw
+
+ .init_array ALIGN(8) :
+ {
+ PROVIDE (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE (__init_array_end = .);
+ } >sc7fw
+
+ .fini_array ALIGN(8) :
+ {
+ PROVIDE (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ PROVIDE (__fini_array_end = .);
+ } >sc7fw
+
+ .ctors ALIGN(8) :
+ {
+ KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ } >sc7fw
+
+ .dtors ALIGN(8) :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ } >sc7fw
+
+ __got_start__ = .;
+
+ .got : { *(.got) *(.igot) } >sc7fw
+ .got.plt : { *(.got.plt) *(.igot.plt) } >sc7fw
+
+ __got_end__ = .;
+
+ .data ALIGN(8) :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ } >sc7fw
+
+ __bss_start__ = .;
+ .bss ALIGN(8) :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ . = ALIGN(16);
+ } >sc7fw
+ __bss_end__ = .;
+
+ __end__ = ABSOLUTE(.) ;
+
+ /* ==================
+ ==== Metadata ====
+ ================== */
+
+ /* Discard sections that difficult post-processing */
+ /DISCARD/ : { *(.group .comment .note .interp) }
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+}
\ No newline at end of file
diff --git a/exosphere2/program/sc7fw/sc7fw.specs b/exosphere2/program/sc7fw/sc7fw.specs
new file mode 100644
index 000000000..13455dc90
--- /dev/null
+++ b/exosphere2/program/sc7fw/sc7fw.specs
@@ -0,0 +1,7 @@
+%rename link old_link
+
+*link:
+%(old_link) -T %:getenv(TOPDIR /sc7fw.ld) --gc-sections --nmagic -nostdlib -nostartfiles
+
+*startfile:
+crti%O%s crtbegin%O%s
diff --git a/exosphere2/program/sc7fw/source/sc7fw_dram.cpp b/exosphere2/program/sc7fw/source/sc7fw_dram.cpp
new file mode 100644
index 000000000..dcdf9c94b
--- /dev/null
+++ b/exosphere2/program/sc7fw/source/sc7fw_dram.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "sc7fw_util.hpp"
+#include "sc7fw_dram.hpp"
+
+namespace ams::sc7fw {
+
+ namespace {
+
+ constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress();
+
+ void UpdateEmcTiming() {
+ /* Enable timing update. */
+ reg::Write(EMC_ADDRESS(EMC_TIMING_CONTROL), EMC_REG_BITS_ENUM(TIMING_CONTROL_TIMING_UPDATE, ENABLED));
+
+ /* Wait for the timing update to complete. */
+ while (!reg::HasValue(EMC_ADDRESS(EMC_EMC_STATUS), EMC_REG_BITS_ENUM(EMC_STATUS_TIMING_UPDATE_STALLED, DONE))) {
+ /* ... */
+ }
+ }
+
+ void RequestAllPadsPowerDown(uintptr_t addr, uintptr_t expected) {
+ constexpr u32 DpdAllRequestValue = reg::Encode(PMC_REG_BITS_ENUM(IO_DPD_REQ_CODE, DPD_ON)) | 0x0FFFFFFF;
+ const auto RequestAddress = addr;
+ const auto StatusAddress = addr + 4;
+
+ /* Request all pads enter power down. */
+ reg::Write(PMC + RequestAddress, DpdAllRequestValue);
+
+ /* Wait until the status reflects our expectation (and all pads are shut down). */
+ while (reg::Read(PMC + StatusAddress) != expected) { /* ... */ }
+
+ /* Wait a little while to allow the power down status to propagate. */
+ SpinLoop(0x20);
+ };
+
+ }
+
+ void SaveEmcFsp() {
+ /* We require that the RAM is LPDDR4. */
+ AMS_ABORT_UNLESS(reg::HasValue(EMC_ADDRESS(EMC_FBIO_CFG5), EMC_REG_BITS_ENUM(FBIO_CFG5_DRAM_TYPE, LPDDR4)));
+
+ /* Read the frequency set points from MRW3. */
+ constexpr u32 FspShift = 6;
+ constexpr u32 FspBits = 2;
+ constexpr u32 FspMask = ((1u << FspBits) - 1) << FspShift;
+ static_assert(FspMask == 0x000000C0);
+ const u32 fsp = (reg::Read(EMC_ADDRESS(EMC_MRW3)) & FspMask) >> FspShift;
+
+ /* Write the fsp to PMC_SCRATCH18, where it will be restored to MRW3 by brom. */
+ reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH18, REG_BITS_VALUE(FspShift, FspBits, fsp));
+
+ /* Write the fsp twice to PMC_SCRATCH12, where it will be restored to MRW12 by brom. */
+ reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH12, REG_BITS_VALUE(FspShift, FspBits, fsp), REG_BITS_VALUE(FspShift + 8, FspBits, fsp));
+
+ /* Write the fsp twice to PMC_SCRATCH13, where it will be restored to MRW13 by brom. */
+ reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH13, REG_BITS_VALUE(FspShift, FspBits, fsp), REG_BITS_VALUE(FspShift + 8, FspBits, fsp));
+ }
+
+ void EnableSdramSelfRefresh() {
+ /* We require that the RAM is dual-channel. */
+ AMS_ABORT_UNLESS(reg::HasValue(EMC_ADDRESS(EMC_FBIO_CFG7), EMC_REG_BITS_ENUM(FBIO_CFG7_CH1_ENABLE, ENABLE)));
+
+ /* Disable RAM's ability to dynamically self-refresh, and to opportunistically perform powerdown. */
+ reg::Write(EMC_ADDRESS(EMC_CFG), EMC_REG_BITS_ENUM(CFG_DYN_SELF_REF, DISABLED),
+ EMC_REG_BITS_ENUM(CFG_DRAM_ACPD, NO_POWERDOWN));
+
+ /* Update the EMC timing. */
+ UpdateEmcTiming();
+
+ /* Wait five microseconds. */
+ util::WaitMicroSeconds(5);
+
+ /* Disable ZQ calibration. */
+ reg::Write(EMC_ADDRESS(EMC_ZCAL_INTERVAL), 0);
+
+ /* Disable automatic calibration. */
+ reg::Write(EMC_ADDRESS(EMC_AUTO_CAL_CONFIG), EMC_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL, ENABLE),
+ EMC_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL, ENABLE),
+ EMC_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, DISABLE));
+
+ /* Get whether digital delay locked loops are enabled. */
+ const bool has_dll = reg::HasValue(EMC_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, ENABLED));
+ if (has_dll) {
+ /* If they are, disable them. */
+ reg::ReadWrite(EMC_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, DISABLED));
+ }
+
+ /* Update the EMC timing. */
+ UpdateEmcTiming();
+
+ /* If dll was enabled, wait until both EMC0 and EMC1 have dll disabled. */
+ if (has_dll) {
+ while (!reg::HasValue(EMC0_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, DISABLED))) { /* ... */ }
+ while (!reg::HasValue(EMC1_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, DISABLED))) { /* ... */ }
+ }
+
+ /* Stall all reads and writes. */
+ reg::Write(EMC_ADDRESS(EMC_REQ_CTRL), EMC_REG_BITS_VALUE(REQ_CTRL_STALL_ALL_READS, 1),
+ EMC_REG_BITS_VALUE(REQ_CTRL_STALL_ALL_WRITES, 1));
+
+ /* Wait until both EMC0 and EMC1 have no outstanding transactions. */
+ while (!reg::HasValue(EMC0_ADDRESS(EMC_EMC_STATUS), EMC_REG_BITS_ENUM(EMC_STATUS_NO_OUTSTANDING_TRANSACTIONS, COMPLETED))) { /* ... */ }
+ while (!reg::HasValue(EMC1_ADDRESS(EMC_EMC_STATUS), EMC_REG_BITS_ENUM(EMC_STATUS_NO_OUTSTANDING_TRANSACTIONS, COMPLETED))) { /* ... */ }
+
+ /* Enable self-refresh. */
+ reg::Write(EMC_ADDRESS(EMC_SELF_REF), EMC_REG_BITS_ENUM(SELF_REF_SREF_DEV_SELECTN, BOTH),
+ EMC_REG_BITS_ENUM(SELF_REF_SELF_REF_CMD, ENABLED));
+
+ /* Wait until both EMC and EMC1 are in self-refresh. */
+ const auto desired = reg::HasValue(EMC_ADDRESS(EMC_ADR_CFG), EMC_REG_BITS_ENUM(ADR_CFG_EMEM_NUMDEV, N2)) ? EMC_REG_BITS_ENUM(EMC_STATUS_DRAM_IN_SELF_REFRESH, BOTH_ENABLED)
+ : EMC_REG_BITS_ENUM(EMC_STATUS_DRAM_DEV0_IN_SELF_REFRESH, ENABLED);
+
+ /* NOTE: Nintendo's sc7 entry firmware has a bug here. */
+ /* Instead of waiting for both EMCs to report self-refresh, they just read the EMC_STATUS for each EMC. */
+ /* This is incorrect, per documentation. */
+ while (!reg::HasValue(EMC0_ADDRESS(EMC_EMC_STATUS), desired)) { /* ... */ }
+ while (!reg::HasValue(EMC1_ADDRESS(EMC_EMC_STATUS), desired)) { /* ... */ }
+ }
+
+ void EnableEmcAllSegmentsRefresh() {
+ constexpr int MR17_PASR_Segment = 17;
+
+ /* Write zeros to MR17_PASR_Segment to enable refresh for all segments for dev0. */
+ reg::Write(EMC_ADDRESS(EMC_MRW), EMC_REG_BITS_ENUM (MRW_DEV_SELECTN, DEV0),
+ EMC_REG_BITS_ENUM (MRW_CNT, EXT1),
+ EMC_REG_BITS_VALUE(MRW_MA, MR17_PASR_Segment),
+ EMC_REG_BITS_VALUE(MRW_OP, 0));
+
+ /* If dev1 exists, do the same for dev1. */
+ if (reg::HasValue(EMC_ADDRESS(EMC_ADR_CFG), EMC_REG_BITS_ENUM(ADR_CFG_EMEM_NUMDEV, N2))) {
+ reg::Write(EMC_ADDRESS(EMC_MRW), EMC_REG_BITS_ENUM (MRW_DEV_SELECTN, DEV1),
+ EMC_REG_BITS_ENUM (MRW_CNT, EXT1),
+ EMC_REG_BITS_VALUE(MRW_MA, MR17_PASR_Segment),
+ EMC_REG_BITS_VALUE(MRW_OP, 0));
+ }
+ }
+
+ void EnableDdrDeepPowerDown() {
+ /* Read and decode the parameters Nintendo stores in EMC_PMC_SCRATCH3. */
+ const u32 scratch3 = reg::Read(EMC_ADDRESS(EMC_PMC_SCRATCH3));
+ const bool weak_bias = (scratch3 & reg::EncodeMask(EMC_REG_BITS_MASK(PMC_SCRATCH3_WEAK_BIAS))) == reg::EncodeValue(EMC_REG_BITS_ENUM(PMC_SCRATCH3_WEAK_BIAS, ENABLED));
+ const u32 ddr_cntrl = (scratch3 & reg::EncodeMask(EMC_REG_BITS_MASK(PMC_SCRATCH3_DDR_CNTRL)));
+
+ /* Write the decoded value to PMC_DDR_CNTRL. */
+ reg::Write(PMC + APBDEV_PMC_DDR_CNTRL, ddr_cntrl);
+
+ /* If weak bias is enabled, set all VTT_E_WB bits in APBDEV_PMC_WEAK_BIAS. */
+ if (weak_bias) {
+ constexpr u32 WeakBiasVttEWbAll = 0x7FFF0000;
+ reg::Write(PMC + APBDEV_PMC_WEAK_BIAS, WeakBiasVttEWbAll);
+ }
+
+ /* Request that DPD3 pads power down. */
+ constexpr u32 EristaDpd3Mask = 0x0FFFFFFF;
+ constexpr u32 MarikoDpd3Mask = 0x0FFF9FFF;
+ if (true /* TODO: IsErista */) {
+ RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD3_REQ, EristaDpd3Mask);
+ } else {
+ RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD3_REQ, MarikoDpd3Mask);
+ }
+
+ /* Request that DPD4 pads power down. */
+ constexpr u32 Dpd4Mask = 0x0FFF1FFF;
+ RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD4_REQ, Dpd4Mask);
+ }
+
+}
diff --git a/exosphere2/program/sc7fw/source/sc7fw_dram.hpp b/exosphere2/program/sc7fw/source/sc7fw_dram.hpp
new file mode 100644
index 000000000..dca0a47b0
--- /dev/null
+++ b/exosphere2/program/sc7fw/source/sc7fw_dram.hpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::sc7fw {
+
+ void SaveEmcFsp();
+ void EnableSdramSelfRefresh();
+ void EnableEmcAllSegmentsRefresh();
+ void EnableDdrDeepPowerDown();
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s b/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s
new file mode 100644
index 000000000..ca8c3b787
--- /dev/null
+++ b/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+.section .vectors, "ax", %progbits
+.align 3
+.global reset
+reset:
+ b _start
+ b _ZN3ams5sc7fw16ExceptionHandlerEv
diff --git a/exosphere2/program/sc7fw/source/sc7fw_main.cpp b/exosphere2/program/sc7fw/source/sc7fw_main.cpp
new file mode 100644
index 000000000..0a082586a
--- /dev/null
+++ b/exosphere2/program/sc7fw/source/sc7fw_main.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "sc7fw_util.hpp"
+#include "sc7fw_dram.hpp"
+
+namespace ams::sc7fw {
+
+ namespace {
+
+ constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress();
+
+ void DisableCrail() {
+ /* Wait for CRAIL to be off. */
+ while (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_ENUM(PWRGATE_STATUS_CRAIL, OFF))) { /* ... */ }
+
+ /* Set CRAIL to be clamped. */
+ reg::ReadWrite(PMC + APBDEV_PMC_SET_SW_CLAMP, PMC_REG_BITS_VALUE(SET_SW_CLAMP_CRAIL, 1));
+
+ /* Wait for CRAIL to be clamped. */
+ while (!reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, PMC_REG_BITS_ENUM(CLAMP_STATUS_CRAIL, ENABLE))) { /* ... */ }
+
+ /* Spin loop for a short while, to allow time for the clamp to take effect. */
+ sc7fw::SpinLoop(10);
+
+ /* Initialize i2c-5. */
+ i2c::Initialize(i2c::Port_5);
+
+ /* Disable the voltage to CPU. */
+ pmic::DisableVddCpu(fuse::GetRegulator());
+
+ /* Wait 700 microseconds to ensure voltage is disabled. */
+ util::WaitMicroSeconds(700);
+ }
+
+ void DisableAllInterrupts() {
+ /* Disable all interrupts for bpmp in all interrupt controllers. */
+ reg::Write(PRI_ICTLR(ICTLR_COP_IER_CLR), ~0u);
+ reg::Write(SEC_ICTLR(ICTLR_COP_IER_CLR), ~0u);
+ reg::Write(TRI_ICTLR(ICTLR_COP_IER_CLR), ~0u);
+ reg::Write(QUAD_ICTLR(ICTLR_COP_IER_CLR), ~0u);
+ reg::Write(PENTA_ICTLR(ICTLR_COP_IER_CLR), ~0u);
+ reg::Write(HEXA_ICTLR(ICTLR_COP_IER_CLR), ~0u);
+ }
+
+ void EnterSc7() {
+ /* Disable read buffering and write buffering in the BPMP cache. */
+ reg::ReadWrite(AVP_CACHE_ADDRESS(AVP_CACHE_CONFIG), AVP_CACHE_REG_BITS_ENUM(DISABLE_WB, TRUE),
+ AVP_CACHE_REG_BITS_ENUM(DISABLE_RB, TRUE));
+
+ /* Ensure the CPU Rail is turned off. */
+ DisableCrail();
+
+ /* Disable all interrupts. */
+ DisableAllInterrupts();
+
+ /* Save the EMC FSP */
+ SaveEmcFsp();
+
+ /* Enable self-refresh for DRAM */
+ EnableSdramSelfRefresh();
+
+ /* Enable refresh for all EMC devices. */
+ EnableEmcAllSegmentsRefresh();
+
+ /* Enable deep power-down for ddr. */
+ EnableDdrDeepPowerDown();
+
+ /* Enable pad sampling during deep sleep. */
+ reg::ReadWrite(PMC + APBDEV_PMC_DPD_SAMPLE, PMC_REG_BITS_ENUM(DPD_SAMPLE_ON, ENABLE));
+ reg::Read(PMC + APBDEV_PMC_DPD_SAMPLE);
+
+ /* Wait a while for pad sampling to be enabled. */
+ sc7fw::SpinLoop(0x128);
+
+ /* Enter deep sleep. */
+ reg::ReadWrite(PMC + APBDEV_PMC_DPD_ENABLE, PMC_REG_BITS_ENUM(DPD_ENABLE_ON, ENABLE));
+
+ /* Wait forever until we're asleep. */
+ while (true) { /* ... */ }
+ }
+
+ }
+
+ void Main() {
+ EnterSc7();
+ }
+
+ NORETURN void ExceptionHandler() {
+ /* Write enable to MAIN_RESET. */
+ reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE));
+ while (true) { /* ... */ }
+ }
+
+}
+
+namespace ams::diag {
+
+ void AbortImpl() {
+ sc7fw::ExceptionHandler();
+ }
+
+}
diff --git a/exosphere2/program/sc7fw/source/sc7fw_start.s b/exosphere2/program/sc7fw/source/sc7fw_start.s
new file mode 100644
index 000000000..02761c112
--- /dev/null
+++ b/exosphere2/program/sc7fw/source/sc7fw_start.s
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+.section .text.start, "ax", %progbits
+.align 3
+.global _start
+_start:
+ /* Set CPSR_cf and CPSR_cf. */
+ msr cpsr_f, #0xC0
+ msr cpsr_cf, #0xD3
+
+ /* Set the stack pointer. */
+ ldr sp, =0x40005000
+
+ /* Set our link register to the exception handler. */
+ ldr lr, =_ZN3ams5sc7fw16ExceptionHandlerEv
+
+ /* Invoke main. */
+ bl _ZN3ams5sc7fw4MainEv
+
+ /* Infinite loop. */
+ 1: b 1b
\ No newline at end of file
diff --git a/exosphere2/program/sc7fw/source/sc7fw_util.hpp b/exosphere2/program/sc7fw/source/sc7fw_util.hpp
new file mode 100644
index 000000000..2202deb0f
--- /dev/null
+++ b/exosphere2/program/sc7fw/source/sc7fw_util.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::sc7fw {
+
+ void SpinLoop(unsigned int num);
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/sc7fw/source/sc7fw_util_asm.s b/exosphere2/program/sc7fw/source/sc7fw_util_asm.s
new file mode 100644
index 000000000..249c5b0f9
--- /dev/null
+++ b/exosphere2/program/sc7fw/source/sc7fw_util_asm.s
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+.section .text._ZN3ams5sc7fw8SpinLoopEj, "ax", %progbits
+.global _ZN3ams5sc7fw8SpinLoopEj
+.thumb_func
+.syntax unified
+_ZN3ams5sc7fw8SpinLoopEj:
+ 1:
+ /* Subtract one from the count. */
+ subs r0, r0, #1
+
+ /* If we aren't at zero, continue looping. */
+ bgt 1b
+
+ /* Return. */
+ bx lr
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_boot.hpp b/exosphere2/program/source/boot/secmon_boot.hpp
new file mode 100644
index 000000000..dca6d0091
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_boot.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::secmon::boot {
+
+ void MakePageTable();
+
+ void InitializeColdBoot();
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_boot_cache.cpp b/exosphere2/program/source/boot/secmon_boot_cache.cpp
new file mode 100644
index 000000000..ad0649c3e
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_boot_cache.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+
+namespace ams::secmon::boot {
+
+ /* TODO */
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_boot_config.cpp b/exosphere2/program/source/boot/secmon_boot_config.cpp
new file mode 100644
index 000000000..ad0649c3e
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_boot_config.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+
+namespace ams::secmon::boot {
+
+ /* TODO */
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp
new file mode 100644
index 000000000..3095f2f77
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_boot_functions.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_boot_functions.hpp"
+
+namespace ams::secmon::boot {
+
+ void ClearIram() {
+ /* Clear the boot code image from where it was loaded in IRAM. */
+ util::ClearMemory(MemoryRegionPhysicalIramBootCodeImage.GetPointer(), MemoryRegionPhysicalIramBootCodeImage.GetSize());
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_boot_functions.hpp b/exosphere2/program/source/boot/secmon_boot_functions.hpp
new file mode 100644
index 000000000..5ec201add
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_boot_functions.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::secmon::boot {
+
+ void ClearIram();
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_boot_setup.cpp b/exosphere2/program/source/boot/secmon_boot_setup.cpp
new file mode 100644
index 000000000..06b411f87
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_boot_setup.cpp
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_boot.hpp"
+#include "../secmon_setup.hpp"
+#include "../secmon_key_storage.hpp"
+
+namespace ams::secmon::boot {
+
+ namespace {
+
+ void ValidateSystemCounters() {
+ const uintptr_t sysctr0 = MemoryRegionVirtualDeviceSysCtr0.GetAddress();
+
+ /* Validate the system counter frequency is as expected. */
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_CNTFID0) == 19'200'000u);
+
+ /* Validate the system counters are as expected. */
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 0)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 1)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 2)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 3)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 4)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 5)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 6)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 7)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 8)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 9)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID(10)) == 0);
+ AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID(11)) == 0);
+ }
+
+ void SetupPmcRegisters() {
+ const auto pmc = MemoryRegionVirtualDevicePmc.GetAddress();
+
+ /* Set the physical address of the warmboot binary to scratch 1. */
+ reg::Write(pmc + APBDEV_PMC_SCRATCH1, static_cast(MemoryRegionPhysicalDramSecureDataStoreWarmbootFirmware.GetAddress()));
+
+ /* Configure logging by setting bits 18-19 of scratch 20. */
+ reg::ReadWrite(pmc + APBDEV_PMC_SCRATCH20, REG_BITS_VALUE(18, 2, 0));
+
+ /* Clear the wdt reset flag. */
+ reg::ReadWrite(pmc + APBDEV_PMC_SCRATCH190, REG_BITS_VALUE(0, 1, 0));
+
+ /* Configure warmboot to set Set FUSE_PRIVATEKEYDISABLE to KEY_INVISIBLE. */
+ reg::ReadWrite(pmc + APBDEV_PMC_SECURE_SCRATCH21, REG_BITS_VALUE(4, 1, 1));
+
+ /* Write the warmboot key. */
+ /* TODO */
+ }
+
+ /* This function derives the master kek and device keys using the tsec root key. */
+ /* NOTE: Exosphere does not use this in practice, and expects the bootloader to set up keys already. */
+ /* NOTE: This function is currently not implemented. If implemented, it will only be a reference implementation. */
+ [[maybe_unused]]
+ void DeriveMasterKekAndDeviceKey() {
+ /* TODO: Decide whether to implement this. */
+ }
+
+ void SetupRandomKey(int slot, se::KeySlotLockFlags flags) {
+ /* Create an aligned buffer to hold the key. */
+ constexpr size_t KeySize = se::AesBlockSize;
+ util::AlignedBuffer key;
+
+ /* Ensure data is consistent before triggering the SE. */
+ hw::FlushDataCache(key, KeySize);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Generate random bytes into the key. */
+ se::GenerateRandomBytes(key, KeySize);
+
+ /* Ensure that the CPU sees consistent data. */
+ hw::DataSynchronizationBarrierInnerShareable();
+ hw::FlushDataCache(key, KeySize);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Use the random bytes as a key source. */
+ se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_DeviceMaster, key, KeySize);
+
+ /* Lock the keyslot. */
+ se::LockAesKeySlot(slot, flags);
+ }
+
+ constinit const u8 MasterKeyVectorsDev[pkg1::OldMasterKeyCount + 1][se::AesBlockSize] = {
+ {0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE}, /* Zeroes encrypted with Master Key 00. */
+ {0x39, 0x33, 0xF9, 0x31, 0xBA, 0xE4, 0xA7, 0x21, 0x2C, 0xDD, 0xB7, 0xD8, 0xB4, 0x4E, 0x37, 0x23}, /* Master key 00 encrypted with Master key 01. */
+ {0x97, 0x29, 0xB0, 0x32, 0x43, 0x14, 0x8C, 0xA6, 0x85, 0xE9, 0x5A, 0x94, 0x99, 0x39, 0xAC, 0x5D}, /* Master key 01 encrypted with Master key 02. */
+ {0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3}, /* Master key 02 encrypted with Master key 03. */
+ {0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA}, /* Master key 03 encrypted with Master key 04. */
+ {0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC}, /* Master key 04 encrypted with Master key 05. */
+ {0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E}, /* Master key 05 encrypted with Master key 06. */
+ {0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19}, /* Master key 06 encrypted with Master key 07. */
+ {0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04}, /* Master key 07 encrypted with Master key 08. */
+ {0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE}, /* Master key 08 encrypted with Master key 09. */
+ {0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4}, /* Master key 09 encrypted with Master key 0A. */
+ };
+
+ constinit const u8 MasterKeyVectorsProd[pkg1::OldMasterKeyCount + 1][se::AesBlockSize] = {
+ {0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D}, /* Zeroes encrypted with Master Key 00. */
+ {0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD}, /* Master key 00 encrypted with Master key 01. */
+ {0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72}, /* Master key 01 encrypted with Master key 02. */
+ {0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07}, /* Master key 02 encrypted with Master key 03. */
+ {0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9}, /* Master key 03 encrypted with Master key 04. */
+ {0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE}, /* Master key 04 encrypted with Master key 05. */
+ {0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */
+ {0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F}, /* Master key 06 encrypted with Master key 07. */
+ {0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */
+ {0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80}, /* Master key 08 encrypted with Master key 09. */
+ {0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A}, /* Master key 09 encrypted with Master key 0A. */
+ };
+
+ bool TestKeyGeneration(int generation, bool is_prod) {
+ /* Decrypt the vector chain from generation to start. */
+ int slot = pkg1::AesKeySlot_Master;
+ for (int i = generation; i > 0; --i) {
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Temporary, slot, is_prod ? MasterKeyVectorsProd[i] : MasterKeyVectorsDev[i], se::AesBlockSize);
+ slot = pkg1::AesKeySlot_Temporary;
+ }
+
+ /* Decrypt the final vector. */
+ u8 test_vector[se::AesBlockSize];
+ se::DecryptAes128(test_vector, se::AesBlockSize, slot, is_prod ? MasterKeyVectorsProd[0] : MasterKeyVectorsDev[0], se::AesBlockSize);
+
+ constexpr u8 ZeroBlock[se::AesBlockSize] = {};
+ return crypto::IsSameBytes(ZeroBlock, test_vector, se::AesBlockSize);
+ }
+
+ int DetermineKeyGeneration(bool is_prod) {
+ /* Test each generation in order. */
+ for (int generation = 0; generation < pkg1::KeyGeneration_Count; ++generation) {
+ if (TestKeyGeneration(generation, is_prod)) {
+ return generation;
+ }
+ }
+
+ /* We must have found a correct key generation. */
+ AMS_ABORT();
+ }
+
+ void DeriveAllMasterKeys(bool is_prod, u8 * const work_block) {
+
+ /* Determine the generation. */
+ const int generation = DetermineKeyGeneration(is_prod);
+
+ /* Set the global generation. */
+ ::ams::secmon::impl::SetKeyGeneration(generation);
+
+ /* Derive all old keys. */
+ int slot = pkg1::AesKeySlot_Master;
+ for (int i = generation; i > 0; --i) {
+ /* Decrypt the old master key. */
+ se::DecryptAes128(work_block, se::AesBlockSize, slot, is_prod ? MasterKeyVectorsProd[i] : MasterKeyVectorsDev[i], se::AesBlockSize);
+
+ /* Set the old master key. */
+ SetMasterKey(generation - 1, work_block, se::AesBlockSize);
+
+ /* Set the old master key into a temporary keyslot. */
+ se::SetAesKey(pkg1::AesKeySlot_Temporary, work_block, se::AesBlockSize);
+
+ /* Perform the next decryption with the older master key. */
+ slot = pkg1::AesKeySlot_Temporary;
+ }
+ }
+
+ constinit const u8 DeviceMasterKeySourceSources[pkg1::OldDeviceMasterKeyCount][se::AesBlockSize] = {
+ {0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}, /* 4.0.0 Device Master Key Source Source. */
+ {0x6C, 0xEF, 0xC6, 0x27, 0x8B, 0xEC, 0x8A, 0x91, 0x99, 0xAB, 0x24, 0xAC, 0x4F, 0x1C, 0x8F, 0x1C}, /* 5.0.0 Device Master Key Source Source. */
+ {0x70, 0x08, 0x1B, 0x97, 0x44, 0x64, 0xF8, 0x91, 0x54, 0x9D, 0xC6, 0x84, 0x8F, 0x1A, 0xB2, 0xE4}, /* 6.0.0 Device Master Key Source Source. */
+ {0x8E, 0x09, 0x1F, 0x7A, 0xBB, 0xCA, 0x6A, 0xFB, 0xB8, 0x9B, 0xD5, 0xC1, 0x25, 0x9C, 0xA9, 0x17}, /* 6.2.0 Device Master Key Source Source. */
+ {0x8F, 0x77, 0x5A, 0x96, 0xB0, 0x94, 0xFD, 0x8D, 0x28, 0xE4, 0x19, 0xC8, 0x16, 0x1C, 0xDB, 0x3D}, /* 7.0.0 Device Master Key Source Source. */
+ {0x67, 0x62, 0xD4, 0x8E, 0x55, 0xCF, 0xFF, 0x41, 0x31, 0x15, 0x3B, 0x24, 0x0C, 0x7C, 0x07, 0xAE}, /* 8.1.0 Device Master Key Source Source. */
+ {0x4A, 0xC3, 0x4E, 0x14, 0x8B, 0x96, 0x4A, 0xD5, 0xD4, 0x99, 0x73, 0xC4, 0x45, 0xAB, 0x8B, 0x49}, /* 9.0.0 Device Master Key Source Source. */
+ {0x14, 0xB8, 0x74, 0x12, 0xCB, 0xBD, 0x0B, 0x8F, 0x20, 0xFB, 0x30, 0xDA, 0x27, 0xE4, 0x58, 0x94}, /* 9.1.0 Device Master Key Source Source. */
+ };
+
+ constinit const u8 DeviceMasterKekSourcesDev[pkg1::OldDeviceMasterKeyCount][se::AesBlockSize] = {
+ {0xD6, 0xBD, 0x9F, 0xC6, 0x18, 0x09, 0xE1, 0x96, 0x20, 0x39, 0x60, 0xD2, 0x89, 0x83, 0x31, 0x34}, /* 4.0.0 Device Master Kek Source. */
+ {0x59, 0x2D, 0x20, 0x69, 0x33, 0xB5, 0x17, 0xBA, 0xCF, 0xB1, 0x4E, 0xFD, 0xE4, 0xC2, 0x7B, 0xA8}, /* 5.0.0 Device Master Kek Source. */
+ {0xF6, 0xD8, 0x59, 0x63, 0x8F, 0x47, 0xCB, 0x4A, 0xD8, 0x74, 0x05, 0x7F, 0x88, 0x92, 0x33, 0xA5}, /* 6.0.0 Device Master Kek Source. */
+ {0x20, 0xAB, 0xF2, 0x0F, 0x05, 0xE3, 0xDE, 0x2E, 0xA1, 0xFB, 0x37, 0x5E, 0x8B, 0x22, 0x1A, 0x38}, /* 6.2.0 Device Master Kek Source. */
+ {0x60, 0xAE, 0x56, 0x68, 0x11, 0xE2, 0x0C, 0x99, 0xDE, 0x05, 0xAE, 0x68, 0x78, 0x85, 0x04, 0xAE}, /* 7.0.0 Device Master Kek Source. */
+ {0x94, 0xD6, 0xA8, 0xC0, 0x95, 0xAF, 0xD0, 0xA6, 0x27, 0x53, 0x5E, 0xE5, 0x8E, 0x70, 0x1F, 0x87}, /* 8.1.0 Device Master Kek Source. */
+ {0x61, 0x6A, 0x88, 0x21, 0xA3, 0x52, 0xB0, 0x19, 0x16, 0x25, 0xA4, 0xE3, 0x4C, 0x54, 0x02, 0x0F}, /* 9.0.0 Device Master Kek Source. */
+ {0x9D, 0xB1, 0xAE, 0xCB, 0xF6, 0xF6, 0xE3, 0xFE, 0xAB, 0x6F, 0xCB, 0xAF, 0x38, 0x03, 0xFC, 0x7B}, /* 9.1.0 Device Master Kek Source. */
+ };
+
+ constinit const u8 DeviceMasterKekSourcesProd[pkg1::OldDeviceMasterKeyCount][se::AesBlockSize] = {
+ {0x88, 0x62, 0x34, 0x6E, 0xFA, 0xF7, 0xD8, 0x3F, 0xE1, 0x30, 0x39, 0x50, 0xF0, 0xB7, 0x5D, 0x5D}, /* 4.0.0 Device Master Kek Source. */
+ {0x06, 0x1E, 0x7B, 0xE9, 0x6D, 0x47, 0x8C, 0x77, 0xC5, 0xC8, 0xE7, 0x94, 0x9A, 0xA8, 0x5F, 0x2E}, /* 5.0.0 Device Master Kek Source. */
+ {0x99, 0xFA, 0x98, 0xBD, 0x15, 0x1C, 0x72, 0xFD, 0x7D, 0x9A, 0xD5, 0x41, 0x00, 0xFD, 0xB2, 0xEF}, /* 6.0.0 Device Master Kek Source. */
+ {0x81, 0x3C, 0x6C, 0xBF, 0x5D, 0x21, 0xDE, 0x77, 0x20, 0xD9, 0x6C, 0xE3, 0x22, 0x06, 0xAE, 0xBB}, /* 6.2.0 Device Master Kek Source. */
+ {0x86, 0x61, 0xB0, 0x16, 0xFA, 0x7A, 0x9A, 0xEA, 0xF6, 0xF5, 0xBE, 0x1A, 0x13, 0x5B, 0x6D, 0x9E}, /* 7.0.0 Device Master Kek Source. */
+ {0xA6, 0x81, 0x71, 0xE7, 0xB5, 0x23, 0x74, 0xB0, 0x39, 0x8C, 0xB7, 0xFF, 0xA0, 0x62, 0x9F, 0x8D}, /* 8.1.0 Device Master Kek Source. */
+ {0x03, 0xE7, 0xEB, 0x43, 0x1B, 0xCF, 0x5F, 0xB5, 0xED, 0xDC, 0x97, 0xAE, 0x21, 0x8D, 0x19, 0xED}, /* 9.0.0 Device Master Kek Source. */
+ {0xCE, 0xFE, 0x41, 0x0F, 0x46, 0x9A, 0x30, 0xD6, 0xF2, 0xE9, 0x0C, 0x6B, 0xB7, 0x15, 0x91, 0x36}, /* 9.1.0 Device Master Kek Source. */
+ };
+
+ void DeriveAllDeviceMasterKeys(bool is_prod, u8 * const work_block) {
+ /* Get the current key generation. */
+ const int current_generation = secmon::GetKeyGeneration();
+
+ /* Iterate for all generations. */
+ for (int i = 0; i < pkg1::OldDeviceMasterKeyCount; ++i) {
+ const int generation = pkg1::KeyGeneration_4_0_0 + i;
+
+ /* Load the first master key into the temporary keyslot keyslot. */
+ LoadMasterKey(pkg1::AesKeySlot_Temporary, pkg1::KeyGeneration_1_0_0);
+
+ /* Decrypt the device master kek for the generation. */
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Temporary, pkg1::AesKeySlot_Temporary, is_prod ? DeviceMasterKekSourcesProd[i] : DeviceMasterKekSourcesDev[i], se::AesBlockSize);
+
+ /* Decrypt the device master key source into the work block. */
+ se::DecryptAes128(work_block, se::AesBlockSize, pkg1::AesKeySlot_DeviceMasterKeySourceKek, DeviceMasterKeySourceSources[i], se::AesBlockSize);
+
+ /* If we're decrypting the current device master key, decrypt into the keyslot. */
+ if (generation == current_generation) {
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_DeviceMaster, pkg1::AesKeySlot_Temporary, work_block, se::AesBlockSize);
+ } else {
+ /* Otherwise, decrypt the work block into itself and set the old device master key. */
+ se::DecryptAes128(work_block, se::AesBlockSize, pkg1::AesKeySlot_Temporary, work_block, se::AesBlockSize);
+
+ /* Set the device master key. */
+ SetDeviceMasterKey(generation, work_block, se::AesBlockSize);
+ }
+ }
+
+ /* Clear and lock the Device Master Key Source Kek. */
+ se::ClearAesKeySlot(pkg1::AesKeySlot_DeviceMasterKeySourceKek);
+ se::LockAesKeySlot(pkg1::AesKeySlot_DeviceMasterKeySourceKek, se::KeySlotLockFlags_AllLockKek);
+ }
+
+ void DeriveAllKeys() {
+ /* Determine whether we're prod. */
+ const bool is_prod = fuse::GetHardwareState() != fuse::HardwareState_Development;
+
+ /* Get the ephemeral work block. */
+ u8 * const work_block = se::GetEphemeralWorkBlock();
+ ON_SCOPE_EXIT { util::ClearMemory(work_block, se::AesBlockSize); };
+
+ /* Lock the master key as a key. */
+ se::LockAesKeySlot(pkg1::AesKeySlot_Master, se::KeySlotLockFlags_AllLockKey);
+
+ /* Setup a random key to protect the old master and device master keys. */
+ SetupRandomKey(pkg1::AesKeySlot_RandomForKeyStorageWrap, se::KeySlotLockFlags_AllLockKey);
+
+ /* Derive the master keys. */
+ DeriveAllMasterKeys(is_prod, work_block);
+
+ /* Derive the device master keys. */
+ DeriveAllDeviceMasterKeys(is_prod, work_block);
+
+ /* Lock the device master key as a kek. */
+ se::LockAesKeySlot(pkg1::AesKeySlot_DeviceMaster, se::KeySlotLockFlags_AllLockKek);
+
+ /* Setup a random key to protect user keys. */
+ SetupRandomKey(pkg1::AesKeySlot_RandomForUserWrap, se::KeySlotLockFlags_AllLockKek);
+ }
+
+ void InitializeKeys() {
+ /* Read lock all aes keys. */
+ for (int i = 0; i < se::AesKeySlotCount; ++i) {
+ se::LockAesKeySlot(i, se::KeySlotLockFlags_AllReadLock);
+ }
+
+ /* Lock the secure monitor aes keys to be secmon only and non-readable. */
+ for (int i = pkg1::AesKeySlot_SecmonStart; i < pkg1::AesKeySlot_SecmonEnd; ++i) {
+ se::LockAesKeySlot(i, se::KeySlotLockFlags_KeyUse | se::KeySlotLockFlags_PerKey);
+ }
+
+ /* Lock the unused keyslots entirely. */
+ static_assert(pkg1::AesKeySlot_UserEnd <= pkg1::AesKeySlot_SecmonStart);
+ for (int i = pkg1::AesKeySlot_UserEnd; i < pkg1::AesKeySlot_SecmonStart; ++i) {
+ se::LockAesKeySlot(i, se::KeySlotLockFlags_AllLockKek);
+ }
+
+ /* Read lock all rsa keys. */
+ for (int i = 0; i < se::RsaKeySlotCount; ++i) {
+ se::LockRsaKeySlot(i, se::KeySlotLockFlags_KeyUse | se::KeySlotLockFlags_PerKey | se::KeySlotLockFlags_KeyRead);
+ }
+
+ /* Initialize the rng. */
+ se::InitializeRandom();
+
+ /* Derive the master kek and device key. */
+ if constexpr (false) {
+ DeriveMasterKekAndDeviceKey();
+ }
+
+ /* Lock the device key as only usable as a kek. */
+ se::LockAesKeySlot(pkg1::AesKeySlot_Device, se::KeySlotLockFlags_AllLockKek);
+
+ /* Derive all keys. */
+ DeriveAllKeys();
+ }
+
+ }
+
+ void InitializeColdBoot() {
+ /* Ensure that the system counters are valid. */
+ ValidateSystemCounters();
+
+ /* Set the security engine to Tzram Secure. */
+ se::SetTzramSecure();
+
+ /* Set the security engine to Per Key Secure. */
+ se::SetPerKeySecure();
+
+ /* Setup the PMC registers. */
+ SetupPmcRegisters();
+
+ /* Lockout the scratch that we've just written. */
+ /* pmc::LockSecureRegisters(1); */
+
+ /* Generate a random srk. */
+ se::GenerateSrk();
+
+ /* Initialize the SE keyslots. */
+ InitializeKeys();
+
+ /* Save a test vector for the SE keyslots. */
+ SaveSecurityEngineAesKeySlotTestVector();
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_crt0.s b/exosphere2/program/source/boot/secmon_crt0.s
new file mode 100644
index 000000000..44d24ebcc
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_crt0.s
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+.section .crt0.text.start, "ax", %progbits
+.align 6
+.global _start
+_start:
+ /* mask all interrupts */
+ msr daifset, #0xF
+
+ /* Set the stack pointer to a temporary location. */
+ ldr x20, =0x7C010800
+ mov sp, x20
+
+ /* Initialize all memory to expected state. */
+ ldr x0, =__bss_start__
+ ldr x1, =__bss_end__
+ ldr x2, =__boot_bss_start__
+ ldr x3, =__boot_bss_end__
+ bl _ZN3ams6secmon4boot10InitializeEmmmm
+
+ /* Jump to the first bit of virtual code. */
+ ldr x16, =_ZN3ams6secmon5StartEv
+ br x16
diff --git a/exosphere2/program/source/boot/secmon_crt0_cpp.cpp b/exosphere2/program/source/boot/secmon_crt0_cpp.cpp
new file mode 100644
index 000000000..35b993ee5
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_crt0_cpp.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_boot.hpp"
+#include "../secmon_setup.hpp"
+
+extern "C" void __libc_init_array();
+
+namespace ams::secmon::boot {
+
+ void Initialize(uintptr_t bss_start, size_t bss_end, uintptr_t boot_bss_start, uintptr_t boot_bss_end) {
+ /* Set our start time. */
+ auto &secmon_params = *MemoryRegionPhysicalDeviceBootloaderParams.GetPointer();
+ secmon_params.secmon_start_time = *reinterpret_cast(MemoryRegionPhysicalDeviceTimer.GetAddress() + 0x10);
+
+ /* Setup DMA controllers. */
+ SetupSocDmaControllers();
+
+ /* Make the page table. */
+ MakePageTable();
+
+ /* Setup memory controllers the MMU. */
+ SetupCpuMemoryControllersEnableMmu();
+
+ /* Clear bss. */
+ std::memset(reinterpret_cast(bss_start), 0, bss_end - bss_start);
+ std::memset(reinterpret_cast(boot_bss_start), 0, boot_bss_end - boot_bss_start);
+
+ /* Call init array. */
+ __libc_init_array();
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_key_data.cpp b/exosphere2/program/source/boot/secmon_key_data.cpp
new file mode 100644
index 000000000..ad0649c3e
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_key_data.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+
+namespace ams::secmon::boot {
+
+ /* TODO */
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp
new file mode 100644
index 000000000..738452dab
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_main.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_boot.hpp"
+#include "secmon_boot_functions.hpp"
+#include "../secmon_setup.hpp"
+#include "../secmon_misc.hpp"
+
+namespace ams::secmon {
+
+ void Main() {
+ /* Set library register addresses. */
+ /* actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); */
+ clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress());
+ /* flowctrl::SetRegisterAddress(); */
+ fuse::SetRegisterAddress(MemoryRegionVirtualDeviceFuses.GetAddress());
+ gic::SetRegisterAddress(MemoryRegionVirtualDeviceGicDistributor.GetAddress(), MemoryRegionVirtualDeviceGicCpuInterface.GetAddress());
+ i2c::SetRegisterAddress(i2c::Port_1, MemoryRegionVirtualDeviceI2c1.GetAddress());
+ i2c::SetRegisterAddress(i2c::Port_5, MemoryRegionVirtualDeviceI2c5.GetAddress());
+ /* pinmux::SetRegisterAddress(); */
+ pmc::SetRegisterAddress(MemoryRegionVirtualDevicePmc.GetAddress());
+ se::SetRegisterAddress(MemoryRegionVirtualDeviceSecurityEngine.GetAddress());
+ uart::SetRegisterAddress(MemoryRegionVirtualDeviceUart.GetAddress());
+ wdt::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress());
+ util::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress());
+
+ /* Get the secure monitor parameters. */
+ auto &secmon_params = *reinterpret_cast(MemoryRegionVirtualDeviceBootloaderParams.GetAddress());
+
+ /* Perform initialization. */
+ {
+ /* Perform initial setup. */
+ /* This checks the security engine's validity, and configures common interrupts in the GIC. */
+ /* This also initializes the global configuration context. */
+ secmon::Setup1();
+
+ /* Save the boot info. */
+ secmon::SaveBootInfo(secmon_params);
+
+ /* Perform cold-boot specific init. */
+ secmon::boot::InitializeColdBoot();
+
+ /* Setup the SoC security measures. */
+ secmon::SetupSocSecurity();
+
+ /* TODO: More init. */
+
+ /* Clear the crt0 code that was present in iram. */
+ secmon::boot::ClearIram();
+
+ /* Alert the bootloader that we're initialized. */
+ secmon_params.secmon_state = pkg1::SecureMonitorState_Initialized;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_make_page_table.cpp b/exosphere2/program/source/boot/secmon_make_page_table.cpp
new file mode 100644
index 000000000..dfde30d16
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_make_page_table.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_setup.hpp"
+#include "secmon_boot.hpp"
+
+namespace ams::secmon::boot {
+
+ namespace {
+
+ using namespace ams::mmu;
+
+ constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRoCode = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRoCode, MemoryAttributeIndexNormal);
+ constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRwCode = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwCode, MemoryAttributeIndexNormal);
+ constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRoData = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRoData, MemoryAttributeIndexNormal);
+ constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwData, MemoryAttributeIndexNormal);
+ constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexNormal);
+
+ constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureDevice = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwData, MemoryAttributeIndexDevice);
+ constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureDevice = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexDevice);
+
+
+ constexpr void ClearMemory(volatile u64 *start, size_t size) {
+ volatile u64 * const end = start + (size / sizeof(u64));
+
+ for (volatile u64 *cur = start; cur < end; ++cur) {
+ *cur = 0;
+ }
+ }
+
+ constexpr void MakePageTablesImpl(u64 *l1, u64 *l2, u64 *l3) {
+ /* Setup the L1 table. */
+ {
+ ClearMemory(l1, MemoryRegionPhysicalTzramL1PageTable.GetSize());
+
+ /* Create an L1 table entry for the physical region. */
+ SetL1TableEntry(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode);
+ static_assert(GetL1EntryIndex(MemoryRegionPhysical.GetAddress()) == 1);
+
+ /* Create an L1 mapping entry for dram. */
+ SetL1BlockEntry(l1, MemoryRegionDram.GetAddress(), MemoryRegionDram.GetAddress(), MemoryRegionDram.GetSize(), MappingAttributesEl3NonSecureRwData);
+
+ /* Create an L1 table entry for the virtual region. */
+ SetL1TableEntry(l1, MemoryRegionVirtual.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode);
+ }
+
+ /* Setup the L2 table. */
+ {
+ ClearMemory(l2, MemoryRegionPhysicalTzramL2L3PageTable.GetSize());
+
+ /* Create an L2 table entry for the virtual region. */
+ SetL2TableEntry(l2, MemoryRegionVirtualL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode);
+
+ /* Create an L2 table entry for the physical iram region. */
+ SetL2TableEntry(l2, MemoryRegionPhysicalIramL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode);
+
+ /* Create an L2 table entry for the physical tzram region. */
+ SetL2TableEntry(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode);
+ }
+
+ /* Setup the L3 table. */
+ {
+ /* L2 and L3 share a page table. */
+ if (l2 != l3) {
+ ClearMemory(l3, MemoryRegionPhysicalTzramL2L3PageTable.GetSize());
+ }
+
+ /* Identity-map TZRAM as rwx. */
+ SetL3BlockEntry(l3, MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize(), MappingAttributesEl3SecureRwCode);
+
+ /* Identity-map IRAM boot code as rwx. */
+ SetL3BlockEntry(l3, MemoryRegionPhysicalIramBootCode.GetAddress(), MemoryRegionPhysicalIramBootCode.GetAddress(), MemoryRegionPhysicalIramBootCode.GetSize(), MappingAttributesEl3SecureRwCode);
+
+ /* Map all devices. */
+ {
+ #define MAP_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \
+ SetL3BlockEntry(l3, MemoryRegionVirtualDevice##_NAME_.GetAddress(), MemoryRegionPhysicalDevice##_NAME_.GetAddress(), MemoryRegionVirtualDevice##_NAME_.GetSize(), _SECURE_ ? MappingAttributesEl3SecureDevice : MappingAttributesEl3NonSecureDevice);
+
+ AMS_SECMON_FOREACH_DEVICE_REGION(MAP_DEVICE_REGION);
+
+ #undef MAP_DEVICE_REGION
+ }
+
+ /* Map the IRAM SC7 work region. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualIramSc7Work.GetAddress(), MemoryRegionPhysicalIramSc7Work.GetAddress(), MemoryRegionVirtualIramSc7Work.GetSize(), MappingAttributesEl3NonSecureDevice);
+
+ /* Map the IRAM SC7 firmware region. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualIramSc7Firmware.GetAddress(), MemoryRegionPhysicalIramSc7Firmware.GetAddress(), MemoryRegionVirtualIramSc7Firmware.GetSize(), MappingAttributesEl3NonSecureDevice);
+
+ /* Map the TZRAM ro alias region. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualTzramReadOnlyAlias.GetAddress(), MemoryRegionPhysicalTzramReadOnlyAlias.GetAddress(), MemoryRegionVirtualTzramReadOnlyAlias.GetSize(), MappingAttributesEl3SecureRoData);
+
+ /* Map the DRAM secure data store region. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualDramSecureDataStore.GetAddress(), MemoryRegionPhysicalDramSecureDataStore.GetAddress(), MemoryRegionVirtualDramSecureDataStore.GetSize(), MappingAttributesEl3NonSecureDevice);
+
+ /* Map the program region as rwx. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualTzramProgram.GetAddress(), MemoryRegionPhysicalTzramProgram.GetAddress(), MemoryRegionVirtualTzramProgram.GetSize(), MappingAttributesEl3SecureRwCode);
+
+ /* Map the boot code region. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualTzramBootCode.GetAddress(), MemoryRegionPhysicalTzramBootCode.GetAddress(), MemoryRegionVirtualTzramBootCode.GetSize(), MappingAttributesEl3SecureRwCode);
+
+ /* Map the volatile data regions regions. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualTzramVolatileData.GetAddress(), MemoryRegionPhysicalTzramVolatileData.GetAddress(), MemoryRegionVirtualTzramVolatileData.GetSize(), MappingAttributesEl3SecureRwData);
+ SetL3BlockEntry(l3, MemoryRegionVirtualTzramVolatileStack.GetAddress(), MemoryRegionPhysicalTzramVolatileStack.GetAddress(), MemoryRegionVirtualTzramVolatileStack.GetSize(), MappingAttributesEl3SecureRwData);
+
+ /* Map the configuration data. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualTzramConfigurationData.GetAddress(), MemoryRegionPhysicalTzramConfigurationData.GetAddress(), MemoryRegionVirtualTzramConfigurationData.GetSize(), MappingAttributesEl3SecureRwData);
+
+ /* Map the page tables. */
+ SetL3BlockEntry(l3, util::AlignDown(MemoryRegionVirtualTzramL1PageTable.GetAddress(), PageSize), util::AlignDown(MemoryRegionPhysicalTzramL1PageTable.GetAddress(), PageSize), PageSize, MappingAttributesEl3SecureDevice);
+ SetL3BlockEntry(l3, MemoryRegionVirtualTzramL2L3PageTable.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), MemoryRegionVirtualTzramL2L3PageTable.GetSize(), MappingAttributesEl3SecureRwData);
+ }
+ }
+
+ constexpr bool ValidateTzramPageTables() {
+ u64 l1_table[MemoryRegionPhysicalTzramL1PageTable.GetSize() / sizeof(u64)] = {};
+ u64 l2_l3_table[MemoryRegionPhysicalTzramL2L3PageTable.GetSize() / sizeof(u64)] = {};
+
+ MakePageTablesImpl(l1_table, l2_l3_table, l2_l3_table);
+
+ return true;
+ }
+
+ static_assert(ValidateTzramPageTables());
+
+ }
+
+ void MakePageTable() {
+ u64 * const l1 = MemoryRegionPhysicalTzramL1PageTable.GetPointer();
+ u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer();
+ MakePageTablesImpl(l1, l2_l3, l2_l3);
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_package2.cpp b/exosphere2/program/source/boot/secmon_package2.cpp
new file mode 100644
index 000000000..ad0649c3e
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_package2.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+
+namespace ams::secmon::boot {
+
+ /* TODO */
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/boot/secmon_size_data.s b/exosphere2/program/source/boot/secmon_size_data.s
new file mode 100644
index 000000000..0943ef943
--- /dev/null
+++ b/exosphere2/program/source/boot/secmon_size_data.s
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+.section .metadata.sizes, "ax", %progbits
+.align 6
+.global __metadata__sizes
+__metadata__sizes:
+ .quad 0xAAAAAAAAAAAAAAAA, 0xBBBBBBBBBBBBBBBB
+ .quad __glob_start__
+ .quad __bootcode_start__
+ .quad __bootcode_end__
+ .quad __program_start__
+ .quad 0xCCCCCCCCCCCCCCCC, 0xDDDDDDDDDDDDDDDD
diff --git a/exosphere2/program/source/secmon_cache.inc b/exosphere2/program/source/secmon_cache.inc
new file mode 100644
index 000000000..1ee25f79a
--- /dev/null
+++ b/exosphere2/program/source/secmon_cache.inc
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+void FlushEntireDataCache();
+void InvalidateEntireDataCache();
+
+void EnsureMappingConsistency();
+void EnsureMappingConsistency(uintptr_t address);
+void EnsureInstructionConsistency();
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_cache_impl.inc b/exosphere2/program/source/secmon_cache_impl.inc
new file mode 100644
index 000000000..c486c8b4d
--- /dev/null
+++ b/exosphere2/program/source/secmon_cache_impl.inc
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace {
+
+ ALWAYS_INLINE int FloorLog2(int v) {
+ return BITSIZEOF(u32) - (hw::CountLeadingZeros(static_cast(v)) + 1);
+ }
+
+ ALWAYS_INLINE int CeilLog2(int v) {
+ const int log = FloorLog2(v);
+ return ((1 << log) == v) ? log : log + 1;
+ }
+
+ void FlushDataCacheTo(int loc) {
+ for (int level = 0; level < loc; ++level) {
+ /* Set the selection register. */
+ {
+ util::BitPack32 csselr = {};
+ csselr.Set(0);
+ csselr.Set(level);
+ HW_CPU_SET_CSSELR_EL1(csselr);
+ }
+
+ /* Ensure that reordering doesn't occur around this operation. */
+ hw::InstructionSynchronizationBarrier();
+
+ /* Get ccsidr. */
+ util::BitPack32 ccsidr;
+ HW_CPU_GET_CCSIDR_EL1(ccsidr);
+
+ /* Get cache size id info. */
+ const int num_sets = ccsidr.Get() + 1;
+ const int num_ways = ccsidr.Get() + 1;
+ const int line_size = ccsidr.Get() + 4;
+
+ const int way_shift = 32 - FloorLog2(num_ways);
+ const int set_shift = line_size;
+
+ for (int way = 0; way <= num_ways; way++) {
+ for (int set = 0; set <= num_sets; set++) {
+ const u64 value = (static_cast(way) << way_shift) | (static_cast(set) << set_shift) | (static_cast(level) << 1);
+ __asm__ __volatile__("dc cisw, %[value]" :: [value]"r"(value) : "memory");
+ }
+ }
+ }
+ }
+
+ void InvalidateDataCacheTo(int loc) {
+ for (int level = 0; level < loc; ++level) {
+ /* Set the selection register. */
+ {
+ util::BitPack32 csselr = {};
+ csselr.Set(0);
+ csselr.Set(level);
+ HW_CPU_SET_CSSELR_EL1(csselr);
+ }
+
+ /* Ensure that reordering doesn't occur around this operation. */
+ hw::InstructionSynchronizationBarrier();
+
+ /* Get ccsidr. */
+ util::BitPack32 ccsidr;
+ HW_CPU_GET_CCSIDR_EL1(ccsidr);
+
+ /* Get cache size id info. */
+ const int num_sets = ccsidr.Get() + 1;
+ const int num_ways = ccsidr.Get() + 1;
+ const int line_size = ccsidr.Get() + 4;
+
+ const int way_shift = 32 - FloorLog2(num_ways);
+ const int set_shift = line_size;
+
+ for (int way = 0; way <= num_ways; way++) {
+ for (int set = 0; set <= num_sets; set++) {
+ const u64 value = (static_cast(way) << way_shift) | (static_cast(set) << set_shift) | (static_cast(level) << 1);
+ __asm__ __volatile__("dc isw, %[value]" :: [value]"r"(value) : "memory");
+ }
+ }
+ }
+ }
+
+}
+
+void FlushEntireDataCache() {
+ util::BitPack32 clidr;
+ HW_CPU_GET_CLIDR_EL1(clidr);
+ FlushDataCacheTo(clidr.Get());
+}
+
+void InvalidateEntireDataCache() {
+ util::BitPack32 clidr;
+ HW_CPU_GET_CLIDR_EL1(clidr);
+ InvalidateDataCacheTo(clidr.Get());
+}
+
+void EnsureMappingConsistency() {
+ ::ams::hw::DataSynchronizationBarrierInnerShareable();
+ ::ams::hw::InvalidateEntireTlb();
+ ::ams::hw::DataSynchronizationBarrierInnerShareable();
+
+ ::ams::hw::InstructionSynchronizationBarrier();
+}
+
+void EnsureMappingConsistency(uintptr_t address) {
+ ::ams::hw::DataSynchronizationBarrierInnerShareable();
+ ::ams::hw::InvalidateTlb(address);
+ ::ams::hw::DataSynchronizationBarrierInnerShareable();
+
+ ::ams::hw::InstructionSynchronizationBarrier();
+}
+
+void EnsureInstructionConsistency() {
+ ::ams::hw::DataSynchronizationBarrierInnerShareable();
+ ::ams::hw::InvalidateEntireInstructionCache();
+ ::ams::hw::DataSynchronizationBarrierInnerShareable();
+
+ ::ams::hw::InstructionSynchronizationBarrier();
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_cpu_context.cpp b/exosphere2/program/source/secmon_cpu_context.cpp
new file mode 100644
index 000000000..d2185a699
--- /dev/null
+++ b/exosphere2/program/source/secmon_cpu_context.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_cpu_context.hpp"
+#include "secmon_error.hpp"
+
+namespace ams::secmon {
+
+ namespace {
+
+ struct DebugRegisters {
+ u32 osdttrx_el1;
+ u32 osdtrtx_el1;
+ u32 mdscr_el1;
+ u32 oseccr_el1;
+ u32 mdccint_el1;
+ u32 dbgclaimclr_el1;
+ u32 dbgvcr32_el2;
+ u32 sder32_el3;
+ u32 mdcr_el2;
+ u32 mdcr_el3;
+ u32 spsr_el3;
+ u64 dbgbvcr_el1[12];
+ u64 dbgwvcr_el1[ 8];
+ };
+
+ struct CoreContext {
+ EntryContext entry_context;
+ bool is_on;
+ bool is_reset_expected;
+ bool is_debug_registers_saved;
+ DebugRegisters debug_registers;
+ };
+
+ void SaveDebugRegisters(DebugRegisters &dr) {
+ /* Set the OS lock; this will be unlocked by entry code. */
+ HW_CPU_SET_OSLAR_EL1(1);
+
+ /* Save general debug registers. */
+ HW_CPU_GET_OSDTRRX_EL1 (dr.osdttrx_el1);
+ HW_CPU_GET_OSDTRTX_EL1 (dr.osdtrtx_el1);
+ HW_CPU_GET_MDSCR_EL1 (dr.mdscr_el1);
+ HW_CPU_GET_OSECCR_EL1 (dr.oseccr_el1);
+ HW_CPU_GET_MDCCINT_EL1 (dr.mdccint_el1);
+ HW_CPU_GET_DBGCLAIMCLR_EL1(dr.dbgclaimclr_el1);
+ HW_CPU_GET_DBGVCR32_EL2 (dr.dbgvcr32_el2);
+ HW_CPU_GET_SDER32_EL3 (dr.sder32_el3);
+ HW_CPU_GET_MDCR_EL2 (dr.mdcr_el2);
+ HW_CPU_GET_MDCR_EL3 (dr.mdcr_el3);
+ HW_CPU_GET_SPSR_EL3 (dr.spsr_el3);
+
+ /* Save debug breakpoints. */
+ HW_CPU_GET_DBGBVR0_EL1(dr.dbgbvcr_el1[ 0]);
+ HW_CPU_GET_DBGBCR0_EL1(dr.dbgbvcr_el1[ 1]);
+ HW_CPU_GET_DBGBVR1_EL1(dr.dbgbvcr_el1[ 2]);
+ HW_CPU_GET_DBGBCR1_EL1(dr.dbgbvcr_el1[ 3]);
+ HW_CPU_GET_DBGBVR2_EL1(dr.dbgbvcr_el1[ 4]);
+ HW_CPU_GET_DBGBCR2_EL1(dr.dbgbvcr_el1[ 5]);
+ HW_CPU_GET_DBGBVR3_EL1(dr.dbgbvcr_el1[ 6]);
+ HW_CPU_GET_DBGBCR3_EL1(dr.dbgbvcr_el1[ 7]);
+ HW_CPU_GET_DBGBVR4_EL1(dr.dbgbvcr_el1[ 8]);
+ HW_CPU_GET_DBGBCR4_EL1(dr.dbgbvcr_el1[ 9]);
+ HW_CPU_GET_DBGBVR5_EL1(dr.dbgbvcr_el1[10]);
+ HW_CPU_GET_DBGBCR5_EL1(dr.dbgbvcr_el1[11]);
+
+ /* Save debug watchpoints. */
+ HW_CPU_GET_DBGWVR0_EL1(dr.dbgwvcr_el1[0]);
+ HW_CPU_GET_DBGWCR0_EL1(dr.dbgwvcr_el1[1]);
+ HW_CPU_GET_DBGWVR1_EL1(dr.dbgwvcr_el1[2]);
+ HW_CPU_GET_DBGWCR1_EL1(dr.dbgwvcr_el1[3]);
+ HW_CPU_GET_DBGWVR2_EL1(dr.dbgwvcr_el1[4]);
+ HW_CPU_GET_DBGWCR2_EL1(dr.dbgwvcr_el1[5]);
+ HW_CPU_GET_DBGWVR3_EL1(dr.dbgwvcr_el1[6]);
+ HW_CPU_GET_DBGWCR3_EL1(dr.dbgwvcr_el1[7]);
+ }
+
+ void RestoreDebugRegisters(const DebugRegisters &dr) {
+ /* Restore general debug registers. */
+ HW_CPU_SET_OSDTRRX_EL1 (dr.osdttrx_el1);
+ HW_CPU_SET_OSDTRTX_EL1 (dr.osdtrtx_el1);
+ HW_CPU_SET_MDSCR_EL1 (dr.mdscr_el1);
+ HW_CPU_SET_OSECCR_EL1 (dr.oseccr_el1);
+ HW_CPU_SET_MDCCINT_EL1 (dr.mdccint_el1);
+ HW_CPU_SET_DBGCLAIMCLR_EL1(dr.dbgclaimclr_el1);
+ HW_CPU_SET_DBGVCR32_EL2 (dr.dbgvcr32_el2);
+ HW_CPU_SET_SDER32_EL3 (dr.sder32_el3);
+ HW_CPU_SET_MDCR_EL2 (dr.mdcr_el2);
+ HW_CPU_SET_MDCR_EL3 (dr.mdcr_el3);
+ HW_CPU_SET_SPSR_EL3 (dr.spsr_el3);
+
+ /* Restore debug breakpoints. */
+ HW_CPU_SET_DBGBVR0_EL1(dr.dbgbvcr_el1[ 0]);
+ HW_CPU_SET_DBGBCR0_EL1(dr.dbgbvcr_el1[ 1]);
+ HW_CPU_SET_DBGBVR1_EL1(dr.dbgbvcr_el1[ 2]);
+ HW_CPU_SET_DBGBCR1_EL1(dr.dbgbvcr_el1[ 3]);
+ HW_CPU_SET_DBGBVR2_EL1(dr.dbgbvcr_el1[ 4]);
+ HW_CPU_SET_DBGBCR2_EL1(dr.dbgbvcr_el1[ 5]);
+ HW_CPU_SET_DBGBVR3_EL1(dr.dbgbvcr_el1[ 6]);
+ HW_CPU_SET_DBGBCR3_EL1(dr.dbgbvcr_el1[ 7]);
+ HW_CPU_SET_DBGBVR4_EL1(dr.dbgbvcr_el1[ 8]);
+ HW_CPU_SET_DBGBCR4_EL1(dr.dbgbvcr_el1[ 9]);
+ HW_CPU_SET_DBGBVR5_EL1(dr.dbgbvcr_el1[10]);
+ HW_CPU_SET_DBGBCR5_EL1(dr.dbgbvcr_el1[11]);
+
+ /* Restore debug watchpoints. */
+ HW_CPU_SET_DBGWVR0_EL1(dr.dbgwvcr_el1[0]);
+ HW_CPU_SET_DBGWCR0_EL1(dr.dbgwvcr_el1[1]);
+ HW_CPU_SET_DBGWVR1_EL1(dr.dbgwvcr_el1[2]);
+ HW_CPU_SET_DBGWCR1_EL1(dr.dbgwvcr_el1[3]);
+ HW_CPU_SET_DBGWVR2_EL1(dr.dbgwvcr_el1[4]);
+ HW_CPU_SET_DBGWCR2_EL1(dr.dbgwvcr_el1[5]);
+ HW_CPU_SET_DBGWVR3_EL1(dr.dbgwvcr_el1[6]);
+ HW_CPU_SET_DBGWCR3_EL1(dr.dbgwvcr_el1[7]);
+ }
+
+ constinit CoreContext g_core_contexts[NumCores] = {};
+
+ }
+
+ bool IsCoreOn(int core) {
+ return g_core_contexts[core].is_on;
+ }
+
+ void SetCoreOff() {
+ g_core_contexts[hw::GetCurrentCoreId()].is_on = false;
+ }
+
+ bool IsResetExpected() {
+ return g_core_contexts[hw::GetCurrentCoreId()].is_reset_expected;
+ }
+
+ void SetResetExpected(int core, bool expected) {
+ g_core_contexts[core].is_reset_expected = expected;
+ }
+
+ void SetResetExpected(bool expected) {
+ SetResetExpected(hw::GetCurrentCoreId(), expected);
+ }
+
+ void SetEntryContext(int core, uintptr_t address, uintptr_t arg) {
+ g_core_contexts[core].entry_context.pc = address;
+ g_core_contexts[core].entry_context.x0 = arg;
+ }
+
+ void GetEntryContext(EntryContext *out) {
+ auto &ctx = g_core_contexts[hw::GetCurrentCoreId()];
+
+ const auto pc = ctx.entry_context.pc;
+ const auto x0 = ctx.entry_context.x0;
+
+ if (pc == 0 || ctx.is_on) {
+ SetError(pkg1::ErrorInfo_InvalidCoreContext);
+ AMS_ABORT("Invalid core context");
+ }
+
+ ctx.entry_context = {};
+ ctx.is_on = true;
+
+ out->pc = pc;
+ out->x0 = x0;
+ }
+
+ void SaveDebugRegisters() {
+ auto &ctx = g_core_contexts[hw::GetCurrentCoreId()];
+
+ SaveDebugRegisters(ctx.debug_registers);
+ ctx.is_debug_registers_saved = true;
+ }
+
+ void RestoreDebugRegisters() {
+ auto &ctx = g_core_contexts[hw::GetCurrentCoreId()];
+
+ if (ctx.is_debug_registers_saved) {
+ RestoreDebugRegisters(ctx.debug_registers);
+ ctx.is_debug_registers_saved = false;
+ }
+ }
+
+}
diff --git a/exosphere2/program/source/secmon_cpu_context.hpp b/exosphere2/program/source/secmon_cpu_context.hpp
new file mode 100644
index 000000000..a19bd8af8
--- /dev/null
+++ b/exosphere2/program/source/secmon_cpu_context.hpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::secmon {
+
+ constexpr inline int NumCores = 4;
+
+ struct EntryContext {
+ u64 x0;
+ u64 pc;
+ };
+
+ bool IsCoreOn(int core);
+ void SetCoreOff();
+
+ bool IsResetExpected();
+ void SetResetExpected(int core, bool expected);
+ void SetResetExpected(bool expected);
+
+ void SetEntryContext(int core, uintptr_t address, uintptr_t arg);
+ void GetEntryContext(EntryContext *out);
+
+ void SaveDebugRegisters();
+ void RestoreDebugRegisters();
+
+}
diff --git a/exosphere2/program/source/secmon_error.cpp b/exosphere2/program/source/secmon_error.cpp
new file mode 100644
index 000000000..68338212d
--- /dev/null
+++ b/exosphere2/program/source/secmon_error.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_error.hpp"
+
+namespace ams::diag {
+
+ void AbortImpl() {
+ secmon::SetError(pkg1::ErrorInfo_UnknownAbort);
+ secmon::ErrorReboot();
+ }
+
+}
+
+namespace ams::secmon {
+
+ void SetError(pkg1::ErrorInfo info) {
+ const uintptr_t address = secmon::MemoryRegionVirtualDevicePmc.GetAddress() + PKG1_SECURE_MONITOR_PMC_ERROR_SCRATCH;
+
+ if (reg::Read(address) == pkg1::ErrorInfo_None) {
+ reg::Write(address, info);
+ }
+ }
+
+ NORETURN void ErrorReboot() {
+ /* Lockout the security engine. */
+ se::Lockout();
+
+ /* TODO: Lockout fuses. */
+
+ /* TODO: Disable SE Crypto Operations. */
+
+ while (true) {
+ wdt::Reboot();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_error.hpp b/exosphere2/program/source/secmon_error.hpp
new file mode 100644
index 000000000..abbfad245
--- /dev/null
+++ b/exosphere2/program/source/secmon_error.hpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::secmon {
+
+ void SetError(pkg1::ErrorInfo info);
+ NORETURN void ErrorReboot();
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_exception_vectors.s b/exosphere2/program/source/secmon_exception_vectors.s
new file mode 100644
index 000000000..ef1ab9f32
--- /dev/null
+++ b/exosphere2/program/source/secmon_exception_vectors.s
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/* 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 Secure Monitor. */
+.global _ZN3ams6secmon16ExceptionVectorsEv
+vector_base _ZN3ams6secmon16ExceptionVectorsEv
+
+/* Current EL, SP0 */
+vector_entry synch_sp0
+ /* Branch to the exception handler. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ .endfunc
+ .cfi_endproc
+_ZN3ams6secmon26UnexpectedExceptionHandlerEv:
+ /* Load the ErrorInfo scratch. */
+ ldr x0, =0x1F004AC40
+
+ /* Write ErrorInfo_UnknownAbort to it. */
+ ldr w1, =0x07F00010
+ str w1, [x0]
+
+ /* Perform an error reboot. */
+ b _ZN3ams6secmon11ErrorRebootEv
+
+vector_entry irq_sp0
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ check_vector_size irq_sp0
+
+vector_entry fiq_sp0
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ check_vector_size fiq_sp0
+
+vector_entry serror_sp0
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ check_vector_size serror_sp0
+
+/* Current EL, SPx */
+vector_entry synch_spx
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ check_vector_size synch_spx
+
+vector_entry irq_spx
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ check_vector_size irq_spx
+
+vector_entry fiq_spx
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ check_vector_size fiq_spx
+
+vector_entry serror_spx
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ check_vector_size serror_spx
+
+/* Lower EL, A64 */
+vector_entry synch_a64
+ /* Check whether the exception is an SMC. If it's not, take the unexpected handler. */
+ stp x29, x30, [sp, #-0x10]!
+ mrs x30, esr_el3
+ lsr w29, w30, #26
+ cmp w29, #0x17
+ ldp x29, x30, [sp], #0x10
+ b.ne _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+
+ /* Save x29 and x30. */
+ stp x29, x30, [sp, #-0x10]!
+
+ /* Get the current core. */
+ mrs x29, mpidr_el1
+ and x29, x29, #3
+
+ /* If we're not on core 3, take the core0-2 handler. */
+ cmp x29, #3
+ b.ne _ZN3ams6secmon25HandleSmcExceptionCore012Ev
+
+ /* Handle the smc. */
+ bl _ZN3ams6secmon18HandleSmcExceptionEv
+
+ /* Return. */
+ ldp x29, x30, [sp], #0x10
+ eret
+ check_vector_size synch_a64
+
+vector_entry irq_a64
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ check_vector_size irq_a64
+
+vector_entry fiq_a64
+ /* Save X29, X30. */
+ stp x29, x30, [sp, #-0x10]!
+
+ /* Get the current core ID, ensure it's core 3. */
+ mrs x29, mpidr_el1
+ and x29, x29, #3
+ cmp x29, #3
+ b.ne _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+
+ /* Save x26-x28, x18. */
+ stp x28, x18, [sp, #-0x10]!
+ stp x26, x27, [sp, #-0x10]!
+
+ /* Set x18 to the global data region. */
+ ldr x18, =0x1F01FA000
+
+ /* Handle the fiq exception. */
+ bl _ZN3ams6secmon18HandleFiqExceptionEv
+
+ /* Restore registers. */
+ ldp x26, x27, [sp], #0x10
+ ldp x28, x18, [sp], #0x10
+ ldp x29, x30, [sp], #0x10
+
+ /* Return. */
+ eret
+ check_vector_size fiq_a64
+
+vector_entry serror_a64
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ .endfunc
+ .cfi_endproc
+_ZN3ams6secmon25HandleSmcExceptionCore012Ev:
+ /* Acquire exclusive access to the common smc stack. */
+ stp x4, x5, [sp, #-0x10]!
+ stp x2, x3, [sp, #-0x10]!
+ stp x0, x1, [sp, #-0x10]!
+ bl _ZN3ams6secmon25AcquireCommonSmcStackLockEv
+ ldp x0, x1, [sp], #0x10
+ ldp x2, x3, [sp], #0x10
+ ldp x4, x5, [sp], #0x10
+
+ /* Pivot to use the common smc stack. */
+ mov x30, sp
+ ldr x29, =0x1F01F6E80
+ mov sp, x29
+ stp x29, x30, [sp, #-0x10]!
+
+ /* Handle the SMC. */
+ bl _ZN3ams6secmon18HandleSmcExceptionEv
+
+ /* Restore our core-specific stack. */
+ ldp x29, x30, [sp], #0x10
+ mov x30, sp
+
+ /* Release our exclusive access to the common smc stack. */
+ stp x0, x1, [sp, #-0x10]!
+ bl _ZN3ams6secmon25ReleaseCommonSmcStackLockEv
+ ldp x0, x1, [sp], #0x10
+
+ /* Return. */
+ ldp x29, x30, [sp], #0x10
+ eret
+
+/* Lower EL, A32 */
+vector_entry synch_a32
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ check_vector_size synch_a32
+
+vector_entry irq_a32
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ .endfunc
+ .cfi_endproc
+_ZN3ams6secmon18HandleSmcExceptionEv:
+ /* Save registers. */
+ stp x29, x30, [sp, #-0x10]!
+ stp x18, x19, [sp, #-0x10]!
+ stp x16, x17, [sp, #-0x10]!
+ stp x14, x15, [sp, #-0x10]!
+ stp x12, x13, [sp, #-0x10]!
+ stp x10, x11, [sp, #-0x10]!
+ stp x8, x9, [sp, #-0x10]!
+ stp x6, x7, [sp, #-0x10]!
+ stp x4, x5, [sp, #-0x10]!
+ stp x2, x3, [sp, #-0x10]!
+ stp x0, x1, [sp, #-0x10]!
+
+ /* Set x18 to the global data region. */
+ ldr x18, =0x1F01FA000
+
+ /* Get esr. */
+ mrs x0, esr_el3
+ and x0, x0, #0xFFFF
+
+ /* Get the function arguments. */
+ mov x1, sp
+
+ /* Invoke the smc handler. */
+ bl _ZN3ams6secmon3smc9HandleSmcEiRNS1_12SmcArgumentsE
+
+ /* Restore registers. */
+ ldp x0, x1, [sp], #0x10
+ ldp x2, x3, [sp], #0x10
+ ldp x4, x5, [sp], #0x10
+ ldp x6, x7, [sp], #0x10
+ ldp x8, x9, [sp], #0x10
+ ldp x10, x11, [sp], #0x10
+ ldp x12, x13, [sp], #0x10
+ ldp x14, x15, [sp], #0x10
+ ldp x16, x17, [sp], #0x10
+ ldp x18, x19, [sp], #0x10
+ ldp x29, x30, [sp], #0x10
+
+ ret
+vector_entry fiq_a32
+ /* Handle fiq from a32 the same as fiq from a64. */
+ b fiq_a64
+ .endfunc
+ .cfi_endproc
+_ZN3ams6secmon18HandleFiqExceptionEv:
+ /* Save registers. */
+ stp x29, x30, [sp, #-0x10]!
+ stp x24, x25, [sp, #-0x10]!
+ stp x22, x23, [sp, #-0x10]!
+ stp x20, x21, [sp, #-0x10]!
+ stp x18, x19, [sp, #-0x10]!
+ stp x16, x17, [sp, #-0x10]!
+ stp x14, x15, [sp, #-0x10]!
+ stp x12, x13, [sp, #-0x10]!
+ stp x10, x11, [sp, #-0x10]!
+ stp x8, x9, [sp, #-0x10]!
+ stp x6, x7, [sp, #-0x10]!
+ stp x4, x5, [sp, #-0x10]!
+ stp x2, x3, [sp, #-0x10]!
+ stp x0, x1, [sp, #-0x10]!
+
+ /* Invoke the interrupt handler. */
+ bl _ZN3ams6secmon15HandleInterruptEv
+
+ /* Restore registers. */
+ ldp x0, x1, [sp], #0x10
+ ldp x2, x3, [sp], #0x10
+ ldp x4, x5, [sp], #0x10
+ ldp x6, x7, [sp], #0x10
+ ldp x8, x9, [sp], #0x10
+ ldp x10, x11, [sp], #0x10
+ ldp x12, x13, [sp], #0x10
+ ldp x14, x15, [sp], #0x10
+ ldp x16, x17, [sp], #0x10
+ ldp x18, x19, [sp], #0x10
+ ldp x20, x21, [sp], #0x10
+ ldp x22, x23, [sp], #0x10
+ ldp x24, x25, [sp], #0x10
+ ldp x29, x30, [sp], #0x10
+
+ ret
+
+vector_entry serror_a32
+ /* An unexpected exception was taken. */
+ b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
+ check_vector_size serror_a32
+
+ /* Instantiate the literal pool for the exception vectors. */
+ .ltorg
diff --git a/exosphere2/program/source/secmon_interrupt_handler.cpp b/exosphere2/program/source/secmon_interrupt_handler.cpp
new file mode 100644
index 000000000..41f466632
--- /dev/null
+++ b/exosphere2/program/source/secmon_interrupt_handler.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_interrupt_handler.hpp"
+#include "secmon_error.hpp"
+
+namespace ams::secmon {
+
+ namespace {
+
+ constexpr inline int InterruptHandlersMax = 4;
+
+ constinit InterruptHandler g_handlers[InterruptHandlersMax] = {};
+ constinit int g_interrupt_ids[InterruptHandlersMax] = {};
+
+ }
+
+ void SetInterruptHandler(int interrupt_id, InterruptHandler handler) {
+ for (int i = 0; i < InterruptHandlersMax; ++i) {
+ if (g_interrupt_ids[i] == 0) {
+ g_interrupt_ids[i] = interrupt_id;
+ g_handlers[i] = handler;
+ return;
+ }
+ }
+
+ AMS_ABORT("Failed to register interrupt handler");
+ }
+
+ void HandleInterrupt() {
+ /* Get the interrupt id. */
+ const int interrupt_id = gic::GetInterruptRequestId();
+ if (interrupt_id >= gic::InterruptCount) {
+ /* Invalid interrupt number, just return. */
+ return;
+ }
+
+ /* Check each handler. */
+ for (int i = 0; i < InterruptHandlersMax; ++i) {
+ if (g_interrupt_ids[i] == interrupt_id) {
+ /* Invoke the handler. */
+ g_handlers[i]();
+ gic::SetEndOfInterrupt(interrupt_id);
+ return;
+ }
+ }
+
+ AMS_ABORT("Failed to find interrupt handler.");
+ }
+
+}
diff --git a/exosphere2/program/source/secmon_interrupt_handler.hpp b/exosphere2/program/source/secmon_interrupt_handler.hpp
new file mode 100644
index 000000000..136da0264
--- /dev/null
+++ b/exosphere2/program/source/secmon_interrupt_handler.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::secmon {
+
+ using InterruptHandler = void (*)();
+
+ void SetInterruptHandler(int interrupt_id, InterruptHandler handler);
+
+}
diff --git a/exosphere2/program/source/secmon_key_storage.cpp b/exosphere2/program/source/secmon_key_storage.cpp
new file mode 100644
index 000000000..8e823deec
--- /dev/null
+++ b/exosphere2/program/source/secmon_key_storage.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_key_storage.hpp"
+
+namespace ams::secmon {
+
+ namespace {
+
+ constinit u8 g_rsa_moduli[ImportRsaKey_Count][se::RsaSize] = {};
+ constinit bool g_rsa_modulus_committed[ImportRsaKey_Count] = {};
+
+ ALWAYS_INLINE u8 *GetRsaKeyModulus(ImportRsaKey which) {
+ return g_rsa_moduli[which];
+ }
+
+ ALWAYS_INLINE u8 *GetRsaKeyPrivateExponent(ImportRsaKey which) {
+ return ::ams::secmon::impl::GetRsaPrivateExponentStorage(static_cast(which));
+ }
+
+ ALWAYS_INLINE bool IsRsaKeyProvisional(ImportRsaKey which) {
+ return g_rsa_modulus_committed[which] == false;
+ }
+
+ void ClearRsaKeyModulus(ImportRsaKey which) {
+ g_rsa_modulus_committed[which] = false;
+ std::memset(g_rsa_moduli[which], 0, se::RsaSize);
+ }
+
+ ALWAYS_INLINE u8 *GetMasterKeyStorage(int index) {
+ return ::ams::secmon::impl::GetMasterKeyStorage(index);
+ }
+
+ ALWAYS_INLINE u8 *GetDeviceMasterKeyStorage(int index) {
+ return ::ams::secmon::impl::GetDeviceMasterKeyStorage(index);
+ }
+
+ }
+
+ void ImportRsaKeyExponent(ImportRsaKey which, const void *src, size_t size) {
+ /* If we import an exponent, the modulus is not committed. */
+ ClearRsaKeyModulus(which);
+
+ /* Copy the exponent. */
+ std::memcpy(GetRsaKeyPrivateExponent(which), src, size);
+ }
+
+ void ImportRsaKeyModulusProvisionally(ImportRsaKey which, const void *src, size_t size) {
+ std::memcpy(GetRsaKeyModulus(which), src, std::min(static_cast(size), se::RsaSize));
+ }
+
+ void CommitRsaKeyModulus(ImportRsaKey which) {
+ g_rsa_modulus_committed[which] = true;
+ }
+
+ bool LoadRsaKey(int slot, ImportRsaKey which) {
+ /* If the key is still provisional, we can't load it. */
+ if (IsRsaKeyProvisional(which)) {
+ return false;
+ }
+
+ se::SetRsaKey(slot, GetRsaKeyModulus(which), se::RsaSize, GetRsaKeyPrivateExponent(which), se::RsaSize);
+ return true;
+ }
+
+ void LoadProvisionalRsaKey(int slot, ImportRsaKey which) {
+ se::SetRsaKey(slot, GetRsaKeyModulus(which), se::RsaSize, GetRsaKeyPrivateExponent(which), se::RsaSize);
+ }
+
+ void SetMasterKey(int generation, const void *src, size_t size) {
+ const int index = generation - pkg1::KeyGeneration_Min;
+ se::EncryptAes128(GetMasterKeyStorage(index), se::AesBlockSize, pkg1::AesKeySlot_RandomForKeyStorageWrap, src, size);
+ }
+
+ void LoadMasterKey(int slot, int generation) {
+ const int index = std::min(0, generation - pkg1::KeyGeneration_Min);
+ se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_RandomForKeyStorageWrap, GetMasterKeyStorage(index), se::AesBlockSize);
+ }
+
+ void SetDeviceMasterKey(int generation, const void *src, size_t size) {
+ const int index = generation - pkg1::KeyGeneration_4_0_0;
+ se::EncryptAes128(GetDeviceMasterKeyStorage(index), se::AesBlockSize, pkg1::AesKeySlot_RandomForKeyStorageWrap, src, size);
+ }
+
+ void LoadDeviceMasterKey(int slot, int generation) {
+ const int index = std::min(0, generation - pkg1::KeyGeneration_4_0_0);
+ se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_RandomForKeyStorageWrap, GetDeviceMasterKeyStorage(index), se::AesBlockSize);
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_key_storage.hpp b/exosphere2/program/source/secmon_key_storage.hpp
new file mode 100644
index 000000000..39d34c9da
--- /dev/null
+++ b/exosphere2/program/source/secmon_key_storage.hpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::secmon {
+
+ enum ImportRsaKey {
+ ImportRsaKey_EsDrmCert = 0,
+ ImportRsaKey_Lotus = 1,
+ ImportRsaKey_Ssl = 2,
+ ImportRsaKey_EsClientCert = 3,
+
+ ImportRsaKey_Count = 4,
+ };
+ static_assert(util::size(secmon::ConfigurationContext{}.rsa_private_exponents) == ImportRsaKey_Count);
+
+ void ImportRsaKeyExponent(ImportRsaKey which, const void *src, size_t size);
+ void ImportRsaKeyModulusProvisionally(ImportRsaKey which, const void *src, size_t size);
+ void CommitRsaKeyModulus(ImportRsaKey which);
+
+ bool LoadRsaKey(int slot, ImportRsaKey which);
+ void LoadProvisionalRsaKey(int slot, ImportRsaKey which);
+
+ void SetMasterKey(int generation, const void *src, size_t size);
+ void LoadMasterKey(int slot, int generation);
+
+ void SetDeviceMasterKey(int generation, const void *src, size_t size);
+ void LoadDeviceMasterKey(int slot, int generation);
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_misc.cpp b/exosphere2/program/source/secmon_misc.cpp
new file mode 100644
index 000000000..8f7a1c500
--- /dev/null
+++ b/exosphere2/program/source/secmon_misc.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_misc.hpp"
+
+namespace ams::secmon {
+
+ namespace {
+
+ pkg1::BctParameters g_bct_params;
+
+ }
+
+ void SaveBootInfo(const pkg1::SecureMonitorParameters &secmon_params) {
+ /* Save the BCT parameters. */
+ g_bct_params = secmon_params.bct_params;
+ }
+
+ bool IsRecoveryBoot() {
+ return (g_bct_params.bootloader_attributes & pkg1::BootloaderAttribute_RecoveryBoot) != 0;
+ }
+
+ u32 GetRestrictedSmcMask() {
+ return (g_bct_params.bootloader_attributes & pkg1::BootloaderAttribute_RestrictedSmcMask) >> pkg1::BootloaderAttribute_RestrictedSmcShift;
+ }
+
+ bool IsJtagEnabled() {
+ util::BitPack32 dbg_auth;
+ HW_CPU_GET_DBGAUTHSTATUS_EL1(dbg_auth);
+ return dbg_auth.Get() == 3;
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_misc.hpp b/exosphere2/program/source/secmon_misc.hpp
new file mode 100644
index 000000000..b26641919
--- /dev/null
+++ b/exosphere2/program/source/secmon_misc.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::secmon {
+
+ void SaveBootInfo(const pkg1::SecureMonitorParameters &secmon_params);
+
+ bool IsRecoveryBoot();
+
+ u32 GetRestrictedSmcMask();
+
+ bool IsJtagEnabled();
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp
new file mode 100644
index 000000000..a468d6dce
--- /dev/null
+++ b/exosphere2/program/source/secmon_setup.cpp
@@ -0,0 +1,750 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_setup.hpp"
+#include "secmon_error.hpp"
+#include "secmon_cpu_context.hpp"
+#include "secmon_interrupt_handler.hpp"
+
+namespace ams::secmon {
+
+ namespace {
+
+ constexpr inline const uintptr_t TIMER = secmon::MemoryRegionVirtualDeviceTimer.GetAddress();
+ constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionVirtualDeviceApbMisc.GetAddress();
+ constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionVirtualDeviceFlowController.GetAddress();
+ constexpr inline const uintptr_t PMC = secmon::MemoryRegionVirtualDevicePmc.GetAddress();
+ constexpr inline const uintptr_t MC = secmon::MemoryRegionVirtualDeviceMemoryController.GetAddress();
+
+ alignas(8) constinit u8 g_se_aes_key_slot_test_vector[se::AesBlockSize] = {};
+
+ struct Carveout {
+ uintptr_t address;
+ size_t size;
+ };
+
+ constinit Carveout g_kernel_carveouts[KernelCarveoutCount] = {
+ { secmon::MemoryRegionDramDefaultKernelCarveout.GetAddress(), secmon::MemoryRegionDramDefaultKernelCarveout.GetSize(), },
+ { 0, 0, },
+ };
+
+ constinit bool g_is_cold_boot = true;
+
+ constinit const se::StickyBits ExpectedSeStickyBits = {
+ .se_security = (1 << 0), /* SE_HARD_SETTING */
+ .tzram_security = 0,
+ .crypto_security_perkey = 0,
+ .crypto_keytable_access = {
+ (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 0: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
+ (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 1: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
+ (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 2: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
+ (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 3: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
+ (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 4: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
+ (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 5: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */
+ (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 6: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */
+ (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 7: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */
+ (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 8: Temp keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */
+ (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 9: SmcTemp keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */
+ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 10: Wrap1 keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
+ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 11: Wrap2 keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
+ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 12: DMaster keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
+ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Master keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
+ (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 14: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */
+ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Device keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
+ },
+ .rsa_security_perkey = 0,
+ .rsa_keytable_access = {
+ (0 << 2) | (1 << 1) | (0 << 0), /* KEYUSE/KEYREAD disabled, KEYUPDATE enabled. */
+ (0 << 2) | (1 << 1) | (0 << 0), /* KEYUSE/KEYREAD disabled, KEYUPDATE enabled. */
+ },
+ };
+
+ void InitializeConfigurationContext() {
+ /* Get the global context. */
+ auto &ctx = ::ams::secmon::impl::GetConfigurationContext();
+
+ /* Clear the context to zero. */
+ std::memset(std::addressof(ctx), 0, sizeof(ctx));
+
+ /* If the storage context is valid, we want to copy it to the global context. */
+ if (const auto &storage_ctx = *MemoryRegionPhysicalDramMonitorConfiguration.GetPointer(); storage_ctx.IsValid()) {
+ ctx.secmon_cfg.CopyFrom(storage_ctx);
+ ctx.emummc_cfg = storage_ctx.emummc_cfg;
+ } else {
+ /* If we don't have a valid storage context, we can just use the default one. */
+ ctx.secmon_cfg = DefaultSecureMonitorConfiguration;
+ }
+ }
+
+ void GenerateSecurityEngineAesKeySlotTestVector(void *dst, size_t size) {
+ /* Clear the output. */
+ AMS_ABORT_UNLESS(size == se::AesBlockSize);
+ std::memset(dst, 0, se::AesBlockSize);
+
+ /* Ensure output is seen as cleared by the se. */
+ hw::FlushDataCache(dst, se::AesBlockSize);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Declare a block. */
+ alignas(8) u8 empty_block[se::AesBlockSize];
+
+ /* Iteratively transform an empty block. */
+ #define TRANSFORM_WITH_KEY(key) \
+ __builtin_memset(empty_block, 0, sizeof(empty_block)); \
+ se::SetEncryptedAesKey256(pkg1::AesKeySlot_Temporary, key, empty_block, sizeof(empty_block)); \
+ se::DecryptAes128(dst, se::AesBlockSize, pkg1::AesKeySlot_Temporary, dst, se::AesBlockSize)
+
+ TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForUserWrap);
+ TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForKeyStorageWrap);
+ TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Master);
+ TRANSFORM_WITH_KEY(pkg1::AesKeySlot_DeviceMaster);
+ TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Device);
+
+ TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForUserWrap);
+ TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForKeyStorageWrap);
+ TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Master);
+ TRANSFORM_WITH_KEY(pkg1::AesKeySlot_DeviceMaster);
+ TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Device);
+
+ /* Ensure output is seen correctly by the cpu. */
+ hw::FlushDataCache(dst, se::AesBlockSize);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Clear the temporary key slot. */
+ se::ClearAesKeySlot(pkg1::AesKeySlot_Temporary);
+ }
+
+ void VerifySecurityEngineStickyBits() {
+ if (!se::ValidateStickyBits(ExpectedSeStickyBits)) {
+ SetError(pkg1::ErrorInfo_InvalidSecurityEngineStickyBits);
+ AMS_ABORT("Invalid sticky bits");
+ }
+ }
+
+ void VerifySecurityEngineAesKeySlotTestVector() {
+ alignas(8) u8 test_vector[se::AesBlockSize];
+ GenerateSecurityEngineAesKeySlotTestVector(test_vector, sizeof(test_vector));
+
+ AMS_ABORT_UNLESS(crypto::IsSameBytes(g_se_aes_key_slot_test_vector, test_vector, se::AesBlockSize));
+ }
+
+ void ClearAesKeySlots() {
+ /* Clear all non-secure monitor keys. */
+ for (int i = 0; i < pkg1::AesKeySlot_SecmonStart; ++i) {
+ se::ClearAesKeySlot(i);
+ }
+
+ /* Clear the secure-monitor temporary keys. */
+ se::ClearAesKeySlot(pkg1::AesKeySlot_Temporary);
+ se::ClearAesKeySlot(pkg1::AesKeySlot_Smc);
+ }
+
+ void ClearRsaKeySlots() {
+ /* Clear all rsa keyslots. */
+ for (int i = 0; i < se::RsaKeySlotCount; ++i) {
+ se::ClearRsaKeySlot(i);
+ }
+ }
+
+ void SetupKernelCarveouts() {
+ #define MC_ENABLE_CLIENT_ACCESS(INDEX, WHICH) MC_REG_BITS_ENUM(CLIENT_ACCESS##INDEX##_##WHICH, ENABLE)
+
+ constexpr u32 ClientAccess0 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(0, PTCR),
+ MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0A),
+ MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0AB),
+ MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0B),
+ MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0BB),
+ MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0C),
+ MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0CB),
+ MC_ENABLE_CLIENT_ACCESS(0, AFIR),
+ MC_ENABLE_CLIENT_ACCESS(0, DISPLAYHC),
+ MC_ENABLE_CLIENT_ACCESS(0, DISPLAYHCB),
+ MC_ENABLE_CLIENT_ACCESS(0, HDAR),
+ MC_ENABLE_CLIENT_ACCESS(0, HOST1XDMAR),
+ MC_ENABLE_CLIENT_ACCESS(0, HOST1XR),
+ MC_ENABLE_CLIENT_ACCESS(0, NVENCSRD),
+ MC_ENABLE_CLIENT_ACCESS(0, PPCSAHBDMAR),
+ MC_ENABLE_CLIENT_ACCESS(0, PPCSAHBSLVR));
+
+ constexpr u32 ClientAccess1 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(1, MPCORER),
+ MC_ENABLE_CLIENT_ACCESS(1, NVENCSWR),
+ MC_ENABLE_CLIENT_ACCESS(1, AFIW),
+ MC_ENABLE_CLIENT_ACCESS(1, HDAW),
+ MC_ENABLE_CLIENT_ACCESS(1, HOST1XW),
+ MC_ENABLE_CLIENT_ACCESS(1, MPCOREW),
+ MC_ENABLE_CLIENT_ACCESS(1, PPCSAHBDMAW),
+ MC_ENABLE_CLIENT_ACCESS(1, PPCSAHBSLVW));
+
+ constexpr u32 ClientAccess2 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTR),
+ MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTW),
+ MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVR),
+ MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVW));
+
+ constexpr u32 ClientAccess2_100 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTR),
+ MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTW),
+ MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVR),
+ MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVW),
+ MC_ENABLE_CLIENT_ACCESS(2, TSECSRD),
+ MC_ENABLE_CLIENT_ACCESS(2, TSECSWR));
+
+ constexpr u32 ClientAccess3 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(3, SDMMCRA),
+ MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAA),
+ MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAB),
+ MC_ENABLE_CLIENT_ACCESS(3, SDMMCWA),
+ MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAA),
+ MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAB),
+ MC_ENABLE_CLIENT_ACCESS(3, VICSRD),
+ MC_ENABLE_CLIENT_ACCESS(3, VICSWR),
+ MC_ENABLE_CLIENT_ACCESS(3, DISPLAYD),
+ MC_ENABLE_CLIENT_ACCESS(3, APER),
+ MC_ENABLE_CLIENT_ACCESS(3, APEW),
+ MC_ENABLE_CLIENT_ACCESS(3, NVJPGSRD),
+ MC_ENABLE_CLIENT_ACCESS(3, NVJPGSWR));
+
+ constexpr u32 ClientAccess3_100 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(3, SDMMCRA),
+ MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAA),
+ MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAB),
+ MC_ENABLE_CLIENT_ACCESS(3, SDMMCWA),
+ MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAA),
+ MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAB),
+ MC_ENABLE_CLIENT_ACCESS(3, VICSRD),
+ MC_ENABLE_CLIENT_ACCESS(3, VICSWR),
+ MC_ENABLE_CLIENT_ACCESS(3, DISPLAYD),
+ MC_ENABLE_CLIENT_ACCESS(3, NVDECSRD),
+ MC_ENABLE_CLIENT_ACCESS(3, NVDECSWR),
+ MC_ENABLE_CLIENT_ACCESS(3, APER),
+ MC_ENABLE_CLIENT_ACCESS(3, APEW),
+ MC_ENABLE_CLIENT_ACCESS(3, NVJPGSRD),
+ MC_ENABLE_CLIENT_ACCESS(3, NVJPGSWR));
+
+ constexpr u32 ClientAccess4 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(4, SESRD),
+ MC_ENABLE_CLIENT_ACCESS(4, SESWR));
+
+ constexpr u32 ClientAccess4_800 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(4, SESRD),
+ MC_ENABLE_CLIENT_ACCESS(4, SESWR),
+ MC_ENABLE_CLIENT_ACCESS(4, TSECRDB),
+ MC_ENABLE_CLIENT_ACCESS(4, TSECWRB));
+
+
+ constexpr u32 ClientAccess4_100 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(4, SESRD),
+ MC_ENABLE_CLIENT_ACCESS(4, SESWR));
+
+ #undef MC_ENABLE_CLIENT_ACCESS
+
+ constexpr u32 ForceInternalAccess0 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS0_AVPCARM7R, ENABLE));
+ constexpr u32 ForceInternalAccess0_100 = 0;
+
+ constexpr u32 ForceInternalAccess1 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS1_AVPCARM7W, ENABLE));
+ constexpr u32 ForceInternalAccess1_100 = 0;
+
+ constexpr u32 CarveoutConfig = reg::Encode(MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS),
+ MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, ANY_ADDRESS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, TZ_SECURE));
+
+ constexpr u32 CarveoutConfig_100 = reg::Encode(MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS),
+ MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, ANY_ADDRESS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, TZ_SECURE));
+
+ const u32 target_fw = GetTargetFirmware();
+ u32 client_access_2;
+ u32 client_access_3;
+ u32 client_access_4;
+ u32 carveout_config;
+ if (target_fw >= TargetFirmware_8_1_0) {
+ client_access_2 = ClientAccess2;
+ client_access_3 = ClientAccess3;
+ client_access_4 = ClientAccess4;
+ carveout_config = CarveoutConfig;
+ } else if (target_fw >= TargetFirmware_8_0_0) {
+ client_access_2 = ClientAccess2;
+ client_access_3 = ClientAccess3;
+ client_access_4 = ClientAccess4_800;
+ carveout_config = CarveoutConfig;
+ } else {
+ client_access_2 = ClientAccess2_100;
+ client_access_3 = ClientAccess3_100;
+ client_access_4 = ClientAccess4_100;
+ carveout_config = CarveoutConfig_100;
+ }
+
+ /* Configure carveout 4. */
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM, g_kernel_carveouts[0].address >> 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM_HI, g_kernel_carveouts[0].address >> 32);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_SIZE_128KB, g_kernel_carveouts[0].size / 128_KB);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0, ClientAccess0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1, ClientAccess1);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2, client_access_2);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3, client_access_3);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4, client_access_4);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0, (target_fw >= TargetFirmware_4_0_0) ? ForceInternalAccess0 : ForceInternalAccess0_100);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1, (target_fw >= TargetFirmware_4_0_0) ? ForceInternalAccess1 : ForceInternalAccess1_100);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT4_CFG0, carveout_config);
+
+ /* Configure carveout 5. */
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_BOM, g_kernel_carveouts[0].address >> 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_BOM_HI, g_kernel_carveouts[0].address >> 32);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_SIZE_128KB, g_kernel_carveouts[0].size / 128_KB);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0, ClientAccess0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1, ClientAccess1);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2, client_access_2);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3, client_access_3);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4, client_access_4);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT5_CFG0, carveout_config);
+ }
+
+ void ConfigureSlaveSecurity() {
+ u32 reg0, reg1, reg2;
+ if (GetTargetFirmware() > TargetFirmware_1_0_0) {
+ reg0 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(0, SATA_AUX, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(0, DTV, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(0, QSPI, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(0, SE, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(0, SATA, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(0, LA, ENABLE));
+
+ reg1 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, SPI1, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, SPI2, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, SPI3, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, SPI5, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, SPI6, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, I2C6, ENABLE));
+
+ reg2 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, SDMMC3, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(2, DDS, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(2, DP2, ENABLE));
+
+ const auto hw_type = fuse::GetHardwareType();
+
+ /* Switch Lite can't use HDMI, so set CEC to secure on hoag. */
+ if (hw_type == fuse::HardwareType_Hoag) {
+ reg0 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(0, CEC, ENABLE));
+ }
+
+ /* Icosa, Iowa, and Five all set I2C4 to be secure. */
+ if (hw_type == fuse::HardwareType_Icosa && hw_type == fuse::HardwareType_Iowa && hw_type == fuse::HardwareType_Five) {
+ reg1 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, I2C4, ENABLE));
+
+ }
+
+ /* Hoag additionally sets UART_B to secure. */
+ if (hw_type == fuse::HardwareType_Hoag) {
+ reg1 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, UART_B, ENABLE));
+ }
+
+ /* Copper and Calcio lack a lot of hardware, so set the corresponding registers to secure for them. */
+ if (hw_type == fuse::HardwareType_Calcio || hw_type == fuse::HardwareType_Copper) {
+ reg1 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, UART_B, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, UART_C, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, SPI4, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, I2C2, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, I2C3, ENABLE));
+
+ /* Copper/Calcio have no gamecard reader, and thus set SDMMC2 as secure. */
+ reg2 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, SDMMC2, ENABLE));
+ }
+
+ /* Mariko hardware types (not Icosa or Copper) additionally set mariko-only mmio (SE2, PKA1, FEK) as secure. */
+ if (hw_type != fuse::HardwareType_Icosa && hw_type != fuse::HardwareType_Copper) {
+ reg2 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, SE2, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(2, PKA1, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(2, FEK, ENABLE));
+ }
+ } else {
+ reg0 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(0, SATA_AUX, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(0, DTV, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(0, QSPI, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(0, SATA, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(0, LA, ENABLE));
+
+ reg1 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, SPI1, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, SPI2, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, SPI3, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, SPI5, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, SPI6, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, I2C6, ENABLE));
+
+ reg2 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, DDS, ENABLE),
+ REG_BITS_VALUE(5, 1, 1), /* Note: Bit 5 is not documented in TRM. */
+ REG_BITS_VALUE(4, 1, 1)); /* Note: Bit 4 is not documented in TRM. */
+ }
+
+ reg::Write(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, reg0);
+ reg::Write(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0, reg1);
+ reg::Write(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0, reg2);
+ }
+
+ void SetupSecureRegisters() {
+ /* Configure timers 5-8 and watchdog timers 0-3 as secure. */
+ reg::Write(TIMER + TIMER_SHARED_TIMER_SECURE_CFG, TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR5, ENABLE),
+ TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR6, ENABLE),
+ TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR7, ENABLE),
+ TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR8, ENABLE),
+ TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT0, ENABLE),
+ TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT1, ENABLE),
+ TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT2, ENABLE),
+ TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT3, ENABLE));
+
+ /* Lock cluster switching, to prevent usage of the A53 cores. */
+ reg::Write(FLOW_CTLR + FLOW_CTLR_BPMP_CLUSTER_CONTROL, FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER_LOCK, ENABLE),
+ FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_CLUSTER_SWITCH_ENABLE, DISABLE),
+ FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, FAST));
+
+ /* Enable flow controller debug qualifier for legacy FIQs. */
+ reg::Write(FLOW_CTLR + FLOW_CTLR_FLOW_DBG_QUAL, FLOW_REG_BITS_ENUM(FLOW_DBG_QUAL_FIQ2CCPLEX_ENABLE, ENABLE));
+
+ /* Configure the PMC to disable deep power-down. */
+ reg::Write(PMC + APBDEV_PMC_DPD_ENABLE, PMC_REG_BITS_ENUM(DPD_ENABLE_TSC_MULT_EN, DISABLE),
+ PMC_REG_BITS_ENUM(DPD_ENABLE_ON, DISABLE));
+
+ /* Configure the video protect region. */
+ reg::Write(MC + MC_VIDEO_PROTECT_GPU_OVERRIDE_0, 1);
+ reg::Write(MC + MC_VIDEO_PROTECT_GPU_OVERRIDE_1, 0);
+ reg::Write(MC + MC_VIDEO_PROTECT_BOM, 0);
+ reg::Write(MC + MC_VIDEO_PROTECT_SIZE_MB, 0);
+ reg::Write(MC + MC_VIDEO_PROTECT_REG_CTRL, MC_REG_BITS_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_ALLOW_TZ_WRITE, DISABLED),
+ MC_REG_BITS_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_WRITE_ACCESS, DISABLED));
+
+ /* Configure the SEC carveout. */
+ reg::Write(MC + MC_SEC_CARVEOUT_BOM, 0);
+ reg::Write(MC + MC_SEC_CARVEOUT_SIZE_MB, 0);
+ reg::Write(MC + MC_SEC_CARVEOUT_REG_CTRL, MC_REG_BITS_ENUM(SEC_CARVEOUT_REG_CTRL_SEC_CARVEOUT_WRITE_ACCESS, DISABLED));
+
+ /* Configure the MTS carveout. */
+ reg::Write(MC + MC_MTS_CARVEOUT_BOM, 0);
+ reg::Write(MC + MC_MTS_CARVEOUT_SIZE_MB, 0);
+ reg::Write(MC + MC_MTS_CARVEOUT_ADR_HI, 0);
+ reg::Write(MC + MC_MTS_CARVEOUT_REG_CTRL, MC_REG_BITS_ENUM(MTS_CARVEOUT_REG_CTRL_MTS_CARVEOUT_WRITE_ACCESS, DISABLED));
+
+ /* Configure the security carveout. */
+ reg::Write(MC + MC_SECURITY_CFG0, MC_REG_BITS_VALUE(SECURITY_CFG0_SECURITY_BOM, 0));
+ reg::Write(MC + MC_SECURITY_CFG1, MC_REG_BITS_VALUE(SECURITY_CFG1_SECURITY_SIZE, 0));
+ reg::Write(MC + MC_SECURITY_CFG3, MC_REG_BITS_VALUE(SECURITY_CFG3_SECURITY_BOM_HI, 3));
+
+ /* Configure security carveout 1. */
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_BOM, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_BOM_HI, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_SIZE_128KB, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT1_CFG0, MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS),
+ MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, UNTRANSLATED_ONLY),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, LOCKBIT_SECURE));
+
+ /* Security carveout 2 will be configured later by SetupGpuCarveout, after magic values are written to configure gpu/tsec. */
+
+ /* Configure carveout 3. */
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_BOM, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_BOM_HI, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_SIZE_128KB, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2, MC_REG_BITS_ENUM (CLIENT_ACCESS2_GPUSRD, ENABLE),
+ MC_REG_BITS_ENUM (CLIENT_ACCESS2_GPUSWR, ENABLE));
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4, MC_REG_BITS_ENUM (CLIENT_ACCESS4_GPUSRD2, ENABLE),
+ MC_REG_BITS_ENUM (CLIENT_ACCESS4_GPUSWR2, ENABLE));
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT3_CFG0, MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS),
+ MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 3),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, DISABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, UNTRANSLATED_ONLY),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, LOCKBIT_SECURE));
+
+ /* Configure the two kernel carveouts. */
+ SetupKernelCarveouts();
+
+ /* Configure slave register security. */
+ ConfigureSlaveSecurity();
+ }
+
+ void SetupSmmu() {
+ /* Turn on SMMU translation for all devices. */
+ reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_0, ~0u);
+ reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_1, ~0u);
+ reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_2, ~0u);
+ reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_3, ~0u);
+ reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_4, ~0u);
+
+ /* Configure ASIDs 1-3 as secure, and all others as non-secure. */
+ reg::Write(MC + MC_SMMU_ASID_SECURITY, MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_1, SECURE),
+ MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_2, SECURE),
+ MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_3, SECURE));
+
+ reg::Write(MC + MC_SMMU_ASID_SECURITY_1, 0);
+ reg::Write(MC + MC_SMMU_ASID_SECURITY_2, 0);
+ reg::Write(MC + MC_SMMU_ASID_SECURITY_3, 0);
+ reg::Write(MC + MC_SMMU_ASID_SECURITY_4, 0);
+ reg::Write(MC + MC_SMMU_ASID_SECURITY_5, 0);
+ reg::Write(MC + MC_SMMU_ASID_SECURITY_6, 0);
+ reg::Write(MC + MC_SMMU_ASID_SECURITY_7, 0);
+
+ /* Initialize the PTB registers to zero .*/
+ reg::Write(MC + MC_SMMU_PTB_ASID, 0);
+ reg::Write(MC + MC_SMMU_PTB_DATA, 0);
+
+ /* Configure the TLB and PTC, then read TLB_CONFIG to ensure configuration takes. */
+ reg::Write(MC + MC_SMMU_TLB_CONFIG, MC_REG_BITS_ENUM (SMMU_TLB_CONFIG_TLB_HIT_UNDER_MISS, ENABLE),
+ MC_REG_BITS_ENUM (SMMU_TLB_CONFIG_TLB_ROUND_ROBIN_ARBITRATION, ENABLE),
+ MC_REG_BITS_VALUE(SMMU_TLB_CONFIG_TLB_ACTIVE_LINES, 0x30));
+
+ reg::Write(MC + MC_SMMU_PTC_CONFIG, MC_REG_BITS_ENUM (SMMU_PTC_CONFIG_PTC_CACHE_ENABLE, ENABLE),
+ MC_REG_BITS_VALUE(SMMU_PTC_CONFIG_PTC_REQ_LIMIT, 8),
+ MC_REG_BITS_VALUE(SMMU_PTC_CONFIG_PTC_INDEX_MAP, 0x3F));
+
+ reg::Read (MC + MC_SMMU_TLB_CONFIG);
+
+ /* Flush the entire page table cache, and read TLB_CONFIG to ensure the flush takes. */
+ reg::Write(MC + MC_SMMU_PTC_FLUSH, 0);
+ reg::Read (MC + MC_SMMU_TLB_CONFIG);
+
+ /* Flush the entire translation lookaside buffer, and read TLB_CONFIG to ensure the flush takes. */
+ reg::Write(MC + MC_SMMU_PTC_FLUSH, 0);
+ reg::Read (MC + MC_SMMU_TLB_CONFIG);
+
+ /* Enable the SMMU, and read TLB_CONFIG to ensure the enable takes. */
+ reg::Write(MC + MC_SMMU_CONFIG, MC_REG_BITS_ENUM (SMMU_CONFIG_SMMU_ENABLE, ENABLE));
+ reg::Read (MC + MC_SMMU_TLB_CONFIG);
+ }
+
+ }
+
+ void Setup1() {
+ /* Load the global configuration context. */
+ InitializeConfigurationContext();
+
+ /* Initialize uart for logging. */
+ log::Initialize();
+
+ /* Initialize the security engine. */
+ se::Initialize();
+
+ /* Initialize the gic. */
+ gic::InitializeCommon();
+ }
+
+ void SaveSecurityEngineAesKeySlotTestVector() {
+ GenerateSecurityEngineAesKeySlotTestVector(g_se_aes_key_slot_test_vector, sizeof(g_se_aes_key_slot_test_vector));
+ }
+
+ void SetupSocSecurity() {
+ /* Set the fuse visibility. */
+ clkrst::SetFuseVisibility(true);
+
+ /* Set fuses as only secure-writable. */
+ fuse::SetWriteSecureOnly();
+
+ /* Lockout the fuses. */
+ fuse::Lockout();
+
+ /* Set the security engine to secure mode. */
+ se::SetSecure(true);
+
+ /* Verify the security engine's sticky bits. */
+ VerifySecurityEngineStickyBits();
+
+ /* Verify the security engine's Aes slots contain correct contents. */
+ VerifySecurityEngineAesKeySlotTestVector();
+
+ /* Clear aes keyslots. */
+ ClearAesKeySlots();
+
+ /* Clear rsa keyslots. */
+ ClearRsaKeySlots();
+
+ /* Overwrite keys that we want to be random with random contents. */
+ se::InitializeRandom();
+ se::SetRandomKey(pkg1::AesKeySlot_Temporary);
+ se::GenerateSrk();
+ se::SetRandomKey(pkg1::AesKeySlot_TzramSave);
+
+ /* Initialize pmc secure scratch. */
+ pmc::InitializeRandomScratch();
+
+ /* Setup secure registers. */
+ SetupSecureRegisters();
+
+ /* Setup the smmu. */
+ SetupSmmu();
+
+ /* Clear the cpu reset vector. */
+ reg::Write(secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress() + EVP_CPU_RESET_VECTOR, 0);
+
+ /* Configure the SB registers to our start address. */
+ constexpr u32 ResetVectorLow = static_cast((PhysicalTzramProgramResetVector >> 0));
+ constexpr u32 ResetVectorHigh = static_cast((PhysicalTzramProgramResetVector >> BITSIZEOF(u32)));
+
+ /* Write our reset vector to the secure boot registers. */
+ reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_AA64_RESET_LOW, ResetVectorLow);
+ reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_AA64_RESET_HIGH, ResetVectorHigh);
+
+ /* Disable non-secure writes to the reset vector. */
+ reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_CSR, SB_REG_BITS_ENUM(CSR_NS_RST_VEC_WR_DIS, DISABLE));
+
+ /* Read back SB_CSR to make sure our non-secure write disable takes. */
+ reg::Read(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_CSR);
+
+ /* Write our reset vector to scratch registers used by warmboot, and lock those scratch registers. */
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH34, ResetVectorLow);
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH35, ResetVectorHigh);
+ pmc::LockSecureRegister(pmc::SecureRegister_ResetVector);
+
+ /* Setup the security engine interrupt. */
+ constexpr int SecurityEngineInterruptId = 90;
+ gic::SetPriority (SecurityEngineInterruptId, gic::HighestPriority);
+ gic::SetInterruptGroup(SecurityEngineInterruptId, 0);
+ gic::SetEnable (SecurityEngineInterruptId, true);
+ gic::SetSpiTargetCpu (SecurityEngineInterruptId, (1 << 3));
+ gic::SetSpiMode (SecurityEngineInterruptId, gic::InterruptMode_Level);
+
+ /* Setup the activity monitor interrupt. */
+ constexpr int ActivityMonitorInterruptId = 77;
+ gic::SetPriority (ActivityMonitorInterruptId, gic::HighestPriority);
+ gic::SetInterruptGroup(ActivityMonitorInterruptId, 0);
+ gic::SetEnable (ActivityMonitorInterruptId, true);
+ gic::SetSpiTargetCpu (ActivityMonitorInterruptId, (1 << 3));
+ gic::SetSpiMode (ActivityMonitorInterruptId, gic::InterruptMode_Level);
+
+ /* If we're coldboot, perform one-time setup. */
+ if (g_is_cold_boot) {
+ /* Register both interrupt handlers. */
+ SetInterruptHandler(SecurityEngineInterruptId, se::HandleInterrupt);
+ SetInterruptHandler(ActivityMonitorInterruptId, actmon::HandleInterrupt);
+
+ /* We're expecting the other cores to come out of reset. */
+ for (int i = 1; i < NumCores; ++i) {
+ SetResetExpected(i, true);
+ }
+
+ /* We only coldboot once. */
+ g_is_cold_boot = false;
+ }
+ }
+
+ void SetupSocProtections() {
+
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_setup.hpp b/exosphere2/program/source/secmon_setup.hpp
new file mode 100644
index 000000000..4113c7905
--- /dev/null
+++ b/exosphere2/program/source/secmon_setup.hpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::secmon {
+
+ constexpr inline u64 MemoryAttributeIndexNormal = 0;
+ constexpr inline u64 MemoryAttributeIndexDevice = 1;
+
+ constexpr inline int KernelCarveoutCount = 2;
+
+ void SetupCpuMemoryControllersEnableMmu();
+
+ void SetupSocDmaControllers();
+ void SetupSocSecurity();
+ void SetupSocProtections();
+
+ void Setup1();
+
+ void SaveSecurityEngineAesKeySlotTestVector();
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_setup_warm.cpp b/exosphere2/program/source/secmon_setup_warm.cpp
new file mode 100644
index 000000000..1f168d89a
--- /dev/null
+++ b/exosphere2/program/source/secmon_setup_warm.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_setup.hpp"
+
+namespace ams::secmon {
+
+ namespace setup {
+
+ #include "secmon_cache_impl.inc"
+
+ }
+
+ namespace {
+
+ using namespace ams::mmu;
+
+ void SetupCpuCommonControllers() {
+ /* Set cpuactlr_el1. */
+ {
+ util::BitPack64 cpuactlr = {};
+ cpuactlr.Set(1);
+ cpuactlr.Set(1);
+ HW_CPU_SET_CPUACTLR_EL1(cpuactlr);
+ }
+
+ /* Set cpuectlr_el1. */
+ {
+ util::BitPack64 cpuectlr = {};
+ cpuectlr.Set(1);
+ cpuectlr.Set(3);
+ cpuectlr.Set(3);
+ HW_CPU_SET_CPUECTLR_EL1(cpuectlr);
+ }
+
+ /* Prevent instruction reordering. */
+ hw::InstructionSynchronizationBarrier();
+ }
+
+ void SetupCpuEl3Controllers() {
+ /* Set scr_el3. */
+ {
+ util::BitPack32 scr = {};
+ scr.Set(1); /* Set EL0/EL1 as Non-Secure. */
+ scr.Set(0); /* IRQs are taken in IRQ mode. */
+ scr.Set(1); /* FIQs are taken in Monitor mode. */
+ scr.Set(1); /* External aborts are taken in Monitor mode. */
+ scr.Set(1); /* CPSR.F is non-secure writable. */
+ scr.Set(1); /* CPSR.A is non-secure writable. */
+ scr.Set(0); /* This bit is not implemented. */
+ scr.Set(0); /* Secure Monitor Call is allowed. */
+ scr.Set(0); /* Hypervisor Calls are disabled. */ /* TODO: Enable for thermosphere? */
+ scr.Set(1); /* Secure mode cannot fetch instructions from non-secure memory. */
+ scr.Set(1); /* Reserved bit. N probably sets it because on Cortex A53, this sets kernel as aarch64. */
+ scr.Set(0); /* Reserved bit. On Cortex A53, this sets secure registers to EL3 only. */
+ scr.Set(0); /* WFI is not trapped. */
+ scr.Set(0); /* WFE is not trapped. */
+
+ HW_CPU_SET_SCR_EL3(scr);
+ }
+
+ /* Set ttbr0_el3. */
+ {
+ constexpr u64 ttbr0 = MemoryRegionPhysicalTzramL1PageTable.GetAddress();
+ HW_CPU_SET_TTBR0_EL3(ttbr0);
+ }
+
+ /* Set tcr_el3. */
+ {
+ util::BitPack32 tcr = { hw::TcrEl3::Res1 };
+ tcr.Set(31); /* Configure TTBR0 addressed size to be 64 GiB */
+ tcr.Set(1); /* Configure PTE walks as inner write-back write-allocate cacheable */
+ tcr.Set(1); /* Configure PTE walks as outer write-back write-allocate cacheable */
+ tcr.Set(3); /* Configure PTE walks as inner shareable */
+ tcr.Set(0); /* Set TTBR0_EL3 granule as 4 KiB */
+ tcr.Set(1); /* Set the physical addrss size as 36-bit (64 GiB) */
+ tcr.Set(0); /* Top byte is not ignored in addrss calculations */
+
+ HW_CPU_SET_TCR_EL3(tcr);
+ }
+
+ /* Clear cptr_el3. */
+ {
+ util::BitPack32 cptr = {};
+
+ cptr.Set(0); /* FP/SIMD instructions don't trap. */
+ cptr.Set(0); /* Reserved bit (no trace functionality present). */
+ cptr.Set(0); /* Access to cpacr_El1 does not trap. */
+
+ HW_CPU_SET_CPTR_EL3(cptr);
+ }
+
+ /* Set mair_el3. */
+ {
+ u64 mair = (MemoryRegionAttributes_Normal << (MemoryRegionAttributeWidth * MemoryAttributeIndexNormal)) |
+ (MemoryRegionAttributes_Device << (MemoryRegionAttributeWidth * MemoryAttributeIndexDevice));
+ HW_CPU_SET_MAIR_EL3(mair);
+ }
+
+ /* Set vectors. */
+ {
+ constexpr u64 vectors = MemoryRegionVirtualTzramProgramExceptionVectors.GetAddress();
+ HW_CPU_SET_VBAR_EL3(vectors);
+ }
+
+ /* Prevent instruction re-ordering around this point. */
+ hw::InstructionSynchronizationBarrier();
+ }
+
+ void EnableMmu() {
+ /* Create sctlr value. */
+ util::BitPack32 sctlr = { hw::SctlrEl3::Res1 };
+ sctlr.Set(1); /* Globally enable the MMU. */
+ sctlr.Set(0); /* Disable alignment fault checking. */
+ sctlr.Set(1); /* Globally enable the data and unified caches. */
+ sctlr.Set(0); /* Disable stack alignment checking. */
+ sctlr.Set(1); /* Globally enable the instruction cache. */
+ sctlr.Set(0); /* Do not force writable pages to be ExecuteNever. */
+ sctlr.Set(0); /* Exceptions should be little endian. */
+
+ /* Ensure all writes are done before turning on the mmu. */
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Invalidate the entire tlb. */
+ hw::InvalidateEntireTlb();
+
+ /* Ensure instruction consistency. */
+ hw::DataSynchronizationBarrierInnerShareable();
+ hw::InstructionSynchronizationBarrier();
+
+ /* Set sctlr_el3. */
+ HW_CPU_SET_SCTLR_EL3(sctlr);
+ hw::InstructionSynchronizationBarrier();
+ }
+
+ }
+
+ void SetupCpuMemoryControllersEnableMmu() {
+ SetupCpuCommonControllers();
+ SetupCpuEl3Controllers();
+ EnableMmu();
+ }
+
+ void SetupSocDmaControllers() {
+ /* Ensure that our caches are managed. */
+ setup::InvalidateEntireDataCache();
+ setup::EnsureInstructionConsistency();
+
+ /* Lock tsec. */
+ tsec::Lock();
+
+ /* Enable SWID[0] for all bits. */
+ reg::Write(AHB_ARBC(AHB_MASTER_SWID), ~0u);
+
+ /* Clear SWID1 for all bits. */
+ reg::Write(AHB_ARBC(AHB_MASTER_SWID_1), 0u);
+
+ /* Set MSELECT config to set WRAP_TO_INCR_SLAVE0(APC) | WRAP_TO_INCR_SLAVE1(PCIe) | WRAP_TO_INCR_SLAVE2(GPU) */
+ /* and clear ERR_RESP_EN_SLAVE1(PCIe) | ERR_RESP_EN_SLAVE2(GPU) */
+ {
+ auto mselect_cfg = reg::Read(MSELECT(MSELECT_CONFIG));
+ mselect_cfg &= ~(1u << 24); /* Clear ERR_RESP_EN_SLAVE1(PCIe) */
+ mselect_cfg &= ~(1u << 25); /* Clear ERR_RESP_EN_SLAVE2(GPU) */
+ mselect_cfg |= (1u << 27); /* Set WRAP_TO_INCR_SLAVE0(APC) */
+ mselect_cfg |= (1u << 28); /* Set WRAP_TO_INCR_SLAVE1(PCIe) */
+ mselect_cfg |= (1u << 29); /* Set WRAP_TO_INCR_SLAVE2(GPU) */
+ reg::Write(MSELECT(MSELECT_CONFIG), mselect_cfg);
+ }
+
+ /* Disable USB, USB2, AHB-DMA from arbitration. */
+ {
+ auto arb_dis = reg::Read(AHB_ARBC(AHB_ARBITRATION_DISABLE));
+ arb_dis |= (1u << 5); /* Disable AHB-DMA */
+ arb_dis |= (1u << 6); /* Disable USB */
+ arb_dis |= (1u << 18); /* Disable USB2 */
+ reg::Write(AHB_ARBC(AHB_ARBITRATION_DISABLE), arb_dis);
+ }
+
+ /* Select high priority group with priority 7. */
+ {
+ u32 priority_ctrl = {};
+ priority_ctrl |= (7u << 29); /* Set group 7. */
+ priority_ctrl |= (1u << 0); /* Set high priority. */
+ reg::Write(AHB_ARBC(AHB_ARBITRATION_PRIORITY_CTRL), priority_ctrl);
+ }
+
+ /* Prevent splitting AHB writes to TZRAM. */
+ {
+ reg::Write(AHB_ARBC(AHB_GIZMO_TZRAM), (1u << 7));
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s
new file mode 100644
index 000000000..e71744dad
--- /dev/null
+++ b/exosphere2/program/source/secmon_start_virtual.s
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+.section .text._ZN3ams6secmon5StartEv, "ax", %progbits
+.align 6
+.global _ZN3ams6secmon5StartEv
+_ZN3ams6secmon5StartEv:
+ /* Set SPSEL 1 stack pointer to the core 0 exception stack address. */
+ msr spsel, #1
+ ldr x20, =0x1F01F6F00
+ mov sp, x20
+
+ /* Set SPSEL 0 stack pointer to a temporary location in volatile memory. */
+ msr spsel, #1
+ ldr x20, =0x1F01C0800
+ mov sp, x20
+
+ /* Setup X18 to point to the global context. */
+ ldr x18, =0x1F01FA000
+
+ /* Invoke main. */
+ bl _ZN3ams6secmon4MainEv
+
+ /* Set the stack pointer to the core 3 exception stack address. */
+ ldr x20, =0x1F01FB000
+ mov sp, x20
+
+ /* TODO: JumpToLowerExceptionLevel */
+ 1: b 1b
+
+.section .text._ZN3ams6secmon25AcquireCommonSmcStackLockEv, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon25AcquireCommonSmcStackLockEv
+_ZN3ams6secmon25AcquireCommonSmcStackLockEv:
+ /* Get the address of the lock. */
+ ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE
+
+ /* Prepare to try to take the spinlock. */
+ mov w1, #1
+ sevl
+ prfm pstl1keep, [x0]
+
+1: /* Repeatedly try to take the lock. */
+ wfe
+ ldaxr w2, [x0]
+ cbnz w2, 1b
+ stxr w2, w1, [x0]
+ cbnz w2, 1b
+
+ /* Return. */
+ ret
+
+.section .text._ZN3ams6secmon25ReleaseCommonSmcStackLockEv, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon25ReleaseCommonSmcStackLockEv
+_ZN3ams6secmon25ReleaseCommonSmcStackLockEv:
+ /* Get the address of the lock. */
+ ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE
+
+ /* Release the spinlock. */
+ stlr wzr, [x0]
+
+ /* Return. */
+ ret
+
+.section .data._ZN3ams6secmon18CommonSmcStackLockE, "aw", %progbits
+.global _ZN3ams6secmon18CommonSmcStackLockE
+_ZN3ams6secmon18CommonSmcStackLockE:
+ /* Define storage for the global common smc stack spinlock. */
+ .word 0
diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp
new file mode 100644
index 000000000..9283a278c
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_aes.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcGenerateAesKek(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcLoadAesKey(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcComputeAes(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcGenerateSpecificAesKey(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcComputeCmac(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcLoadPreparedAesKey(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp
new file mode 100644
index 000000000..b0b3c3d95
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_aes.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcGenerateAesKek(const SmcArguments &args);
+ SmcResult SmcLoadAesKey(const SmcArguments &args);
+ SmcResult SmcComputeAes(const SmcArguments &args);
+ SmcResult SmcGenerateSpecificAesKey(const SmcArguments &args);
+ SmcResult SmcComputeCmac(const SmcArguments &args);
+ SmcResult SmcLoadPreparedAesKey(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.cpp b/exosphere2/program/source/smc/secmon_smc_carveout.cpp
new file mode 100644
index 000000000..ba037d498
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_carveout.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_carveout.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcSetKernelCarveoutRegion(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.hpp b/exosphere2/program/source/smc/secmon_smc_carveout.hpp
new file mode 100644
index 000000000..f3d346d97
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_carveout.hpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcSetKernelCarveoutRegion(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_common.hpp b/exosphere2/program/source/smc/secmon_smc_common.hpp
new file mode 100644
index 000000000..dba7c4af5
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_common.hpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::secmon::smc {
+
+ enum class SmcResult : u32 {
+ Success = 0,
+ NotImplemented = 1,
+ InvalidArgument = 2,
+ InProgress = 3,
+ NoAsyncOperation = 4,
+ InvalidAsyncOperation = 5,
+ NotPermitted = 6,
+ NotInitialized = 7,
+
+ PsciSuccess = 0,
+ PsciNotSupported = static_cast(-1),
+ PsciInvalidParameters = static_cast(-2),
+ PsciDenied = static_cast(-3),
+ PsciAlreadyOn = static_cast(-4),
+ };
+
+ struct SmcArguments {
+ u64 r[8];
+ };
+
+ constexpr inline int SecurityEngineUserInterruptId = 44;
+ constexpr inline u64 InvalidAsyncKey = 0;
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp
new file mode 100644
index 000000000..39fb28dcb
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_device_unique_data.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcDecryptDeviceUniqueData(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcReencryptDeviceUniqueData(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ /* Legacy APIs. */
+ SmcResult SmcDecryptAndImportEsDeviceKey(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcDecryptAndImportLotusKey(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp
new file mode 100644
index 000000000..9def74026
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcDecryptDeviceUniqueData(const SmcArguments &args);
+ SmcResult SmcReencryptDeviceUniqueData(const SmcArguments &args);
+
+ /* Legacy APIs. */
+ SmcResult SmcDecryptAndImportEsDeviceKey(const SmcArguments &args);
+ SmcResult SmcDecryptAndImportLotusKey(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_error.cpp b/exosphere2/program/source/smc/secmon_smc_error.cpp
new file mode 100644
index 000000000..309a2e832
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_error.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_error.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcShowError(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_error.hpp b/exosphere2/program/source/smc/secmon_smc_error.hpp
new file mode 100644
index 000000000..62eff45da
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_error.hpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcShowError(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_es.cpp b/exosphere2/program/source/smc/secmon_smc_es.cpp
new file mode 100644
index 000000000..a707c35f0
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_es.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_es.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcPrepareEsDeviceUniqueKey(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcPrepareEsCommonKey(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_es.hpp b/exosphere2/program/source/smc/secmon_smc_es.hpp
new file mode 100644
index 000000000..1aabbc210
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_es.hpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ enum EsKeyType {
+ EsKeyType_TitleKey = 0,
+ EsKeyType_ArchiveKey = 1,
+
+ EsKeyType_Count = 2,
+ };
+
+ SmcResult SmcPrepareEsDeviceUniqueKey(const SmcArguments &args);
+ SmcResult SmcPrepareEsCommonKey(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp
new file mode 100644
index 000000000..75bfa4357
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "../secmon_misc.hpp"
+#include "secmon_smc_common.hpp"
+#include "secmon_smc_handler.hpp"
+#include "secmon_smc_aes.hpp"
+#include "secmon_smc_carveout.hpp"
+#include "secmon_smc_device_unique_data.hpp"
+#include "secmon_smc_error.hpp"
+#include "secmon_smc_es.hpp"
+#include "secmon_smc_info.hpp"
+#include "secmon_smc_memory_access.hpp"
+#include "secmon_smc_power_management.hpp"
+#include "secmon_smc_random.hpp"
+#include "secmon_smc_register_access.hpp"
+#include "secmon_smc_result.hpp"
+#include "secmon_smc_rsa.hpp"
+
+namespace ams::secmon::smc {
+
+ namespace {
+
+ struct HandlerInfo {
+ u32 function_id;
+ u32 restriction_mask;
+ SmcHandler handler;
+ };
+
+ struct HandlerTable {
+ const HandlerInfo *entries;
+ size_t count;
+ };
+
+ enum HandlerType : int {
+ HandlerType_User = 0,
+ HandlerType_Kern = 1,
+ HandlerType_Count = 2,
+ };
+
+ enum Restriction {
+ Restriction_None = (0 << 0),
+ Restriction_Normal = (1 << 0),
+ Restriction_DeviceUniqueDataNotAllowed = (1 << 1),
+ Restriction_SafeModeNotAllowed = (1 << 2),
+ };
+
+ enum SmcCallRange {
+ SmcCallRange_ArmArch = 0,
+ SmcCallRange_Cpu = 1,
+ SmcCallRange_Sip = 2,
+ SmcCallRange_Oem = 3,
+ SmcCallRange_Standard = 4,
+
+ SmcCallRange_TrustedApp = 0x30,
+ };
+
+ enum SmcArgumentType {
+ ArgumentType_Integer = 0,
+ ArgumentType_Pointer = 1,
+ };
+
+ enum SmcConvention {
+ Convention_Smc32 = 0,
+ Convention_Smc64 = 1,
+ };
+
+ enum SmcCallType {
+ SmcCallType_YieldingCall = 0,
+ SmcCallType_FastCall = 1,
+ };
+
+ struct SmcFunctionId {
+ using FunctionId = util::BitPack64::Field< 0, 8, u32>;
+ using ArgumentType0 = util::BitPack64::Field< 8, 1, SmcArgumentType>;
+ using ArgumentType1 = util::BitPack64::Field< 9, 1, SmcArgumentType>;
+ using ArgumentType2 = util::BitPack64::Field<10, 1, SmcArgumentType>;
+ using ArgumentType3 = util::BitPack64::Field<11, 1, SmcArgumentType>;
+ using ArgumentType4 = util::BitPack64::Field<12, 1, SmcArgumentType>;
+ using ArgumentType5 = util::BitPack64::Field<13, 1, SmcArgumentType>;
+ using ArgumentType6 = util::BitPack64::Field<14, 1, SmcArgumentType>;
+ using ArgumentType7 = util::BitPack64::Field<15, 1, SmcArgumentType>;
+ using Reserved = util::BitPack64::Field<16, 8, u32>;
+ using CallRange = util::BitPack64::Field<24, 6, SmcCallRange>;
+ using Convention = util::BitPack64::Field<30, 1, SmcConvention>;
+ using CallType = util::BitPack64::Field<31, 1, SmcCallType>;
+ using Reserved2 = util::BitPack64::Field<32, 32, u32>;
+ };
+
+ constinit HandlerInfo g_user_handlers[] = {
+ { 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
+ { 0xC3000401, Restriction_SafeModeNotAllowed, SmcSetConfig },
+ { 0xC3000002, Restriction_Normal, SmcGetConfigUser },
+ { 0xC3000003, Restriction_Normal, SmcGetResult },
+ { 0xC3000404, Restriction_Normal, SmcGetResultData },
+ { 0xC3000E05, Restriction_SafeModeNotAllowed, SmcModularExponentiate },
+ { 0xC3000006, Restriction_Normal, SmcGenerateRandomBytes },
+ { 0xC3000007, Restriction_Normal, SmcGenerateAesKek },
+ { 0xC3000008, Restriction_Normal, SmcLoadAesKey },
+ { 0xC3000009, Restriction_Normal, SmcComputeAes },
+ { 0xC300000A, Restriction_Normal, SmcGenerateSpecificAesKey },
+ { 0xC300040B, Restriction_Normal, SmcComputeCmac },
+ { 0xC300D60C, Restriction_Normal, SmcReencryptDeviceUniqueData },
+ { 0xC300100D, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptDeviceUniqueData },
+ { 0xC300000E, Restriction_SafeModeNotAllowed, nullptr },
+ { 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey },
+ { 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey },
+ { 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey },
+ { 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonKey }
+ };
+
+ constinit HandlerInfo g_kern_handlers[] = {
+ { 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
+ { 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu },
+ { 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu },
+ { 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu },
+ { 0xC3000002, Restriction_Normal, SmcGetConfigKern },
+ { 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking },
+ { 0xC3000006, Restriction_Normal, SmcShowError },
+ { 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion },
+ { 0xC3000008, Restriction_Normal, SmcReadWriteRegister },
+ };
+
+ constinit HandlerInfo g_ams_handlers[] = {
+ { 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
+ { 0xF0000201, Restriction_None, SmcIramCopy },
+ { 0xF0000002, Restriction_None, SmcReadWriteRegister },
+ { 0xF0000003, Restriction_None, SmcWriteAddress },
+ { 0xF0000003, Restriction_None, SmcGetEmummcConfig },
+ };
+
+ constinit HandlerTable g_handler_tables[] = {
+ { g_user_handlers, util::size(g_user_handlers) },
+ { g_kern_handlers, util::size(g_kern_handlers) },
+ };
+
+ constinit HandlerTable g_ams_handler_table = {
+ g_ams_handlers, util::size(g_ams_handlers)
+ };
+
+ NORETURN void InvalidSmcError(u64 id) {
+ SetError(pkg1::ErrorInfo_UnknownSmc);
+ AMS_ABORT("Invalid SMC: %lx", id);
+ }
+
+ const HandlerTable &GetHandlerTable(HandlerType type, u64 id) {
+ /* Ensure we have a valid handler type. */
+ if (AMS_UNLIKELY(!(0 <= type && type < HandlerType_Count))) {
+ InvalidSmcError(id);
+ }
+
+ /* Check if we're a user SMC. */
+ if (type == HandlerType_User) {
+ /* Nintendo uses OEM SMCs. */
+ /* We will assign Atmosphere extension SMCs the TrustedApplication range. */
+ if (util::BitPack64{id}.Get() == SmcCallRange_TrustedApp) {
+ return g_ams_handler_table;
+ }
+
+ /* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */
+ if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) {
+ InvalidSmcError(id);
+ }
+ }
+
+ return g_handler_tables[type];
+ }
+
+ const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) {
+ /* Get and check the index. */
+ const auto index = util::BitPack64{id}.Get();
+ if (AMS_UNLIKELY(index >= table.count)) {
+ InvalidSmcError(id);
+ }
+
+ /* Get and check the handler info. */
+ const auto &handler_info = table.entries[index];
+
+ /* Check that the handler isn't null. */
+ if (AMS_UNLIKELY(handler_info.handler == nullptr)) {
+ InvalidSmcError(id);
+ }
+
+ /* Check that the handler's id matches. */
+ if (AMS_UNLIKELY(handler_info.function_id != id)) {
+ InvalidSmcError(id);
+ }
+
+ return handler_info;
+ }
+
+ bool IsHandlerRestricted(const HandlerInfo &info) {
+ return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0;
+ }
+
+ SmcResult InvokeSmcHandler(const HandlerInfo &info, const SmcArguments &args) {
+ /* Check if the smc is restricted. */
+ if (AMS_UNLIKELY(IsHandlerRestricted(info))) {
+ return SmcResult::NotPermitted;
+ }
+
+ /* Invoke the smc. */
+ return info.handler(args);
+ }
+
+ }
+
+ void HandleSmc(int type, SmcArguments &args) {
+ /* Get the table. */
+ const auto &table = GetHandlerTable(static_cast(type), args.r[0]);
+
+ /* Get the handler info. */
+ const auto &info = GetHandlerInfo(table, args.r[0]);
+
+ /* Set the invocation result. */
+ args.r[0] = static_cast(InvokeSmcHandler(info, args));
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_handler.hpp b/exosphere2/program/source/smc/secmon_smc_handler.hpp
new file mode 100644
index 000000000..9c5d4b317
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_handler.hpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ using SmcHandler = SmcResult (*)(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp
new file mode 100644
index 000000000..2be199d28
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_info.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_info.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcGetConfigUser(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcGetConfigKern(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcSetConfig(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ /* This is an atmosphere extension smc. */
+ SmcResult SmcGetEmummcConfig(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_info.hpp b/exosphere2/program/source/smc/secmon_smc_info.hpp
new file mode 100644
index 000000000..1d41950ff
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_info.hpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ enum class ConfigItem : u32 {
+ /* Standard config items. */
+ DisableProgramVerification = 1,
+ DramId = 2,
+ SecurityEngineIrqNumber = 3,
+ FuseVersion = 4,
+ HardwareType = 5,
+ IsRetail = 6,
+ IsRecoveryBoot = 7,
+ DeviceId = 8,
+ BootReason = 9,
+ MemoryMode = 10,
+ IsDebugMode = 11,
+ KernelConfiguration = 12,
+ IsChargerHiZModeEnabled = 13,
+ IsQuest = 14,
+ RegulatorType = 15,
+ DeviceUniqueKeyGeneration = 16,
+ Package2Hash = 17,
+
+ /* Extension config items for exosphere. */
+ ExosphereApiVersion = 65000,
+ ExosphereNeedsReboot = 65001,
+ ExosphereNeedsShutdown = 65002,
+ ExosphereGitCommitHash = 65003,
+ ExosphereHasRcmBugPatch = 65004,
+ ExosphereBlankProdInfo = 65005,
+ ExosphereAllowCalWrites = 65006,
+ };
+
+ SmcResult SmcGetConfigUser(const SmcArguments &args);
+ SmcResult SmcGetConfigKern(const SmcArguments &args);
+ SmcResult SmcSetConfig(const SmcArguments &args);
+
+ /* This is an atmosphere extension smc. */
+ SmcResult SmcGetEmummcConfig(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp
new file mode 100644
index 000000000..47f29fc8d
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_memory_access.hpp"
+
+namespace ams::secmon::smc {
+
+ /* This is an atmosphere extension smc. */
+ SmcResult SmcIramCopy(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcWriteAddress(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.hpp b/exosphere2/program/source/smc/secmon_smc_memory_access.hpp
new file mode 100644
index 000000000..2b44fe292
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_memory_access.hpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ /* This is an atmosphere extension smc. */
+ SmcResult SmcIramCopy(const SmcArguments &args);
+ SmcResult SmcWriteAddress(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp
new file mode 100644
index 000000000..7434bf217
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_power_management.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcPowerOffCpu(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcPowerOnCpu(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcSuspendCpu(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.hpp b/exosphere2/program/source/smc/secmon_smc_power_management.hpp
new file mode 100644
index 000000000..7b67d6e78
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_power_management.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcPowerOffCpu(const SmcArguments &args);
+ SmcResult SmcPowerOnCpu(const SmcArguments &args);
+
+ SmcResult SmcSuspendCpu(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_random.cpp b/exosphere2/program/source/smc/secmon_smc_random.cpp
new file mode 100644
index 000000000..283e8d736
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_random.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_random.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcGenerateRandomBytes(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcGenerateRandomBytesNonBlocking(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_random.hpp b/exosphere2/program/source/smc/secmon_smc_random.hpp
new file mode 100644
index 000000000..27b20b241
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_random.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcGenerateRandomBytes(const SmcArguments &args);
+ SmcResult SmcGenerateRandomBytesNonBlocking(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.cpp b/exosphere2/program/source/smc/secmon_smc_register_access.cpp
new file mode 100644
index 000000000..a656174f3
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_register_access.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_register_access.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcReadWriteRegister(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.hpp b/exosphere2/program/source/smc/secmon_smc_register_access.hpp
new file mode 100644
index 000000000..d364b27f8
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_register_access.hpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcReadWriteRegister(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_result.cpp b/exosphere2/program/source/smc/secmon_smc_result.cpp
new file mode 100644
index 000000000..6b17b8e56
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_result.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_result.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcGetResult(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcGetResultData(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_result.hpp b/exosphere2/program/source/smc/secmon_smc_result.hpp
new file mode 100644
index 000000000..9355f22af
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_result.hpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ using GetResultHandler = SmcResult (*)(void *dst, size_t dst_size);
+
+ u64 BeginAsyncOperation(GetResultHandler handler);
+ void CancelAsyncOperation(u64 async_key);
+ void EndAsyncOperation();
+
+ SmcResult SmcGetResult(const SmcArguments &args);
+ SmcResult SmcGetResultData(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.cpp b/exosphere2/program/source/smc/secmon_smc_rsa.cpp
new file mode 100644
index 000000000..78c98e620
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_rsa.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "../secmon_error.hpp"
+#include "secmon_smc_rsa.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcModularExponentiate(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcModularExponentiateByStorageKey(const SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+}
diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.hpp b/exosphere2/program/source/smc/secmon_smc_rsa.hpp
new file mode 100644
index 000000000..c2dcd5cbe
--- /dev/null
+++ b/exosphere2/program/source/smc/secmon_smc_rsa.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include "secmon_smc_common.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcModularExponentiate(const SmcArguments &args);
+ SmcResult SmcModularExponentiateByStorageKey(const SmcArguments &args);
+
+}
diff --git a/exosphere2/program/split_program.py b/exosphere2/program/split_program.py
new file mode 100644
index 000000000..b52406eba
--- /dev/null
+++ b/exosphere2/program/split_program.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+import sys, lz4
+from struct import unpack as up
+
+def lz4_compress(data):
+ return lz4.block.compress(data, 'high_compression', store_size=False)
+
+def split_binary(data):
+ A, B, START, BOOT_CODE_START, BOOT_CODE_END, PROGRAM_START, C, D = up('= 0x40
+ for (fn, fdata) in split_binary(data):
+ with open('%s/%s' % (argv[2], fn), 'wb') as f:
+ f.write(fdata)
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(len(sys.argv), sys.argv))
\ No newline at end of file
diff --git a/libraries/config/arch/arm/arch.mk b/libraries/config/arch/arm/arch.mk
new file mode 100644
index 000000000..f629bb39f
--- /dev/null
+++ b/libraries/config/arch/arm/arch.mk
@@ -0,0 +1,11 @@
+ifeq ($(strip $(DEVKITPRO)),)
+$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro")
+endif
+
+include $(DEVKITPRO)/devkitARM/base_rules
+
+export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM
+export ATMOSPHERE_SETTINGS +=
+export ATMOSPHERE_CFLAGS +=
+export ATMOSPHERE_CXXFLAGS +=
+export ATMOSPHERE_ASFLAGS +=
diff --git a/libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk b/libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk
new file mode 100644
index 000000000..d50dd0607
--- /dev/null
+++ b/libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk
@@ -0,0 +1,5 @@
+export ATMOSPHERE_DEFINES += -DATMOSPHERE_CPU_ARM7TDMI
+export ATMOSPHERE_SETTINGS += -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork
+export ATMOSPHERE_CFLAGS +=
+export ATMOSPHERE_CXXFLAGS +=
+export ATMOSPHERE_ASFLAGS +=
\ No newline at end of file
diff --git a/libraries/config/board/nintendo/nx_bpmp/board.mk b/libraries/config/board/nintendo/nx_bpmp/board.mk
new file mode 100644
index 000000000..e267e24d6
--- /dev/null
+++ b/libraries/config/board/nintendo/nx_bpmp/board.mk
@@ -0,0 +1,5 @@
+export ATMOSPHERE_DEFINES += -DATMOSPHERE_BOARD_NINTENDO_NX -D__BPMP__
+export ATMOSPHERE_SETTINGS +=
+export ATMOSPHERE_CFLAGS +=
+export ATMOSPHERE_CXXFLAGS +=
+export ATMOSPHERE_ASFLAGS +=
\ No newline at end of file
diff --git a/libraries/config/common.mk b/libraries/config/common.mk
index 4d68b232e..e3a12dd7b 100644
--- a/libraries/config/common.mk
+++ b/libraries/config/common.mk
@@ -8,12 +8,13 @@ export ATMOSPHERE_LIBRARIES_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/..
ifeq ($(strip $(ATMOSPHERE_BOARD)),)
export ATMOSPHERE_BOARD := nx-hac-001
-endif
ifeq ($(strip $(ATMOSPHERE_CPU)),)
export ATMOSPHERE_CPU := arm-cortex-a57
endif
+endif
+
export ATMOSPHERE_DEFINES := -DATMOSPHERE
export ATMOSPHERE_SETTINGS := -fPIE -g
export ATMOSPHERE_CFLAGS := -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv \
@@ -23,6 +24,8 @@ export ATMOSPHERE_ASFLAGS :=
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
+
+ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57)
export ATMOSPHERE_ARCH_DIR := arch/arm64
export ATMOSPHERE_BOARD_DIR := board/nintendo/nx
export ATMOSPHERE_OS_DIR := os/horizon
@@ -30,6 +33,16 @@ export ATMOSPHERE_OS_DIR := os/horizon
export ATMOSPHERE_ARCH_NAME := arm64
export ATMOSPHERE_BOARD_NAME := nintendo_nx
export ATMOSPHERE_OS_NAME := horizon
+else ifeq ($(ATMOSPHERE_CPU),arm7tdmi)
+export ATMOSPHERE_ARCH_DIR := arch/arm
+export ATMOSPHERE_BOARD_DIR := board/nintendo/nx_bpmp
+export ATMOSPHERE_OS_DIR := os/horizon
+
+export ATMOSPHERE_ARCH_NAME := arm
+export ATMOSPHERE_BOARD_NAME := nintendo_nx
+export ATMOSPHERE_OS_NAME := horizon
+endif
+
endif
ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57)
@@ -37,12 +50,20 @@ export ATMOSPHERE_CPU_DIR := arch/arm64/cpu/cortex_a57
export ATMOSPHERE_CPU_NAME := arm_cortex_a57
endif
+ifeq ($(ATMOSPHERE_CPU),arm7tdmi)
+export ATMOSPHERE_CPU_DIR := arch/arm/cpu/arm7tdmi
+export ATMOSPHERE_CPU_NAME := arm7tdmi
+endif
+
export ATMOSPHERE_ARCH_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_ARCH_DIR)
export ATMOSPHERE_BOARD_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_BOARD_DIR)
export ATMOSPHERE_OS_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_OS_DIR)
export ATMOSPHERE_CPU_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_CPU_DIR)
+export ATMOSPHERE_LIBRARY_DIR := lib_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)
+export ATMOSPHERE_BUILD_DIR := build_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)
+
include $(ATMOSPHERE_ARCH_MAKE_DIR)/arch.mk
include $(ATMOSPHERE_BOARD_MAKE_DIR)/board.mk
include $(ATMOSPHERE_OS_MAKE_DIR)/os.mk
diff --git a/libraries/config/templates/exosphere.mk b/libraries/config/templates/exosphere.mk
new file mode 100644
index 000000000..163626659
--- /dev/null
+++ b/libraries/config/templates/exosphere.mk
@@ -0,0 +1,46 @@
+#---------------------------------------------------------------------------------
+# pull in common atmosphere configuration
+#---------------------------------------------------------------------------------
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm64)
+DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE
+SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -O2 -Werror -fno-non-call-exceptions
+CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
+CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
+ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
+else ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm)
+DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE
+SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions
+CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
+CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
+ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
+endif
+
+export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now
+
+export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
+ -Wl,--wrap,__cxa_throw \
+ -Wl,--wrap,__cxa_rethrow \
+ -Wl,--wrap,__cxa_allocate_exception \
+ -Wl,--wrap,__cxa_free_exception \
+ -Wl,--wrap,__cxa_begin_catch \
+ -Wl,--wrap,__cxa_end_catch \
+ -Wl,--wrap,__cxa_call_unexpected \
+ -Wl,--wrap,__cxa_call_terminate \
+ -Wl,--wrap,__gxx_personality_v0 \
+ -Wl,--wrap,_Unwind_Resume \
+ -Wl,--wrap,_ZSt19__throw_logic_errorPKc \
+ -Wl,--wrap,_ZSt20__throw_length_errorPKc \
+ -Wl,--wrap,_ZNSt11logic_errorC2EPKc
+
+export LIBS := -lexosphere
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere
diff --git a/libraries/libexosphere/Makefile b/libraries/libexosphere/Makefile
new file mode 100644
index 000000000..97b463c6b
--- /dev/null
+++ b/libraries/libexosphere/Makefile
@@ -0,0 +1,19 @@
+.PHONY: all arm64 arm clean arm64-clean arm-clean
+
+all: arm64 arm
+
+arm64:
+ $(MAKE) -f arm64.mk
+
+arm:
+ $(MAKE) -f arm.mk
+
+#---------------------------------------------------------------------------------
+
+clean: arm64-clean arm-clean
+
+arm64-clean:
+ $(MAKE) -f arm64.mk clean
+
+arm-clean:
+ $(MAKE) -f arm.mk clean
diff --git a/libraries/libexosphere/arm.mk b/libraries/libexosphere/arm.mk
new file mode 100644
index 000000000..7a653f53b
--- /dev/null
+++ b/libraries/libexosphere/arm.mk
@@ -0,0 +1,142 @@
+#---------------------------------------------------------------------------------
+# Define the atmosphere board and cpu
+#---------------------------------------------------------------------------------
+export ATMOSPHERE_BOARD := nx-hac-001
+export ATMOSPHERE_CPU := arm7tdmi
+
+#---------------------------------------------------------------------------------
+# pull in common atmosphere configuration
+#---------------------------------------------------------------------------------
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+
+DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE
+SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions
+CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
+CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
+ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
+
+SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source)
+
+LIBS :=
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(BUILD),$(notdir $(CURDIR)))
+#---------------------------------------------------------------------------------
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \
+ $(foreach dir,$(DATA),$(CURDIR)/$(dir))
+
+CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \
+ $(notdir $(wildcard $(dir)/*.c))))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c)))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c)))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c)))
+
+CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \
+ $(notdir $(wildcard $(dir)/*.cpp))))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp)))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp)))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp)))
+
+SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \
+ $(notdir $(wildcard $(dir)/*.s))))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s)))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s)))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s)))
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES_BIN := $(addsuffix .o,$(BINFILES))
+export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+export GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr)))
+export OFILES := $(OFILES_BIN) $(OFILES_SRC)
+export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ -I.
+
+.PHONY: clean all
+
+#---------------------------------------------------------------------------------
+all: $(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a
+
+$(ATMOSPHERE_LIBRARY_DIR):
+ @[ -d $@ ] || mkdir -p $@
+
+$(ATMOSPHERE_BUILD_DIR):
+ @[ -d $@ ] || mkdir -p $@
+
+$(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a : $(ATMOSPHERE_LIBRARY_DIR) $(ATMOSPHERE_BUILD_DIR) $(SOURCES) $(INCLUDES)
+ @$(MAKE) BUILD=$(ATMOSPHERE_BUILD_DIR) OUTPUT=$(CURDIR)/$@ \
+ DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
+ --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
+ -f $(CURDIR)/arm.mk
+
+dist-bin: all
+ @tar --exclude=*~ -cjf $(TARGET).tar.bz2 include $(ATMOSPHERE_LIBRARY_DIR)
+
+dist-src:
+ @tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source arm.mk
+
+dist: dist-src dist-bin
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2
+
+#---------------------------------------------------------------------------------
+else
+
+DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+$(OUTPUT) : $(OFILES)
+
+$(OFILES) : $(GCH_FILES)
+
+$(OFILES_SRC) : $(HFILES_BIN)
+
+libc.o: CFLAGS += -fno-builtin
+
+#---------------------------------------------------------------------------------
+%_bin.h %.bin.o : %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
+
diff --git a/libraries/libexosphere/arm64.mk b/libraries/libexosphere/arm64.mk
new file mode 100644
index 000000000..c9a17e60d
--- /dev/null
+++ b/libraries/libexosphere/arm64.mk
@@ -0,0 +1,143 @@
+#---------------------------------------------------------------------------------
+# Define the atmosphere board and cpu
+#---------------------------------------------------------------------------------
+export ATMOSPHERE_BOARD := nx-hac-001
+export ATMOSPHERE_CPU := arm-cortex-a57
+
+#---------------------------------------------------------------------------------
+# pull in common atmosphere configuration
+#---------------------------------------------------------------------------------
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+
+DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE
+SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -O2 -Werror -fno-non-call-exceptions
+CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
+CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
+ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
+
+SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source)
+
+LIBS :=
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(BUILD),$(notdir $(CURDIR)))
+#---------------------------------------------------------------------------------
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \
+ $(foreach dir,$(DATA),$(CURDIR)/$(dir))
+
+CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \
+ $(notdir $(wildcard $(dir)/*.c))))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c)))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c)))
+CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c)))
+
+CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \
+ $(notdir $(wildcard $(dir)/*.cpp))))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp)))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp)))
+CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp)))
+
+SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \
+ $(notdir $(wildcard $(dir)/*.s))))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s)))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s)))
+SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s)))
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES_BIN := $(addsuffix .o,$(BINFILES))
+export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+export GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr)))
+export OFILES := $(OFILES_BIN) $(OFILES_SRC)
+export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ -I.
+
+.PHONY: clean all
+
+#---------------------------------------------------------------------------------
+all: $(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a
+
+$(ATMOSPHERE_LIBRARY_DIR):
+ @[ -d $@ ] || mkdir -p $@
+
+$(ATMOSPHERE_BUILD_DIR):
+ @[ -d $@ ] || mkdir -p $@
+
+$(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a : $(ATMOSPHERE_LIBRARY_DIR) $(ATMOSPHERE_BUILD_DIR) $(SOURCES) $(INCLUDES)
+ @$(MAKE) BUILD=$(ATMOSPHERE_BUILD_DIR) OUTPUT=$(CURDIR)/$@ \
+ BUILD_CFLAGS="-DNDEBUG=1 -O2" \
+ DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
+ --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
+ -f $(CURDIR)/arm64.mk
+
+dist-bin: all
+ @tar --exclude=*~ -cjf $(TARGET).tar.bz2 include $(ATMOSPHERE_LIBRARY_DIR)
+
+dist-src:
+ @tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source arm64.mk
+
+dist: dist-src dist-bin
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2
+
+#---------------------------------------------------------------------------------
+else
+
+DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+$(OUTPUT) : $(OFILES)
+
+$(OFILES) : $(GCH_FILES)
+
+$(OFILES_SRC) : $(HFILES_BIN)
+
+libc.o: CFLAGS += -fno-builtin
+
+#---------------------------------------------------------------------------------
+%_bin.h %.bin.o : %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
+
diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp
new file mode 100644
index 000000000..db9b2cb2d
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere.hpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/actmon.hpp b/libraries/libexosphere/include/exosphere/actmon.hpp
new file mode 100644
index 000000000..929b32849
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/actmon.hpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::actmon {
+
+ using InterruptHandler = void(*)();
+
+ void SetRegisterAddress(uintptr_t address);
+
+ void HandleInterrupt();
+
+ void StartMonitoringBpmp(InterruptHandler handler);
+ void StopMonitoringBpmp();
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/clkrst.hpp b/libraries/libexosphere/include/exosphere/clkrst.hpp
new file mode 100644
index 000000000..c26dfb118
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/clkrst.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::clkrst {
+
+ void SetRegisterAddress(uintptr_t address);
+
+ void SetFuseVisibility(bool visible);
+
+ void EnableUartAClock();
+ void EnableUartBClock();
+ void EnableUartCClock();
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/common.hpp b/libraries/libexosphere/include/exosphere/common.hpp
new file mode 100644
index 000000000..c0f4e4b72
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/common.hpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+#if 1 || defined(AMS_BUILD_FOR_AUDITING)
+#define EXOSPHERE_BUILD_FOR_AUDITING
+#endif
+
+#if defined(EXOSPHERE_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
+#define EXOSPHERE_BUILD_FOR_DEBUGGING
+#endif
+
+#ifdef EXOSPHERE_BUILD_FOR_DEBUGGING
+#define EXOSPHERE_ENABLE_ASSERTIONS
+#define EXOSPHERE_ENABLE_DEBUG_PRINT
+#endif
diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp
new file mode 100644
index 000000000..3df44b8a3
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/fuse.hpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include
+
+namespace ams::fuse {
+
+ enum HardwareType {
+ HardwareType_Icosa = 0,
+ HardwareType_Copper = 1,
+ HardwareType_Hoag = 2,
+ HardwareType_Iowa = 3,
+ HardwareType_Calcio = 4,
+ HardwareType_Five = 5,
+
+ HardwareType_Undefined = 0xF,
+ };
+
+ enum HardwareState {
+ HardwareState_Development = 0,
+ HardwareState_Production = 1,
+ HardwareState_Undefined = 2,
+ };
+
+ void SetRegisterAddress(uintptr_t address);
+ void SetWriteSecureOnly();
+ void Lockout();
+
+ u32 GetOdmWord(int index);
+
+ HardwareType GetHardwareType();
+ HardwareState GetHardwareState();
+ pmic::Regulator GetRegulator();
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/gic.hpp b/libraries/libexosphere/include/exosphere/gic.hpp
new file mode 100644
index 000000000..72e30432c
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/gic.hpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::gic {
+
+ enum InterruptMode {
+ InterruptMode_Level = 0,
+ InterruptMode_Edge = 1,
+ };
+
+ constexpr inline s32 HighestPriority = 0;
+ constexpr inline s32 InterruptCount = 224;
+
+ void SetRegisterAddress(uintptr_t distributor_address, uintptr_t cpu_interface_address);
+ void InitializeCommon();
+ void InitializeCoreUnique();
+
+ void SetPriority(int interrupt_id, int priority);
+ void SetInterruptGroup(int interrupt_id, int group);
+ void SetEnable(int interrupt_id, bool enable);
+ void SetSpiTargetCpu(int interrupt_id, u32 cpu_mask);
+ void SetSpiMode(int interrupt_id, InterruptMode mode);
+
+ int GetInterruptRequestId();
+ void SetEndOfInterrupt(int interrupt_id);
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/hw.hpp b/libraries/libexosphere/include/exosphere/hw.hpp
new file mode 100644
index 000000000..73c68b050
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/hw.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+#if defined(ATMOSPHERE_ARCH_ARM64)
+ #include
+ namespace ams::hw { using namespace ams::hw::arch::arm64; }
+#elif defined(ATMOSPHERE_ARCH_ARM)
+ #include
+ namespace ams::hw { using namespace ams::hw::arch::arm; }
+#else
+ #error "Unknown architecture for hw!"
+#endif
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm.hpp
new file mode 100644
index 000000000..344e30726
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/hw/hw_arm.hpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::hw::arch::arm {
+
+#ifdef __BPMP__
+ constexpr inline size_t DataCacheLineSize = 0x1;
+
+ ALWAYS_INLINE void DataSynchronizationBarrier() {
+ /* ... */
+ }
+
+ ALWAYS_INLINE void DataSynchronizationBarrierInnerShareable() {
+ /* ... */
+ }
+
+ ALWAYS_INLINE void DataMemoryBarrier() {
+ /* ... */
+ }
+
+ ALWAYS_INLINE void InstructionSynchronizationBarrier() {
+ /* ... */
+ }
+
+ ALWAYS_INLINE void FlushDataCache(const void *ptr, size_t size) {
+ AMS_UNUSED(ptr);
+ AMS_UNUSED(size);
+ /* ... */
+ }
+#else
+ #error "Unknown ARM board for ams::hw"
+#endif
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64.hpp
new file mode 100644
index 000000000..404a3c5b7
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64.hpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include
+#include
+
+namespace ams::hw::arch::arm64 {
+
+ ALWAYS_INLINE void DataSynchronizationBarrier() {
+ __asm__ __volatile__("dsb sy" ::: "memory");
+ }
+
+ ALWAYS_INLINE void DataSynchronizationBarrierInnerShareable() {
+ __asm__ __volatile__("dsb ish" ::: "memory");
+ }
+
+ ALWAYS_INLINE void DataMemoryBarrier() {
+ __asm__ __volatile__("dmb sy" ::: "memory");
+ }
+
+ ALWAYS_INLINE void InstructionSynchronizationBarrier() {
+ __asm__ __volatile__("isb" ::: "memory");
+ }
+
+ ALWAYS_INLINE void WaitForInterrupt() {
+ __asm__ __volatile__("wfi" ::: "memory");
+ }
+
+ ALWAYS_INLINE void WaitForEvent() {
+ __asm__ __volatile__("wfe" ::: "memory");
+ }
+
+ ALWAYS_INLINE void SendEvent() {
+ __asm__ __volatile__("sev" ::: "memory");
+ }
+
+ ALWAYS_INLINE int CountLeadingZeros(u64 v) {
+ u64 z;
+ __asm__ __volatile__("clz %[z], %[v]" : [z]"=r"(z) : [v]"r"(v));
+ return z;
+ }
+
+ ALWAYS_INLINE int CountLeadingZeros(u32 v) {
+ u32 z;
+ __asm__ __volatile__("clz %w[z], %w[v]" : [z]"=r"(z) : [v]"r"(v));
+ return z;
+ }
+
+ ALWAYS_INLINE int GetCurrentCoreId() {
+ u64 mpidr;
+ HW_CPU_GET_MPIDR_EL1(mpidr);
+ return mpidr & 0xFF;
+ }
+
+}
diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp
new file mode 100644
index 000000000..ac534fe14
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::hw::arch::arm64 {
+
+#if defined(ATMOSPHERE_CPU_ARM_CORTEX_A57) || defined(ATMOSPHERE_CPU_ARM_CORTEX_A53)
+ constexpr inline size_t InstructionCacheLineSize = 0x40;
+ constexpr inline size_t DataCacheLineSize = 0x40;
+ constexpr inline size_t NumPerformanceCounters = 6;
+#else
+ #error "Unknown CPU for cache line sizes"
+#endif
+
+ ALWAYS_INLINE void InvalidateEntireTlb() {
+ __asm__ __volatile__("tlbi alle3is" ::: "memory");
+ }
+
+ ALWAYS_INLINE void InvalidateDataCacheLine(void *ptr) {
+ __asm __volatile__("dc ivac, %[ptr]" :: [ptr]"r"(ptr) : "memory");
+ }
+
+ ALWAYS_INLINE void FlushDataCacheLine(void *ptr) {
+ __asm __volatile__("dc civac, %[ptr]" :: [ptr]"r"(ptr) : "memory");
+ }
+
+ ALWAYS_INLINE void InvalidateEntireInstructionCache() {
+ __asm__ __volatile__("ic iallu" ::: "memory");
+ }
+
+ ALWAYS_INLINE void InvalidateTlb(uintptr_t address) {
+ __asm__ __volatile__("tlbi vae3is, %[address]" :: [address]"r"(address) : "memory");
+ }
+
+ ALWAYS_INLINE void InvalidateTlbLastLevel(uintptr_t address) {
+ __asm__ __volatile__("tlbi vale3is, %[address]" :: [address]"r"(address) : "memory");
+ }
+
+ void FlushDataCache(const void *ptr, size_t size);
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp
new file mode 100644
index 000000000..637f3c026
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::hw::arch::arm64 {
+
+ #define HW_CPU_GET_SYSREG(name, value) __asm__ __volatile__("mrs %0, " #name "" : "=&r"(value) :: "memory");
+
+ #define HW_CPU_SET_SYSREG(name, value) __asm__ __volatile__("msr " #name ", %0" :: "r"(value) : "memory", "cc")
+
+ #define HW_CPU_GET_SCTLR_EL3(value) HW_CPU_GET_SYSREG(sctlr_el3, value)
+ #define HW_CPU_SET_SCTLR_EL3(value) HW_CPU_SET_SYSREG(sctlr_el3, value)
+
+ #define HW_CPU_GET_SCR_EL3(value) HW_CPU_GET_SYSREG(scr_el3, value)
+ #define HW_CPU_SET_SCR_EL3(value) HW_CPU_SET_SYSREG(scr_el3, value)
+
+ #define HW_CPU_GET_CPTR_EL3(value) HW_CPU_GET_SYSREG(cptr_el3, value)
+ #define HW_CPU_SET_CPTR_EL3(value) HW_CPU_SET_SYSREG(cptr_el3, value)
+
+ #define HW_CPU_GET_TTBR0_EL3(value) HW_CPU_GET_SYSREG(ttbr0_el3, value)
+ #define HW_CPU_SET_TTBR0_EL3(value) HW_CPU_SET_SYSREG(ttbr0_el3, value)
+
+ #define HW_CPU_GET_TCR_EL3(value) HW_CPU_GET_SYSREG(tcr_el3, value)
+ #define HW_CPU_SET_TCR_EL3(value) HW_CPU_SET_SYSREG(tcr_el3, value)
+
+ #define HW_CPU_GET_MAIR_EL3(value) HW_CPU_GET_SYSREG(mair_el3, value)
+ #define HW_CPU_SET_MAIR_EL3(value) HW_CPU_SET_SYSREG(mair_el3, value)
+
+ #define HW_CPU_GET_VBAR_EL3(value) HW_CPU_GET_SYSREG(vbar_el3, value)
+ #define HW_CPU_SET_VBAR_EL3(value) HW_CPU_SET_SYSREG(vbar_el3, value)
+
+ #define HW_CPU_GET_CLIDR_EL1(value) HW_CPU_GET_SYSREG(clidr_el1, value)
+ #define HW_CPU_SET_CLIDR_EL1(value) HW_CPU_SET_SYSREG(clidr_el1, value)
+
+ #define HW_CPU_GET_CCSIDR_EL1(value) HW_CPU_GET_SYSREG(ccsidr_el1, value)
+ #define HW_CPU_SET_CCSIDR_EL1(value) HW_CPU_SET_SYSREG(ccsidr_el1, value)
+
+ #define HW_CPU_GET_CSSELR_EL1(value) HW_CPU_GET_SYSREG(csselr_el1, value)
+ #define HW_CPU_SET_CSSELR_EL1(value) HW_CPU_SET_SYSREG(csselr_el1, value)
+
+ #define HW_CPU_GET_CPUACTLR_EL1(value) HW_CPU_GET_SYSREG(s3_1_c15_c2_0, value)
+ #define HW_CPU_SET_CPUACTLR_EL1(value) HW_CPU_SET_SYSREG(s3_1_c15_c2_0, value)
+
+ #define HW_CPU_GET_CPUECTLR_EL1(value) HW_CPU_GET_SYSREG(s3_1_c15_c2_1, value)
+ #define HW_CPU_SET_CPUECTLR_EL1(value) HW_CPU_SET_SYSREG(s3_1_c15_c2_1, value)
+
+ #define HW_CPU_GET_DBGAUTHSTATUS_EL1(value) HW_CPU_GET_SYSREG(dbgauthstatus_el1, value)
+ #define HW_CPU_SET_DBGAUTHSTATUS_EL1(value) HW_CPU_SET_SYSREG(dbgauthstatus_el1, value)
+
+ #define HW_CPU_GET_MPIDR_EL1(value) HW_CPU_GET_SYSREG(mpidr_el1, value)
+
+ #define HW_CPU_GET_OSLAR_EL1(value) HW_CPU_GET_SYSREG(oslar_el1, value)
+ #define HW_CPU_SET_OSLAR_EL1(value) HW_CPU_SET_SYSREG(oslar_el1, value)
+
+ #define HW_CPU_GET_OSDTRRX_EL1(value) HW_CPU_GET_SYSREG(osdtrrx_el1, value)
+ #define HW_CPU_SET_OSDTRRX_EL1(value) HW_CPU_SET_SYSREG(osdtrrx_el1, value)
+
+ #define HW_CPU_GET_OSDTRTX_EL1(value) HW_CPU_GET_SYSREG(osdtrtx_el1, value)
+ #define HW_CPU_SET_OSDTRTX_EL1(value) HW_CPU_SET_SYSREG(osdtrtx_el1, value)
+
+ #define HW_CPU_GET_MDSCR_EL1(value) HW_CPU_GET_SYSREG(mdscr_el1, value)
+ #define HW_CPU_SET_MDSCR_EL1(value) HW_CPU_SET_SYSREG(mdscr_el1, value)
+
+ #define HW_CPU_GET_OSECCR_EL1(value) HW_CPU_GET_SYSREG(oseccr_el1, value)
+ #define HW_CPU_SET_OSECCR_EL1(value) HW_CPU_SET_SYSREG(oseccr_el1, value)
+
+ #define HW_CPU_GET_MDCCINT_EL1(value) HW_CPU_GET_SYSREG(mdccint_el1, value)
+ #define HW_CPU_SET_MDCCINT_EL1(value) HW_CPU_SET_SYSREG(mdccint_el1, value)
+
+ #define HW_CPU_GET_DBGCLAIMCLR_EL1(value) HW_CPU_GET_SYSREG(dbgclaimclr_el1, value)
+ #define HW_CPU_SET_DBGCLAIMCLR_EL1(value) HW_CPU_SET_SYSREG(dbgclaimclr_el1, value)
+
+ #define HW_CPU_GET_DBGVCR32_EL2(value) HW_CPU_GET_SYSREG(dbgvcr32_el2, value)
+ #define HW_CPU_SET_DBGVCR32_EL2(value) HW_CPU_SET_SYSREG(dbgvcr32_el2, value)
+
+ #define HW_CPU_GET_SDER32_EL3(value) HW_CPU_GET_SYSREG(sder32_el3, value)
+ #define HW_CPU_SET_SDER32_EL3(value) HW_CPU_SET_SYSREG(sder32_el3, value)
+
+ #define HW_CPU_GET_MDCR_EL2(value) HW_CPU_GET_SYSREG(mdcr_el2, value)
+ #define HW_CPU_SET_MDCR_EL2(value) HW_CPU_SET_SYSREG(mdcr_el2, value)
+
+ #define HW_CPU_GET_MDCR_EL3(value) HW_CPU_GET_SYSREG(mdcr_el3, value)
+ #define HW_CPU_SET_MDCR_EL3(value) HW_CPU_SET_SYSREG(mdcr_el3, value)
+
+ #define HW_CPU_GET_SPSR_EL3(value) HW_CPU_GET_SYSREG(spsr_el3, value)
+ #define HW_CPU_SET_SPSR_EL3(value) HW_CPU_SET_SYSREG(spsr_el3, value)
+
+ #define HW_CPU_GET_DBGBVR0_EL1(value) HW_CPU_GET_SYSREG(dbgbvr0_el1, value)
+ #define HW_CPU_SET_DBGBVR0_EL1(value) HW_CPU_SET_SYSREG(dbgbvr0_el1, value)
+ #define HW_CPU_GET_DBGBCR0_EL1(value) HW_CPU_GET_SYSREG(dbgbcr0_el1, value)
+ #define HW_CPU_SET_DBGBCR0_EL1(value) HW_CPU_SET_SYSREG(dbgbcr0_el1, value)
+ #define HW_CPU_GET_DBGBVR1_EL1(value) HW_CPU_GET_SYSREG(dbgbvr1_el1, value)
+ #define HW_CPU_SET_DBGBVR1_EL1(value) HW_CPU_SET_SYSREG(dbgbvr1_el1, value)
+ #define HW_CPU_GET_DBGBCR1_EL1(value) HW_CPU_GET_SYSREG(dbgbcr1_el1, value)
+ #define HW_CPU_SET_DBGBCR1_EL1(value) HW_CPU_SET_SYSREG(dbgbcr1_el1, value)
+ #define HW_CPU_GET_DBGBVR2_EL1(value) HW_CPU_GET_SYSREG(dbgbvr2_el1, value)
+ #define HW_CPU_SET_DBGBVR2_EL1(value) HW_CPU_SET_SYSREG(dbgbvr2_el1, value)
+ #define HW_CPU_GET_DBGBCR2_EL1(value) HW_CPU_GET_SYSREG(dbgbcr2_el1, value)
+ #define HW_CPU_SET_DBGBCR2_EL1(value) HW_CPU_SET_SYSREG(dbgbcr2_el1, value)
+ #define HW_CPU_GET_DBGBVR3_EL1(value) HW_CPU_GET_SYSREG(dbgbvr3_el1, value)
+ #define HW_CPU_SET_DBGBVR3_EL1(value) HW_CPU_SET_SYSREG(dbgbvr3_el1, value)
+ #define HW_CPU_GET_DBGBCR3_EL1(value) HW_CPU_GET_SYSREG(dbgbcr3_el1, value)
+ #define HW_CPU_SET_DBGBCR3_EL1(value) HW_CPU_SET_SYSREG(dbgbcr3_el1, value)
+ #define HW_CPU_GET_DBGBVR4_EL1(value) HW_CPU_GET_SYSREG(dbgbvr4_el1, value)
+ #define HW_CPU_SET_DBGBVR4_EL1(value) HW_CPU_SET_SYSREG(dbgbvr4_el1, value)
+ #define HW_CPU_GET_DBGBCR4_EL1(value) HW_CPU_GET_SYSREG(dbgbcr4_el1, value)
+ #define HW_CPU_SET_DBGBCR4_EL1(value) HW_CPU_SET_SYSREG(dbgbcr4_el1, value)
+ #define HW_CPU_GET_DBGBVR5_EL1(value) HW_CPU_GET_SYSREG(dbgbvr5_el1, value)
+ #define HW_CPU_SET_DBGBVR5_EL1(value) HW_CPU_SET_SYSREG(dbgbvr5_el1, value)
+ #define HW_CPU_GET_DBGBCR5_EL1(value) HW_CPU_GET_SYSREG(dbgbcr5_el1, value)
+ #define HW_CPU_SET_DBGBCR5_EL1(value) HW_CPU_SET_SYSREG(dbgbcr5_el1, value)
+
+ #define HW_CPU_GET_DBGWVR0_EL1(value) HW_CPU_GET_SYSREG(dbgwvr0_el1, value)
+ #define HW_CPU_SET_DBGWVR0_EL1(value) HW_CPU_SET_SYSREG(dbgwvr0_el1, value)
+ #define HW_CPU_GET_DBGWCR0_EL1(value) HW_CPU_GET_SYSREG(dbgwcr0_el1, value)
+ #define HW_CPU_SET_DBGWCR0_EL1(value) HW_CPU_SET_SYSREG(dbgwcr0_el1, value)
+ #define HW_CPU_GET_DBGWVR1_EL1(value) HW_CPU_GET_SYSREG(dbgwvr1_el1, value)
+ #define HW_CPU_SET_DBGWVR1_EL1(value) HW_CPU_SET_SYSREG(dbgwvr1_el1, value)
+ #define HW_CPU_GET_DBGWCR1_EL1(value) HW_CPU_GET_SYSREG(dbgwcr1_el1, value)
+ #define HW_CPU_SET_DBGWCR1_EL1(value) HW_CPU_SET_SYSREG(dbgwcr1_el1, value)
+ #define HW_CPU_GET_DBGWVR2_EL1(value) HW_CPU_GET_SYSREG(dbgwvr2_el1, value)
+ #define HW_CPU_SET_DBGWVR2_EL1(value) HW_CPU_SET_SYSREG(dbgwvr2_el1, value)
+ #define HW_CPU_GET_DBGWCR2_EL1(value) HW_CPU_GET_SYSREG(dbgwcr2_el1, value)
+ #define HW_CPU_SET_DBGWCR2_EL1(value) HW_CPU_SET_SYSREG(dbgwcr2_el1, value)
+ #define HW_CPU_GET_DBGWVR3_EL1(value) HW_CPU_GET_SYSREG(dbgwvr3_el1, value)
+ #define HW_CPU_SET_DBGWVR3_EL1(value) HW_CPU_SET_SYSREG(dbgwvr3_el1, value)
+ #define HW_CPU_GET_DBGWCR3_EL1(value) HW_CPU_GET_SYSREG(dbgwcr3_el1, value)
+ #define HW_CPU_SET_DBGWCR3_EL1(value) HW_CPU_SET_SYSREG(dbgwcr3_el1, value)
+
+ /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/system-control-register-el3 */
+ struct SctlrEl3 {
+ using M = util::BitPack32::Field< 0, 1>;
+ using A = util::BitPack32::Field< 1, 1>;
+ using C = util::BitPack32::Field< 2, 1>;
+ using Sa = util::BitPack32::Field< 3, 1>;
+ using I = util::BitPack32::Field<12, 1>;
+ using Wxn = util::BitPack32::Field<19, 1>;
+ using Ee = util::BitPack32::Field<25, 1>;
+
+ static constexpr u32 Res1 = 0x30C50830;
+ };
+
+ /* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/BABGIHHJ.html */
+ struct ScrEl3 {
+ using Ns = util::BitPack32::Field< 0, 1>;
+ using Irq = util::BitPack32::Field< 1, 1>;
+ using Fiq = util::BitPack32::Field< 2, 1>;
+ using Ea = util::BitPack32::Field< 3, 1>;
+ using Fw = util::BitPack32::Field< 4, 1>;
+ using Aw = util::BitPack32::Field< 5, 1>;
+ using Net = util::BitPack32::Field< 6, 1>;
+ using Smd = util::BitPack32::Field< 7, 1>;
+ using Hce = util::BitPack32::Field< 8, 1>;
+ using Sif = util::BitPack32::Field< 9, 1>;
+ using RwCortexA53 = util::BitPack32::Field<10, 1>;
+ using StCortexA53 = util::BitPack32::Field<11, 1>;
+ using Twi = util::BitPack32::Field<12, 1>;
+ using Twe = util::BitPack32::Field<13, 1>;
+ };
+
+ /* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/CIHDEBIG.html */
+ struct CptrEl3 {
+ using Tfp = util::BitPack32::Field<10, 1>;
+ using Tta = util::BitPack32::Field<20, 1>;
+ using Tcpac = util::BitPack32::Field<31, 1>;
+ };
+
+ /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/translation-control-register-el3 */
+ struct TcrEl3 {
+ using T0sz = util::BitPack32::Field< 0, 6>;
+ using Irgn0 = util::BitPack32::Field< 8, 2>;
+ using Orgn0 = util::BitPack32::Field<10, 2>;
+ using Sh0 = util::BitPack32::Field<12, 2>;
+ using Tg0 = util::BitPack32::Field<14, 2>;
+ using Ps = util::BitPack32::Field<16, 3>;
+ using Tbi = util::BitPack32::Field<20, 1>;
+
+ static constexpr u32 Res1 = 0x80800000;
+ };
+
+ struct ClidrEl1 {
+ using Ctype1 = util::BitPack32::Field< 0, 3>;
+ using Ctype2 = util::BitPack32::Field< 3, 3>;
+ using Ctype3 = util::BitPack32::Field< 6, 3>;
+ using Louis = util::BitPack32::Field<21, 3>;
+ using Loc = util::BitPack32::Field<24, 3>;
+ using Louu = util::BitPack32::Field<27, 3>;
+ };
+
+ struct CcsidrEl1 {
+ using LineSize = util::BitPack32::Field< 0, 3>;
+ using Associativity = util::BitPack32::Field< 3, 10>;
+ using NumSets = util::BitPack32::Field<13, 15>;
+ using Wa = util::BitPack32::Field<28, 1>;
+ using Ra = util::BitPack32::Field<29, 1>;
+ using Wb = util::BitPack32::Field<30, 1>;
+ using Wt = util::BitPack32::Field<31, 1>;
+ };
+
+ struct CsselrEl1 {
+ using InD = util::BitPack32::Field<0, 1>;
+ using Level = util::BitPack32::Field<1, 3>;
+ };
+
+ /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/cpu-auxiliary-control-register-el1 */
+ struct CpuactlrEl1CortexA57 {
+ /* TODO: Other bits */
+ using NonCacheableStreamingEnhancement = util::BitPack64::Field<24, 1>;
+ /* TODO: Other bits */
+ using DisableLoadPassDmb = util::BitPack64::Field<59, 1>;
+ using ForceProcessorRcgEnablesActive = util::BitPack64::Field<63, 1>;
+ };
+
+ /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/cpu-extended-control-register-el1 */
+ struct CpuectlrEl1CortexA57 {
+ using ProcessorDynamicRetentionControl = util::BitPack64::Field< 0, 2>;
+ using Smpen = util::BitPack64::Field< 6, 1>;
+ using L2LoadStoreDataPrefetchDistance = util::BitPack64::Field<32, 2>;
+ using L2InstructionFetchPrefetchDistance = util::BitPack64::Field<35, 2>;
+ using DisableTableWalkDescriptorAccessPrefetch = util::BitPack64::Field<38, 1>;
+ };
+
+ /* https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/dbgauthstatus_el1 */
+ struct DbgAuthStatusEl1 {
+ using Nsid = util::BitPack32::Field<0, 2>;
+ using Nsnid = util::BitPack32::Field<2, 2>;
+ using Sid = util::BitPack32::Field<4, 2>;
+ using Snid = util::BitPack32::Field<6, 2>;
+ };
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/i2c.hpp b/libraries/libexosphere/include/exosphere/i2c.hpp
new file mode 100644
index 000000000..fd3b10d2b
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/i2c.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::i2c {
+
+ enum Port {
+ Port_1 = 0,
+ /* TODO: Support other ports? */
+ Port_5 = 4,
+
+ Port_Count = 5,
+ };
+
+ void SetRegisterAddress(Port port, uintptr_t address);
+
+ void Initialize(Port port);
+
+ bool Query(void *dst, size_t dst_size, Port port, int address, int r);
+ bool Send(Port port, int address, int r, const void *src, size_t src_size);
+
+ inline u8 QueryByte(Port port, int address, int r) {
+ u8 byte;
+ Query(std::addressof(byte), sizeof(byte), port, address, r);
+ return byte;
+ }
+
+ inline void SendByte(Port port, int address, int r, u8 byte) {
+ Send(port, address, r, std::addressof(byte), sizeof(byte));
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/log.hpp b/libraries/libexosphere/include/exosphere/log.hpp
new file mode 100644
index 000000000..2f1cca8c5
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/log.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::log {
+
+ void Initialize();
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/mmu.hpp b/libraries/libexosphere/include/exosphere/mmu.hpp
new file mode 100644
index 000000000..c3b888e1d
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/mmu.hpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm.hpp
new file mode 100644
index 000000000..bedce5af7
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm.hpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::mmu::arch::arm {
+
+ /* NOTE: mmu is not supported by libexosphere-on-arm. */
+ /* However, we want the memory layout to be parseable, so we will include some arm64 mmu definitions. */
+ constexpr inline u64 L1EntryShift = 30;
+ constexpr inline u64 L2EntryShift = 21;
+ constexpr inline u64 L3EntryShift = 12;
+
+ constexpr inline u64 L1EntrySize = 1_GB;
+ constexpr inline u64 L2EntrySize = 2_MB;
+ constexpr inline u64 L3EntrySize = 4_KB;
+
+ constexpr inline u64 PageSize = L3EntrySize;
+
+ constexpr inline u64 L1EntryMask = ((static_cast(1) << (48 - L1EntryShift)) - 1) << L1EntryShift;
+ constexpr inline u64 L2EntryMask = ((static_cast(1) << (48 - L2EntryShift)) - 1) << L2EntryShift;
+ constexpr inline u64 L3EntryMask = ((static_cast(1) << (48 - L3EntryShift)) - 1) << L3EntryShift;
+
+ constexpr inline u64 TableEntryMask = L3EntryMask;
+
+ static_assert(L1EntryMask == 0x0000FFFFC0000000ul);
+ static_assert(L2EntryMask == 0x0000FFFFFFE00000ul);
+ static_assert(L3EntryMask == 0x0000FFFFFFFFF000ul);
+
+}
diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp
new file mode 100644
index 000000000..d18e2b4f1
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::mmu::arch::arm64 {
+
+ enum PageTableTableAttribute : u64 {
+ PageTableTableAttribute_None = (0ul << 0),
+ PageTableTableAttribute_PrivilegedExecuteNever = (1ul << 59),
+ PageTableTableAttribute_ExecuteNever = (1ul << 60),
+ PageTableTableAttribute_NonSecure = (1ul << 63),
+
+ PageTableTableAttributes_El3SecureCode = PageTableTableAttribute_None,
+ PageTableTableAttributes_El3SecureData = PageTableTableAttribute_ExecuteNever,
+ PageTableTableAttributes_El3NonSecureCode = PageTableTableAttributes_El3SecureCode | PageTableTableAttribute_NonSecure,
+ PageTableTableAttributes_El3NonSecureData = PageTableTableAttributes_El3SecureData | PageTableTableAttribute_NonSecure,
+ };
+
+ enum PageTableMappingAttribute : u64{
+ /* Security. */
+ PageTableMappingAttribute_NonSecure = (1ul << 5),
+
+ /* El1 Access. */
+ PageTableMappingAttribute_El1NotAllowed = (0ul << 6),
+ PageTableMappingAttribute_El1Allowed = (1ul << 6),
+
+ /* RW Permission. */
+ PageTableMappingAttribute_PermissionReadWrite = (0ul << 7),
+ PageTableMappingAttribute_PermissionReadOnly = (1ul << 7),
+
+ /* Shareability. */
+ PageTableMappingAttribute_ShareabilityNonShareable = (0ul << 8),
+ PageTableMappingAttribute_ShareabiltiyOuterShareable = (2ul << 8),
+ PageTableMappingAttribute_ShareabilityInnerShareable = (3ul << 8),
+
+ /* Access flag. */
+ PageTableMappingAttribute_AccessFlagNotAccessed = (0ul << 10),
+ PageTableMappingAttribute_AccessFlagAccessed = (1ul << 10),
+
+ /* Global. */
+ PageTableMappingAttribute_Global = (0ul << 11),
+ PageTableMappingAttribute_NonGlobal = (1ul << 11),
+
+ /* Contiguous */
+ PageTableMappingAttribute_NonContiguous = (0ul << 52),
+ PageTableMappingAttribute_Contiguous = (1ul << 52),
+
+ /* Privileged Execute Never */
+ PageTableMappingAttribute_PrivilegedExecuteNever = (1ul << 53),
+
+ /* Execute Never */
+ PageTableMappingAttribute_ExecuteNever = (1ul << 54),
+
+
+ /* Useful definitions. */
+ PageTableMappingAttributes_El3SecureRwCode = (
+ PageTableMappingAttribute_PermissionReadWrite |
+ PageTableMappingAttribute_ShareabilityInnerShareable
+ ),
+
+ PageTableMappingAttributes_El3SecureRoCode = (
+ PageTableMappingAttribute_PermissionReadOnly |
+ PageTableMappingAttribute_ShareabilityInnerShareable
+ ),
+
+ PageTableMappingAttributes_El3SecureRoData = (
+ PageTableMappingAttribute_PermissionReadOnly |
+ PageTableMappingAttribute_ShareabilityInnerShareable |
+ PageTableMappingAttribute_ExecuteNever
+ ),
+
+ PageTableMappingAttributes_El3SecureRwData = (
+ PageTableMappingAttribute_PermissionReadWrite |
+ PageTableMappingAttribute_ShareabilityInnerShareable |
+ PageTableMappingAttribute_ExecuteNever
+ ),
+
+ PageTableMappingAttributes_El3NonSecureRwCode = PageTableMappingAttributes_El3SecureRwCode | PageTableMappingAttribute_NonSecure,
+ PageTableMappingAttributes_El3NonSecureRoCode = PageTableMappingAttributes_El3SecureRoCode | PageTableMappingAttribute_NonSecure,
+ PageTableMappingAttributes_El3NonSecureRoData = PageTableMappingAttributes_El3SecureRoData | PageTableMappingAttribute_NonSecure,
+ PageTableMappingAttributes_El3NonSecureRwData = PageTableMappingAttributes_El3SecureRwData | PageTableMappingAttribute_NonSecure,
+
+
+ PageTableMappingAttributes_El3SecureDevice = PageTableMappingAttributes_El3SecureRwData,
+ PageTableMappingAttributes_El3NonSecureDevice = PageTableMappingAttributes_El3NonSecureRwData,
+ };
+
+ enum MemoryRegionAttribute : u64 {
+ MemoryRegionAttribute_Device_nGnRnE = (0ul << 2),
+ MemoryRegionAttribute_Device_nGnRE = (1ul << 2),
+ MemoryRegionAttribute_NormalMemory = (2ul << 2),
+ MemoryRegionAttribute_NormalMemoryNotCacheable = (3ul << 2),
+
+ MemoryRegionAttribute_NormalInnerShift = 0,
+ MemoryRegionAttribute_NormalOuterShift = 4,
+
+ #define AMS_MRA_DEFINE_NORMAL_ATTR(__NAME__, __VAL__) \
+ MemoryRegionAttribute_NormalInner##__NAME__ = (__VAL__ << MemoryRegionAttribute_NormalInnerShift), \
+ MemoryRegionAttribute_NormalOuter##__NAME__ = (__VAL__ << MemoryRegionAttribute_NormalOuterShift)
+
+ AMS_MRA_DEFINE_NORMAL_ATTR(NonCacheable, 4),
+
+ AMS_MRA_DEFINE_NORMAL_ATTR(WriteAllocate, (1ul << 0)),
+ AMS_MRA_DEFINE_NORMAL_ATTR(ReadAllocate, (1ul << 1)),
+
+ AMS_MRA_DEFINE_NORMAL_ATTR(WriteThroughTransient, (0ul << 2)),
+ AMS_MRA_DEFINE_NORMAL_ATTR(WriteBackTransient, (1ul << 2)),
+ AMS_MRA_DEFINE_NORMAL_ATTR(WriteThroughNonTransient, (2ul << 2)),
+ AMS_MRA_DEFINE_NORMAL_ATTR(WriteBackNonTransient, (3ul << 2)),
+
+ #undef AMS_MRA_DEFINE_NORMAL_ATTR
+
+ MemoryRegionAttributes_Normal = (
+ MemoryRegionAttribute_NormalInnerReadAllocate |
+ MemoryRegionAttribute_NormalOuterReadAllocate |
+ MemoryRegionAttribute_NormalInnerWriteAllocate |
+ MemoryRegionAttribute_NormalOuterWriteAllocate |
+ MemoryRegionAttribute_NormalInnerWriteBackNonTransient |
+ MemoryRegionAttribute_NormalOuterWriteBackNonTransient
+ ),
+
+ MemoryRegionAttributes_Device = (
+ MemoryRegionAttribute_Device_nGnRE
+ ),
+ };
+
+ constexpr inline u64 MemoryRegionAttributeWidth = 8;
+
+ constexpr PageTableMappingAttribute AddMappingAttributeIndex(PageTableMappingAttribute attr, int index) {
+ return static_cast(attr | (static_cast::type>(index) << 2));
+ }
+
+ constexpr inline u64 L1EntryShift = 30;
+ constexpr inline u64 L2EntryShift = 21;
+ constexpr inline u64 L3EntryShift = 12;
+
+ constexpr inline u64 L1EntrySize = 1_GB;
+ constexpr inline u64 L2EntrySize = 2_MB;
+ constexpr inline u64 L3EntrySize = 4_KB;
+
+ constexpr inline u64 PageSize = L3EntrySize;
+
+ constexpr inline u64 L1EntryMask = ((1ul << (48 - L1EntryShift)) - 1) << L1EntryShift;
+ constexpr inline u64 L2EntryMask = ((1ul << (48 - L2EntryShift)) - 1) << L2EntryShift;
+ constexpr inline u64 L3EntryMask = ((1ul << (48 - L3EntryShift)) - 1) << L3EntryShift;
+
+ constexpr inline u64 TableEntryMask = L3EntryMask;
+
+ static_assert(L1EntryMask == 0x0000FFFFC0000000ul);
+ static_assert(L2EntryMask == 0x0000FFFFFFE00000ul);
+ static_assert(L3EntryMask == 0x0000FFFFFFFFF000ul);
+
+ constexpr inline u64 TableEntryIndexMask = 0x1FF;
+
+ constexpr inline u64 EntryBlock = 0x1ul;
+ constexpr inline u64 EntryPage = 0x3ul;
+
+ constexpr u64 MakeTableEntry(u64 address, PageTableTableAttribute attr) {
+ return address | static_cast(attr) | 0x3ul;
+ }
+
+ constexpr u64 MakeL1BlockEntry(u64 address, PageTableMappingAttribute attr) {
+ return address | static_cast(attr) | 0x1ul;
+ }
+
+ constexpr u64 MakeL2BlockEntry(u64 address, PageTableMappingAttribute attr) {
+ return address | static_cast(attr) | 0x1ul;
+ }
+
+ constexpr u64 MakeL3BlockEntry(u64 address, PageTableMappingAttribute attr) {
+ return address | static_cast(attr) | 0x3ul;
+ }
+
+ constexpr uintptr_t GetL2Offset(uintptr_t address) {
+ return address & ((1ul << L2EntryShift) - 1);
+ }
+
+ constexpr u64 GetL1EntryIndex(uintptr_t address) {
+ return ((address >> L1EntryShift) & TableEntryIndexMask);
+ }
+
+ constexpr u64 GetL2EntryIndex(uintptr_t address) {
+ return ((address >> L2EntryShift) & TableEntryIndexMask);
+ }
+
+ constexpr u64 GetL3EntryIndex(uintptr_t address) {
+ return ((address >> L3EntryShift) & TableEntryIndexMask);
+ }
+
+ constexpr ALWAYS_INLINE void SetTableImpl(u64 *table, u64 index, u64 value) {
+ /* Ensure (for constexpr validation purposes) that the entry we set is clear. */
+ if (table[index]) {
+ __builtin_unreachable();
+ }
+
+ /* Set the value. */
+ table[index] = value;
+ }
+
+ constexpr void SetL1TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) {
+ SetTableImpl(table, GetL1EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr));
+ }
+
+ constexpr void SetL2TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) {
+ SetTableImpl(table, GetL2EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr));
+ }
+
+ constexpr void SetL1BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) {
+ const u64 start = GetL1EntryIndex(virt_addr);
+ const u64 count = (size >> L1EntryShift);
+
+ for (u64 i = 0; i < count; ++i) {
+ SetTableImpl(table, start + i, MakeL1BlockEntry((phys_addr & L1EntryMask) + (i << L1EntryShift), attr));
+ }
+ }
+
+ constexpr void SetL2BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) {
+ const u64 start = GetL2EntryIndex(virt_addr);
+ const u64 count = (size >> L2EntryShift);
+
+ for (u64 i = 0; i < count; ++i) {
+ SetTableImpl(table, start + i, MakeL2BlockEntry((phys_addr & L2EntryMask) + (i << L2EntryShift), attr));
+ }
+ }
+
+ constexpr void SetL3BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) {
+ const u64 start = GetL3EntryIndex(virt_addr);
+ const u64 count = (size >> L3EntryShift);
+
+ for (u64 i = 0; i < count; ++i) {
+ SetTableImpl(table, start + i, MakeL3BlockEntry((phys_addr & L3EntryMask) + (i << L3EntryShift), attr));
+ }
+ }
+
+ constexpr void InvalidateL1Entries(u64 *table, uintptr_t virt_addr, size_t size) {
+ const u64 start = GetL1EntryIndex(virt_addr);
+ const u64 count = (size >> L1EntryShift);
+ const u64 end = start + count;
+
+ for (u64 i = start; i < end; ++i) {
+ table[i] = 0;
+ }
+ }
+
+ constexpr void InvalidateL2Entries(u64 *table, uintptr_t virt_addr, size_t size) {
+ const u64 start = GetL2EntryIndex(virt_addr);
+ const u64 count = (size >> L2EntryShift);
+ const u64 end = start + count;
+
+ for (u64 i = start; i < end; ++i) {
+ table[i] = 0;
+ }
+ }
+
+ constexpr void InvalidateL3Entries(u64 *table, uintptr_t virt_addr, size_t size) {
+ const u64 start = GetL3EntryIndex(virt_addr);
+ const u64 count = (size >> L3EntryShift);
+ const u64 end = start + count;
+
+ for (u64 i = start; i < end; ++i) {
+ table[i] = 0;
+ }
+ }
+
+}
diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.hpp
new file mode 100644
index 000000000..1c0e02801
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.hpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+#if defined(ATMOSPHERE_ARCH_ARM64)
+ #include
+#elif defined(ATMOSPHERE_ARCH_ARM)
+ #include
+#else
+ #error "Unknown architecture for mmu!"
+#endif
+
+namespace ams::mmu {
+
+ #if defined(ATMOSPHERE_ARCH_ARM64)
+ using namespace ams::mmu::arch::arm64;
+ #elif defined(ATMOSPHERE_ARCH_ARM)
+ using namespace ams::mmu::arch::arm;
+ #else
+ #error "Unknown architecture for mmu!"
+ #endif
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/pkg1.hpp b/libraries/libexosphere/include/exosphere/pkg1.hpp
new file mode 100644
index 000000000..d7dbad6af
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/pkg1.hpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp
new file mode 100644
index 000000000..da73f0112
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::pkg1 {
+
+ enum MemorySize {
+ MemorySize_4GB = 0,
+ MemorySize_6GB = 1,
+ MemorySize_8GB = 2,
+ };
+
+ enum MemoryArrange {
+ MemoryArrange_Normal = 1,
+ MemoryArrange_AppletDev = 2,
+ MemoryArrange_SystemDev = 2,
+ };
+
+ enum MemoryMode {
+ MemoryMode_SizeShift = 4,
+ MemoryMode_SizeMask = 0x30,
+
+ MemoryMode_ArrangeMask = 0x0F,
+
+ MemoryMode_Auto = 0x00,
+
+ MemoryMode_4GB = ((MemorySize_4GB << MemoryMode_SizeShift) | (MemoryArrange_Normal)),
+ MemoryMode_4GBAppletDev = ((MemorySize_4GB << MemoryMode_SizeShift) | (MemoryArrange_AppletDev)),
+ MemoryMode_4GBSystemDev = ((MemorySize_4GB << MemoryMode_SizeShift) | (MemoryArrange_SystemDev)),
+
+ MemoryMode_6GB = ((MemorySize_6GB << MemoryMode_SizeShift) | (MemoryArrange_Normal)),
+ MemoryMode_6GBAppletDev = ((MemorySize_6GB << MemoryMode_SizeShift) | (MemoryArrange_AppletDev)),
+
+ MemoryMode_8GB = ((MemorySize_8GB << MemoryMode_SizeShift) | (MemoryArrange_Normal)),
+ };
+
+ constexpr ALWAYS_INLINE MemorySize GetMemorySize(MemoryMode mode) {
+ return static_cast(mode >> MemoryMode_SizeShift);
+ }
+
+ constexpr ALWAYS_INLINE MemoryArrange GetMemoryArrange(MemoryMode mode) {
+ return static_cast(mode & MemoryMode_ArrangeMask);
+ }
+
+ constexpr ALWAYS_INLINE MemoryMode MakeMemoryMode(MemorySize size, MemoryArrange arrange) {
+ return static_cast((size << MemoryMode_SizeShift) | (arrange));
+ }
+
+ struct BootConfigData {
+ u32 version;
+ u32 reserved_04;
+ u32 reserved_08;
+ u32 reserved_0C;
+ u8 flags1[0x10];
+ u8 flags0[0x10];
+ u64 initial_tsc_value;
+ u8 padding_38[0x200 - 0x38];
+
+ constexpr bool IsDevelopmentFunctionEnabled() const {
+ return (this->flags1[0] & (1 << 1)) != 0;
+ }
+
+ constexpr bool IsSErrorDebugEnabled() const {
+ return (this->flags1[0] & (1 << 2)) != 0;
+ }
+
+ constexpr u8 GetKernelFlags0() const {
+ return this->flags0[1];
+ }
+
+ constexpr u8 GetKernelFlags1() const {
+ return this->flags1[0];
+ }
+
+ constexpr MemoryMode GetMemoryMode() const {
+ return static_cast(this->flags0[3]);
+ }
+
+ bool IsTscInitialValueValid() const {
+ return (this->flags0[4] & (1 << 0)) != 0;
+ }
+ };
+ static_assert(util::is_pod::value);
+ static_assert(sizeof(BootConfigData) == 0x200);
+
+ struct BootConfigSignedData {
+ u32 version;
+ u32 reserved_04;
+ u8 flags;
+ u8 reserved_09[0x10 - 9];
+ u8 ecid[0x10];
+ u8 flags1[0x10];
+ u8 flags0[0x10];
+ u8 padding_40[0x100 - 0x40];
+
+ constexpr bool IsPackage2EncryptionDisabled() const {
+ return (this->flags & (1 << 0)) != 0;
+ }
+
+ constexpr bool IsPackage2SignatureVerificationDisabled() const {
+ return (this->flags & (1 << 1)) != 0;
+ }
+
+ constexpr bool IsProgramVerificationDisabled() const {
+ return (this->flags1[0] & (1 << 0)) != 0;
+ }
+ };
+ static_assert(util::is_pod::value);
+ static_assert(sizeof(BootConfigSignedData) == 0x100);
+
+ struct BootConfig {
+ BootConfigData data;
+ u8 signature[0x100];
+ BootConfigSignedData signed_data;
+ };
+ static_assert(util::is_pod::value);
+ static_assert(sizeof(BootConfig) == 0x400);
+
+}
diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp
new file mode 100644
index 000000000..93f70df18
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::pkg1 {
+
+ enum BootloaderState {
+ BootloaderState_Start = 0,
+ BootloaderState_LoadedBootConfig = 1,
+ BootloaderState_InitializedDram = 2,
+ BootloaderState_LoadedPackage2 = 3,
+ BootloaderState_Done = 4,
+ };
+
+ enum SecureMonitorState {
+ SecureMonitorState_Start = 0,
+ SecureMonitorState_Initialized = 1,
+ };
+
+ struct BctParameters {
+ u32 bootloader_version;
+ u32 bootloader_start_block;
+ u32 bootloader_start_page;
+ u32 bootloader_attributes;
+ };
+ static_assert(util::is_pod::value && sizeof(BctParameters) == 0x10);
+
+ struct SecureMonitorParameters {
+ u32 bootloader_start_time;
+ u32 bootloader_end_time;
+ u32 secmon_start_time;
+ u32 secmon_end_time;
+ BctParameters bct_params;
+ u8 reserved[0xD8];
+ u32 bootloader_state;
+ u32 secmon_state;
+ u8 reserved2[0x100];
+ };
+ static_assert(util::is_pod::value);
+ static_assert(sizeof(SecureMonitorParameters) == 0x200);
+
+ static_assert(offsetof(SecureMonitorParameters, bct_params) == 0x10);
+ static_assert(offsetof(SecureMonitorParameters, bootloader_state) == 0xF8);
+ static_assert(offsetof(SecureMonitorParameters, secmon_state) == 0xFC);
+
+ enum BootloaderAttribute {
+ BootloaderAttribute_None = (0u << 0),
+ BootloaderAttribute_RecoveryBoot = (1u << 0),
+
+ BootloaderAttribute_RestrictedSmcShift = 1,
+ BootloaderAttribute_RestrictedSmcMask = (0xFu << BootloaderAttribute_RestrictedSmcShift),
+ };
+
+}
diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_error_types.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_error_types.hpp
new file mode 100644
index 000000000..76331434d
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_error_types.hpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::pkg1 {
+
+ enum ErrorReason {
+ ErrorReason_None = 0,
+ ErrorReason_InvalidPackage2Signature = 1,
+ ErrorReason_InvalidPackage2Meta = 2,
+ ErrorReason_InvalidPackage2Version = 3,
+ ErrorReason_InvalidPackage2Payload = 4,
+ ErrorReason_UnknownSmc = 5,
+ ErrorReason_UnknownAbort = 6,
+ ErrorReason_InvalidCoreContext = 7,
+ ErrorReason_InvalidSecurityEngineStickyBits = 8,
+ ErrorReason_UnexpectedReset = 9,
+
+ ErrorReason_Exception = 0x10,
+
+ ErrorReason_TransitionToSafeMode = 0x20,
+ ErrorReason_SecureInitializerReboot = 0x21,
+
+ ErrorReason_SdmmcError = 0x30,
+ ErrorReason_InvalidDramId = 0x31,
+ ErrorReason_InvalidPackage2 = 0x32,
+ ErrorReason_InvalidBct = 0x33,
+ ErrorReason_InvalidGpt = 0x34,
+ ErrorReason_FailedToTransitionToSafeMode = 0x35,
+ ErrorReason_ActivityMonitorInterrupt = 0x36,
+
+ ErrorReason_KernelPanic = 0x40,
+ };
+
+ enum ErrorColor {
+ ErrorColor_Black = 0x000,
+
+ ErrorColor_Red = 0x00F,
+ ErrorColor_Yellow = 0x0FF,
+ ErrorColor_Orange = 0x07F,
+ ErrorColor_Blue = 0xF00,
+ ErrorColor_LightBlue = 0xFF0,
+ ErrorColor_Pink = 0xF7F,
+ ErrorColor_Purple = 0xF0A,
+ };
+
+ enum ErrorInfo {
+ ErrorInfo_ReasonMask = 0xFF,
+ ErrorInfo_ColorShift = 20,
+
+ #define MAKE_ERROR_INFO(_COLOR_, _DESC_) ((static_cast(ErrorColor_##_COLOR_) << ErrorInfo_ColorShift) | (ErrorReason_##_DESC_))
+
+ ErrorInfo_None = MAKE_ERROR_INFO(Black, None),
+
+ ErrorInfo_InvalidPackage2Signature = MAKE_ERROR_INFO(Blue, InvalidPackage2Signature),
+ ErrorInfo_InvalidPackage2Meta = MAKE_ERROR_INFO(Blue, InvalidPackage2Meta),
+ ErrorInfo_InvalidPackage2Version = MAKE_ERROR_INFO(Blue, InvalidPackage2Version),
+ ErrorInfo_InvalidPackage2Payload = MAKE_ERROR_INFO(Blue, InvalidPackage2Payload),
+
+ ErrorInfo_UnknownSmc = MAKE_ERROR_INFO(LightBlue, UnknownSmc),
+
+ ErrorInfo_UnknownAbort = MAKE_ERROR_INFO(Yellow, UnknownAbort),
+
+ ErrorInfo_InvalidCoreContext = MAKE_ERROR_INFO(Pink, InvalidCoreContext),
+ ErrorInfo_InvalidSecurityEngineStickyBits = MAKE_ERROR_INFO(Pink, InvalidSecurityEngineStickyBits),
+ ErrorInfo_UnexpectedReset = MAKE_ERROR_INFO(Pink, UnexpectedReset),
+
+ ErrorInfo_Exception = MAKE_ERROR_INFO(Orange, Exception),
+
+ ErrorInfo_TransitionToSafeMode = MAKE_ERROR_INFO(Black, TransitionToSafeMode),
+ ErrorInfo_SecureInitializerReboot = MAKE_ERROR_INFO(Black, SecureInitializerReboot),
+
+ ErrorInfo_SdmmcError = MAKE_ERROR_INFO(Purple, SdmmcError),
+ ErrorInfo_InvalidDramId = MAKE_ERROR_INFO(Purple, InvalidDramId),
+ ErrorInfo_InvalidPackage2 = MAKE_ERROR_INFO(Purple, InvalidPackage2),
+ ErrorInfo_InvalidBct = MAKE_ERROR_INFO(Purple, InvalidBct),
+ ErrorInfo_InvalidGpt = MAKE_ERROR_INFO(Purple, InvalidGpt),
+ ErrorInfo_FailedToTransitionToSafeMode = MAKE_ERROR_INFO(Purple, FailedToTransitionToSafeMode),
+ ErrorInfo_ActivityMonitorInterrupt = MAKE_ERROR_INFO(Purple, ActivityMonitorInterrupt),
+
+ #undef MAKE_ERROR_INFO
+ };
+
+ constexpr inline ErrorReason GetErrorReason(u32 info) {
+ return static_cast(info & ErrorInfo_ReasonMask);
+ }
+
+ constexpr inline ErrorInfo MakeKernelPanicResetInfo(u32 color) {
+ return static_cast((color << ErrorInfo_ColorShift) | (ErrorReason_KernelPanic));
+ }
+
+ #define PKG1_SECURE_MONITOR_PMC_ERROR_SCRATCH (0x840)
+
+}
diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp
new file mode 100644
index 000000000..711f36ba4
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::pkg1 {
+
+ enum KeyGeneration : int {
+
+ KeyGeneration_1_0_0 = 0x00,
+ KeyGeneration_3_0_0 = 0x01,
+ KeyGeneration_3_0_1 = 0x02,
+ KeyGeneration_4_0_0 = 0x03,
+ KeyGeneration_5_0_0 = 0x04,
+ KeyGeneration_6_0_0 = 0x05,
+ KeyGeneration_6_2_0 = 0x06,
+ KeyGeneration_7_0_0 = 0x07,
+ KeyGeneration_8_1_0 = 0x08,
+ KeyGeneration_9_0_0 = 0x09,
+ KeyGeneration_9_1_0 = 0x0A,
+
+ KeyGeneration_Count,
+
+ KeyGeneration_Current = KeyGeneration_Count - 1,
+
+ KeyGeneration_Min = 0x00,
+ KeyGeneration_Max = 0x20,
+ };
+ static_assert(KeyGeneration_Count <= KeyGeneration_Max);
+
+ constexpr inline const int OldMasterKeyCount = KeyGeneration_Count - 1;
+ constexpr inline const int OldDeviceMasterKeyCount = KeyGeneration_Count - KeyGeneration_4_0_0;
+
+}
diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp
new file mode 100644
index 000000000..614aa5a38
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::pkg1 {
+
+ enum AesKeySlot {
+ AesKeySlot_UserStart = 0,
+
+ AesKeySlot_TzramSave = 2,
+
+ AesKeySlot_UserLast = 5,
+ AesKeySlot_UserEnd = AesKeySlot_UserLast + 1,
+
+ AesKeySlot_SecmonStart = 8,
+
+ AesKeySlot_Temporary = 8,
+ AesKeySlot_Smc = 9,
+ AesKeySlot_RandomForUserWrap = 10,
+ AesKeySlot_RandomForKeyStorageWrap = 11,
+ AesKeySlot_DeviceMaster = 12,
+ AesKeySlot_Master = 13,
+ AesKeySlot_Device = 15,
+
+ AesKeySlot_SecmonEnd = 16,
+
+ /* Used only during boot. */
+ AesKeySlot_Tsec = 12,
+ AesKeySlot_TsecRoot = 13,
+ AesKeySlot_SecureBoot = 14,
+ AesKeySlot_SecureStorage = 15,
+
+ AesKeySlot_MasterKek = 13,
+ AesKeySlot_DeviceMasterKeySourceKek = 14,
+ };
+
+ enum RsaKeySlot {
+ RsaKeySlot_Temporary = 0,
+ RsaKeySlot_PrivateKey = 1,
+ };
+
+}
diff --git a/libraries/libexosphere/include/exosphere/pmc.hpp b/libraries/libexosphere/include/exosphere/pmc.hpp
new file mode 100644
index 000000000..d1e2091b4
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/pmc.hpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::pmc {
+
+ enum SecureRegister {
+ SecureRegister_Other = (1 << 0),
+ SecureRegister_DramParameters = (1 << 1),
+ SecureRegister_ResetVector = (1 << 2),
+ SecureRegister_Carveout = (1 << 3),
+ SecureRegister_CmacWrite = (1 << 4),
+ SecureRegister_CmacRead = (1 << 5),
+ SecureRegister_KeySourceWrite = (1 << 6),
+ SecureRegister_KeySourceRead = (1 << 7),
+ SecureRegister_Srk = (1 << 8),
+
+ SecureRegister_CmacReadWrite = SecureRegister_CmacRead | SecureRegister_CmacWrite,
+ SecureRegister_KeySourceReadWrite = SecureRegister_KeySourceRead | SecureRegister_KeySourceWrite,
+ };
+
+ void SetRegisterAddress(uintptr_t address);
+
+ void InitializeRandomScratch();
+
+ void LockSecureRegister(SecureRegister reg);
+
+ enum class LockState {
+ Locked = 0,
+ NotLocked = 1,
+ PartiallyLocked = 2,
+ };
+
+ LockState GetSecureRegisterLockState(SecureRegister reg);
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/pmic.hpp b/libraries/libexosphere/include/exosphere/pmic.hpp
new file mode 100644
index 000000000..46b10372b
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/pmic.hpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::pmic {
+
+ enum Regulator {
+ /* Erista regulators. */
+ Regulator_Erista_Max77621 = 0, /* Device code 0x3A000001 */
+
+ /* Mariko regulators. */
+ Regulator_Mariko_Max77812_A = 1, /* Device code 0x3A000002 */
+ Regulator_Mariko_Max77812_B = 2, /* Device code 0x3A000006 */
+ };
+
+ void EnableVddCpu(Regulator regulator);
+ void DisableVddCpu(Regulator regulator);
+
+}
\ No newline at end of file
diff --git a/libraries/libexosphere/include/exosphere/reg.hpp b/libraries/libexosphere/include/exosphere/reg.hpp
new file mode 100644
index 000000000..a16d66193
--- /dev/null
+++ b/libraries/libexosphere/include/exosphere/reg.hpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::reg {
+
+ using BitsValue = std::tuple;
+ using BitsMask = std::tuple;
+
+ constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return std::get<0>(v); }
+ constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return std::get<0>(v); }
+ constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return std::get<1>(v); }
+ constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return std::get<1>(v); }
+ constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return std::get<2>(v); }
+
+ constexpr ALWAYS_INLINE u32 EncodeMask(const BitsMask v) {
+ return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(v);
+ }
+
+ constexpr ALWAYS_INLINE u32 EncodeMask(const BitsValue v) {
+ return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(v);
+ }
+
+ constexpr ALWAYS_INLINE u32 EncodeValue(const BitsValue v) {
+ return ((GetValue(v) << GetOffset(v)) & EncodeMask(v));
+ }
+
+ template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...))
+ constexpr ALWAYS_INLINE u32 Encode(const Values... values) {
+ return (EncodeValue(values) | ...);
+ }
+
+ ALWAYS_INLINE void Write(volatile u32 *reg, u32 val) { *reg = val; }
+ ALWAYS_INLINE void Write(volatile u32 ®, u32 val) { reg = val; }
+ ALWAYS_INLINE void Write(uintptr_t reg, u32 val) { Write(reinterpret_cast(reg), val); }
+
+ template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE void Write(volatile u32 *reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); }
+
+ template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE void Write(volatile u32 ®, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); }
+
+ template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE void Write(uintptr_t reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); }
+
+ ALWAYS_INLINE u32 Read(volatile u32 *reg) { return *reg; }
+ ALWAYS_INLINE u32 Read(volatile u32 ®) { return reg; }
+ ALWAYS_INLINE u32 Read(uintptr_t reg) { return Read(reinterpret_cast(reg)); }
+
+ ALWAYS_INLINE u32 Read(volatile u32 *reg, u32 mask) { return *reg & mask; }
+ ALWAYS_INLINE u32 Read(volatile u32 ®, u32 mask) { return reg & mask; }
+ ALWAYS_INLINE u32 Read(uintptr_t reg, u32 mask) { return Read(reinterpret_cast(reg), mask); }
+
+ template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE u32 Read(volatile u32 *reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); }
+
+ template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE u32 Read(volatile u32 ®, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); }
+
+ template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE u32 Read(uintptr_t reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); }
+
+ template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE bool HasValue(volatile u32 *reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); }
+
+ template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE bool HasValue(volatile u32 ®, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); }
+
+ template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE bool HasValue(uintptr_t reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); }
+
+ ALWAYS_INLINE void ReadWrite(volatile u32 *reg, u32 val, u32 mask) { *reg = (*reg & (~mask)) | (val & mask); }
+ ALWAYS_INLINE void ReadWrite(volatile u32 ®, u32 val, u32 mask) { reg = ( reg & (~mask)) | (val & mask); }
+ ALWAYS_INLINE void ReadWrite(uintptr_t reg, u32 val, u32 mask) { ReadWrite(reinterpret_cast(reg), val, mask); }
+
+ template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE void ReadWrite(volatile u32 *reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); }
+
+ template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...))
+ ALWAYS_INLINE void ReadWrite(volatile u32 ®, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); }
+
+ template