diff --git a/Makefile b/Makefile
index 2e3302b..7fbc965 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@
$(MAKE) -C thermosphere all
exosphere: thermosphere
- $(MAKE) -C exosphere2 all
+ $(MAKE) -C exosphere all
stratosphere: exosphere libraries
$(MAKE) -C stratosphere all
@@ -120,11 +120,11 @@
cp sept/sept-primary/sept-primary.elf atmosphere-$(AMSVER)-debug/sept-primary.elf
cp sept/sept-secondary/sept-secondary.elf atmosphere-$(AMSVER)-debug/sept-secondary.elf
cp sept/sept-secondary/key_derivation/key_derivation.elf atmosphere-$(AMSVER)-debug/sept-secondary-key-derivation.elf
- cp exosphere2/loader_stub/loader_stub.elf atmosphere-$(AMSVER)-debug/exosphere-loader-stub.elf
- cp exosphere2/program/program.elf atmosphere-$(AMSVER)-debug/exosphere-program.elf
- cp exosphere2/warmboot/warmboot.elf atmosphere-$(AMSVER)-debug/exosphere-warmboot.elf
- cp exosphere2/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf
- cp exosphere2/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf
+ cp exosphere/loader_stub/loader_stub.elf atmosphere-$(AMSVER)-debug/exosphere-loader-stub.elf
+ cp exosphere/program/program.elf atmosphere-$(AMSVER)-debug/exosphere-program.elf
+ cp exosphere/warmboot/warmboot.elf atmosphere-$(AMSVER)-debug/exosphere-warmboot.elf
+ cp exosphere/program/sc7fw/sc7fw.elf atmosphere-$(AMSVER)-debug/exosphere-sc7fw.elf
+ cp exosphere/program/rebootstub/rebootstub.elf atmosphere-$(AMSVER)-debug/exosphere-rebootstub.elf
cp mesosphere/kernel_ldr/kernel_ldr.elf atmosphere-$(AMSVER)-debug/kernel_ldr.elf
cp stratosphere/ams_mitm/ams_mitm.elf atmosphere-$(AMSVER)-debug/ams_mitm.elf
cp stratosphere/boot/boot.elf atmosphere-$(AMSVER)-debug/boot.elf
diff --git a/exosphere/Makefile b/exosphere/Makefile
new file mode 100644
index 0000000..8017acd
--- /dev/null
+++ b/exosphere/Makefile
@@ -0,0 +1,43 @@
+TARGETS := exosphere.bin warmboot.bin program.lz4
+CLEAN_TARGETS := exosphere-clean program-clean boot_code-clean warmboot-clean
+
+SUBFOLDERS := $(MODULES)
+
+all: exosphere.bin warmboot.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
+ @printf LENY >> exosphere.bin
+ @echo "Built exosphere.bin..."
+
+program.lz4:
+ $(MAKE) -C program
+ @cp program/program.lz4 program.lz4
+ @cp program/boot_code.lz4 boot_code.lz4
+
+warmboot.bin:
+ $(MAKE) -C warmboot
+ @cp warmboot/warmboot.bin warmboot.bin
+
+boot_code.lz4: program.lz4
+
+exosphere-clean:
+ $(MAKE) -C loader_stub clean
+ @rm -f exosphere.bin
+
+program-clean:
+ $(MAKE) -C program clean
+ @rm -f program.lz4
+
+warmboot-clean:
+ $(MAKE) -C warmboot clean
+ @rm -f warmboot.bin
+
+boot_code-clean:
+ @rm -f boot_code.lz4
+
+.PHONY: all clean $(CLEAN_TARGETS)
diff --git a/exosphere/loader_stub/Makefile b/exosphere/loader_stub/Makefile
new file mode 100644
index 0000000..a5725e5
--- /dev/null
+++ b/exosphere/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/exosphere/loader_stub/loader_stub.ld b/exosphere/loader_stub/loader_stub.ld
new file mode 100644
index 0000000..bb8239d
--- /dev/null
+++ b/exosphere/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/exosphere/loader_stub/loader_stub.specs b/exosphere/loader_stub/loader_stub.specs
new file mode 100644
index 0000000..0398476
--- /dev/null
+++ b/exosphere/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/exosphere/loader_stub/source/secmon_loader_error.cpp b/exosphere/loader_stub/source/secmon_loader_error.cpp
new file mode 100644
index 0000000..2f67b50
--- /dev/null
+++ b/exosphere/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/exosphere/loader_stub/source/secmon_loader_error.hpp b/exosphere/loader_stub/source/secmon_loader_error.hpp
new file mode 100644
index 0000000..6119907
--- /dev/null
+++ b/exosphere/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/exosphere/loader_stub/source/secmon_loader_main.cpp b/exosphere/loader_stub/source/secmon_loader_main.cpp
new file mode 100644
index 0000000..26b1944
--- /dev/null
+++ b/exosphere/loader_stub/source/secmon_loader_main.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_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/exosphere/loader_stub/source/secmon_loader_uncompress.cpp b/exosphere/loader_stub/source/secmon_loader_uncompress.cpp
new file mode 100644
index 0000000..a58e3c5
--- /dev/null
+++ b/exosphere/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/exosphere/loader_stub/source/secmon_loader_uncompress.hpp b/exosphere/loader_stub/source/secmon_loader_uncompress.hpp
new file mode 100644
index 0000000..b820209
--- /dev/null
+++ b/exosphere/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/exosphere/loader_stub/source/start.s b/exosphere/loader_stub/source/start.s
new file mode 100644
index 0000000..7476605
--- /dev/null
+++ b/exosphere/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, =0x7C020000
+ 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/exosphere/program/Makefile b/exosphere/program/Makefile
new file mode 100644
index 0000000..23b18af
--- /dev/null
+++ b/exosphere/program/Makefile
@@ -0,0 +1,138 @@
+#---------------------------------------------------------------------------------
+# 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)/sc7fw \
+ $(TOPDIR)/rebootstub
+
+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 := sc7fw.bin rebootstub.bin
+
+#---------------------------------------------------------------------------------
+# 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 check_firmwares
+ @[ -d $@ ] || mkdir -p $@
+ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+
+check_libexo:
+ @$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm64
+
+check_firmwares:
+ @$(MAKE) -C $(TOPDIR)/sc7fw all
+ @$(MAKE) -C $(TOPDIR)/rebootstub all
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @$(MAKE) -C $(TOPDIR)/sc7fw clean
+ @$(MAKE) -C $(TOPDIR)/rebootstub 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/exosphere/program/program.ld b/exosphere/program/program.ld
new file mode 100644
index 0000000..8ca3998
--- /dev/null
+++ b/exosphere/program/program.ld
@@ -0,0 +1,285 @@
+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 : ORIGIN = 0x1F01C0000, LENGTH = 8K
+
+ 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_make_page_table.o(.text*))
+ *(.crt0.rodata*)
+ secmon_crt0_cpp.o(.rodata*)
+ secmon_make_page_table.o(.rodata*)
+ *(.crt0.data*)
+ secmon_crt0_cpp.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_volatile_data : {
+ KEEP (*(.volatile_keys .volatile_keys.*))
+ } >tzram_boot AT>glob
+
+ .tzram_boot_volatile_data.fill : {
+ FILL(0x00000000);
+ . = ORIGIN(tzram_boot) + 0x7FF;
+ BYTE(0x00);
+ } >tzram_boot AT>glob
+
+ .tzram_boot_code :
+ {
+ KEEP(secmon_main.o(.text*))
+ KEEP(secmon_boot_functions.o(.text*))
+ KEEP (secmon_boot_cache.o(.text*))
+ KEEP(secmon_boot_config.o(.text*))
+ KEEP(secmon_boot_setup.o(.text*))
+ KEEP(secmon_package2.o(.text*))
+ secmon_main.o(.rodata*)
+ secmon_boot_functions.o(.rodata*)
+ secmon_boot_cache.o(.rodata*)
+ secmon_boot_config.o(.rodata*)
+ secmon_boot_setup.o(.rodata*)
+ secmon_package2.o(.rodata*)
+ secmon_main.o(.data*)
+ secmon_boot_functions.o(.data*)
+ secmon_boot_cache.o(.data*)
+ secmon_boot_config.o(.data*)
+ secmon_boot_setup.o(.data*)
+ secmon_package2.o(.data*)
+ . = ALIGN(8);
+ } >tzram_boot AT>glob
+
+ .tzram_boot_code.bss :
+ {
+ __boot_bss_start__ = ABSOLUTE(.);
+ secmon_main.o(.bss* COMMON)
+ secmon_boot_functions.o(.bss* COMMON)
+ secmon_boot_cache.o(.bss* COMMON)
+ secmon_boot_config.o(.bss* COMMON)
+ secmon_boot_setup.o(.bss* COMMON)
+ secmon_package2.o(.bss* COMMON)
+ __boot_bss_end__ = ABSOLUTE(.);
+ } >tzram_boot AT>glob
+
+ .tzram_boot_code.fill :
+ {
+ FILL(0x00000000);
+ . = ORIGIN(tzram_boot) + LENGTH(tzram_boot) - 1;
+ BYTE(0x00);
+ } > tzram_boot 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 ORIGIN(main) + SIZEOF(.vectors) + SIZEOF(.warmboot) :
+ {
+ *(.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/exosphere/program/program.specs b/exosphere/program/program.specs
new file mode 100644
index 0000000..22b7899
--- /dev/null
+++ b/exosphere/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/exosphere/program/rebootstub/Makefile b/exosphere/program/rebootstub/Makefile
new file mode 100644
index 0000000..31439e8
--- /dev/null
+++ b/exosphere/program/rebootstub/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/exosphere/program/rebootstub/rebootstub.ld b/exosphere/program/rebootstub/rebootstub.ld
new file mode 100644
index 0000000..c37c755
--- /dev/null
+++ b/exosphere/program/rebootstub/rebootstub.ld
@@ -0,0 +1,183 @@
+OUTPUT_ARCH(arm)
+ENTRY(reset)
+
+MEMORY
+{
+ NULL : ORIGIN = 0, LENGTH = 4K
+ rebootstub : ORIGIN = 0x4003F000, LENGTH = 4K
+}
+
+
+SECTIONS
+{
+ /* =========== CODE section =========== */
+ PROVIDE(__start__ = ORIGIN(rebootstub));
+ . = __start__;
+ __code_start = . ;
+
+ .vectors :
+ {
+ KEEP (*(.vectors .vectors.*))
+ . = ALIGN(8);
+ } >rebootstub
+
+ .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);
+ } >rebootstub
+
+ .init :
+ {
+ KEEP( *(.init) )
+ . = ALIGN(8);
+ } >rebootstub
+
+ .plt :
+ {
+ *(.plt)
+ *(.iplt)
+ . = ALIGN(8);
+ } >rebootstub
+
+ .fini :
+ {
+ KEEP( *(.fini) )
+ . = ALIGN(8);
+ } >rebootstub
+
+
+ /* =========== RODATA section =========== */
+ . = ALIGN(8);
+ __rodata_start = . ;
+
+ .rodata :
+ {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ . = ALIGN(8);
+ } >rebootstub
+
+ .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >rebootstub
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >rebootstub
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >rebootstub
+ .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >rebootstub
+
+ .hash : { *(.hash) } >rebootstub
+
+ /* =========== DATA section =========== */
+ . = ALIGN(8);
+ __data_start = . ;
+
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >rebootstub
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >rebootstub
+ .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >rebootstub
+ .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >rebootstub
+
+ .preinit_array ALIGN(8) :
+ {
+ PROVIDE (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE (__preinit_array_end = .);
+ } >rebootstub
+
+ .init_array ALIGN(8) :
+ {
+ PROVIDE (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE (__init_array_end = .);
+ } >rebootstub
+
+ .fini_array ALIGN(8) :
+ {
+ PROVIDE (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ PROVIDE (__fini_array_end = .);
+ } >rebootstub
+
+ .ctors ALIGN(8) :
+ {
+ KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ } >rebootstub
+
+ .dtors ALIGN(8) :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ } >rebootstub
+
+ __got_start__ = .;
+
+ .got : { *(.got) *(.igot) } >rebootstub
+ .got.plt : { *(.got.plt) *(.igot.plt) } >rebootstub
+
+ __got_end__ = .;
+
+ .data ALIGN(8) :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ } >rebootstub
+
+ __bss_start__ = .;
+ .bss ALIGN(8) :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ . = ALIGN(16);
+ } >rebootstub
+ __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/exosphere/program/rebootstub/rebootstub.specs b/exosphere/program/rebootstub/rebootstub.specs
new file mode 100644
index 0000000..4e41b16
--- /dev/null
+++ b/exosphere/program/rebootstub/rebootstub.specs
@@ -0,0 +1,7 @@
+%rename link old_link
+
+*link:
+%(old_link) -T %:getenv(TOPDIR /rebootstub.ld) --gc-sections --nmagic -nostdlib -nostartfiles
+
+*startfile:
+crti%O%s crtbegin%O%s
diff --git a/exosphere/program/rebootstub/source/rebootstub_exception_vectors.s b/exosphere/program/rebootstub/source/rebootstub_exception_vectors.s
new file mode 100644
index 0000000..eaf62f1
--- /dev/null
+++ b/exosphere/program/rebootstub/source/rebootstub_exception_vectors.s
@@ -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 .
+ */
+
+.section .vectors, "ax", %progbits
+.align 3
+.global reset
+reset:
+ b _ZN3ams10rebootstub4MainEv
+
+.global _ZN3ams10rebootstub10RebootTypeE
+_ZN3ams10rebootstub10RebootTypeE:
+.word 0x00000001
\ No newline at end of file
diff --git a/exosphere/program/rebootstub/source/rebootstub_main.s b/exosphere/program/rebootstub/source/rebootstub_main.s
new file mode 100644
index 0000000..03e3ec6
--- /dev/null
+++ b/exosphere/program/rebootstub/source/rebootstub_main.s
@@ -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 .
+ */
+
+.section .text._ZN3ams10rebootstub4MainEv, "ax", %progbits
+.align 3
+.global _ZN3ams10rebootstub4MainEv
+_ZN3ams10rebootstub4MainEv:
+ /* Get the reboot type. */
+ ldr r0, =_ZN3ams10rebootstub10RebootTypeE
+ ldr r0, [r0]
+
+ /* If the reboot type is power off, perform a power off. */
+ cmp r0, #0
+ beq _ZN3ams10rebootstub8PowerOffEv
+
+ /* Otherwise, clear all registers jump to the reboot payload in iram. */
+ ldr r0, =0x52425430 /* RBT0 */
+ mov r1, #0
+ mov r2, #0
+ mov r3, #0
+ mov r4, #0
+ mov r5, #0
+ mov r5, #0
+ mov r6, #0
+ mov r7, #0
+ mov r8, #0
+ mov r9, #0
+ mov r10, #0
+ mov r11, #0
+ mov r12, #0
+ mov r9, #0
+ mov lr, #0
+ ldr sp, =0x40010000
+ ldr pc, =0x40010000
+
+ /* Infinite loop. */
+ 1: b 1b
\ No newline at end of file
diff --git a/exosphere/program/rebootstub/source/rebootstub_power_off.cpp b/exosphere/program/rebootstub/source/rebootstub_power_off.cpp
new file mode 100644
index 0000000..97d0e21
--- /dev/null
+++ b/exosphere/program/rebootstub/source/rebootstub_power_off.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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::rebootstub {
+
+ NORETURN void Halt() {
+ while (true) {
+ reg::Write(secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress() + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
+ FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_JTAG, ENABLED));
+ }
+
+ __builtin_unreachable();
+ }
+
+ NORETURN void PowerOff() {
+ /* Ensure that i2c5 is usable. */
+ clkrst::EnableI2c5Clock();
+
+ /* Initialize i2c5. */
+ i2c::Initialize(i2c::Port_5);
+
+ /* Stop rtc alarms. */
+ rtc::StopAlarm();
+
+ /* Perform a pmic power off. */
+ pmic::PowerOff();
+
+ /* Halt the bpmp. */
+ Halt();
+
+ /* This can never be reached. */
+ __builtin_unreachable();
+ }
+
+}
+
+namespace ams::diag {
+
+ void AbortImpl() {
+ /* Halt the bpmp. */
+ rebootstub::Halt();
+
+ /* This can never be reached. */
+ __builtin_unreachable();
+ }
+
+}
diff --git a/exosphere/program/sc7fw/Makefile b/exosphere/program/sc7fw/Makefile
new file mode 100644
index 0000000..31439e8
--- /dev/null
+++ b/exosphere/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/exosphere/program/sc7fw/sc7fw.ld b/exosphere/program/sc7fw/sc7fw.ld
new file mode 100644
index 0000000..794edc3
--- /dev/null
+++ b/exosphere/program/sc7fw/sc7fw.ld
@@ -0,0 +1,183 @@
+OUTPUT_ARCH(arm)
+ENTRY(reset)
+
+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/exosphere/program/sc7fw/sc7fw.specs b/exosphere/program/sc7fw/sc7fw.specs
new file mode 100644
index 0000000..13455dc
--- /dev/null
+++ b/exosphere/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/exosphere/program/sc7fw/source/sc7fw_dram.cpp b/exosphere/program/sc7fw/source/sc7fw_dram.cpp
new file mode 100644
index 0000000..b477a7d
--- /dev/null
+++ b/exosphere/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 (fuse::GetSocType() == fuse::SocType_Erista) {
+ 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/exosphere/program/sc7fw/source/sc7fw_dram.hpp b/exosphere/program/sc7fw/source/sc7fw_dram.hpp
new file mode 100644
index 0000000..dca0a47
--- /dev/null
+++ b/exosphere/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/exosphere/program/sc7fw/source/sc7fw_exception_vectors.s b/exosphere/program/sc7fw/source/sc7fw_exception_vectors.s
new file mode 100644
index 0000000..ca8c3b7
--- /dev/null
+++ b/exosphere/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/exosphere/program/sc7fw/source/sc7fw_main.cpp b/exosphere/program/sc7fw/source/sc7fw_main.cpp
new file mode 100644
index 0000000..0a08258
--- /dev/null
+++ b/exosphere/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/exosphere/program/sc7fw/source/sc7fw_start.s b/exosphere/program/sc7fw/source/sc7fw_start.s
new file mode 100644
index 0000000..02761c1
--- /dev/null
+++ b/exosphere/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/exosphere/program/sc7fw/source/sc7fw_util.hpp b/exosphere/program/sc7fw/source/sc7fw_util.hpp
new file mode 100644
index 0000000..2202deb
--- /dev/null
+++ b/exosphere/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/exosphere/program/sc7fw/source/sc7fw_util_asm.s b/exosphere/program/sc7fw/source/sc7fw_util_asm.s
new file mode 100644
index 0000000..249c5b0
--- /dev/null
+++ b/exosphere/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/exosphere/program/source/boot/secmon_boot.hpp b/exosphere/program/source/boot/secmon_boot.hpp
new file mode 100644
index 0000000..469aa88
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_boot.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::secmon::boot {
+
+ void MakePageTable();
+ void UnmapPhysicalIdentityMapping();
+ void UnmapDram();
+
+ void InitializeColdBoot();
+
+ bool VerifySignature(void *sig, size_t sig_size, const void *mod, size_t mod_size, const void *msg, size_t msg_size);
+ bool VerifyHash(const void *hash, uintptr_t msg, size_t msg_size);
+
+ bool VerifyBootConfigSignature(pkg1::BootConfig &bc, const void *mod, size_t mod_size);
+ bool VerifyBootConfigEcid(const pkg1::BootConfig &bc);
+
+ void CalculatePackage2Hash(se::Sha256Hash *dst, const pkg2::Package2Meta &meta, uintptr_t package2_start);
+
+ bool VerifyPackage2Signature(pkg2::Package2Header &header, const void *mod, size_t mod_size);
+ void DecryptPackage2(void *dst, size_t dst_size, const void *src, size_t src_size, const void *key, size_t key_size, const void *iv, size_t iv_size, u8 key_generation);
+
+ bool VerifyPackage2Meta(const pkg2::Package2Meta &meta);
+ bool VerifyPackage2Version(const pkg2::Package2Meta &meta);
+ bool VerifyPackage2Payloads(const pkg2::Package2Meta &meta, uintptr_t payload_address);
+
+}
\ No newline at end of file
diff --git a/exosphere/program/source/boot/secmon_boot_cache.cpp b/exosphere/program/source/boot/secmon_boot_cache.cpp
new file mode 100644
index 0000000..f2d1f8e
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_boot_cache.cpp
@@ -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
+#include "secmon_boot_cache.hpp"
+
+namespace ams::secmon::boot {
+
+ #include "../secmon_cache_impl.inc"
+
+}
\ No newline at end of file
diff --git a/exosphere/program/source/boot/secmon_boot_cache.hpp b/exosphere/program/source/boot/secmon_boot_cache.hpp
new file mode 100644
index 0000000..60d5b42
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_boot_cache.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 {
+
+ #include "../secmon_cache.inc"
+
+}
\ No newline at end of file
diff --git a/exosphere/program/source/boot/secmon_boot_config.cpp b/exosphere/program/source/boot/secmon_boot_config.cpp
new file mode 100644
index 0000000..c444862
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_boot_config.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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"
+
+namespace ams::secmon::boot {
+
+ bool VerifyBootConfigSignature(pkg1::BootConfig &bc, const void *mod, size_t mod_size) {
+ return VerifySignature(std::addressof(bc.signature), sizeof(bc.signature), mod, mod_size, std::addressof(bc.signed_data), sizeof(bc.signed_data));
+ }
+
+ bool VerifyBootConfigEcid(const pkg1::BootConfig &bc) {
+ /* Get the ecid. */
+ br::BootEcid ecid;
+ fuse::GetEcid(std::addressof(ecid));
+
+ /* Verify it matches. */
+ return crypto::IsSameBytes(std::addressof(ecid), bc.signed_data.ecid, sizeof(ecid));
+ }
+
+}
diff --git a/exosphere/program/source/boot/secmon_boot_functions.cpp b/exosphere/program/source/boot/secmon_boot_functions.cpp
new file mode 100644
index 0000000..08e42c8
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_boot_functions.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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_boot.hpp"
+#include "secmon_boot_cache.hpp"
+#include "secmon_boot_functions.hpp"
+
+namespace ams::secmon::boot {
+
+ namespace {
+
+ constexpr inline uintptr_t SYSCTR0 = MemoryRegionVirtualDeviceSysCtr0.GetAddress();
+
+ NOINLINE void DecryptPayload(uintptr_t dst, uintptr_t src, size_t size, const void *iv, size_t iv_size, u8 key_generation) {
+ secmon::boot::DecryptPackage2(reinterpret_cast(dst), size, reinterpret_cast(src), size, secmon::boot::GetPackage2AesKey(), crypto::AesEncryptor128::KeySize, iv, iv_size, key_generation);
+ }
+
+ u32 GetChipId() {
+ constexpr u32 ChipIdErista = 0x210;
+ constexpr u32 ChipIdMariko = 0x214;
+
+ switch (GetSocType()) {
+ case fuse::SocType_Erista: return ChipIdErista;
+ case fuse::SocType_Mariko: return ChipIdMariko;
+ AMS_UNREACHABLE_DEFAULT_CASE();
+ }
+ }
+
+ }
+
+ void CheckVerifyResult(bool verify_result, pkg1::ErrorInfo error_info, const char *message) {
+ if (!verify_result) {
+ secmon::SetError(error_info);
+ AMS_ABORT(message);
+ }
+ }
+
+ void ClearIram() {
+ /* Clear the boot code image from where it was loaded in IRAM. */
+ util::ClearMemory(MemoryRegionPhysicalIramBootCodeImage.GetPointer(), MemoryRegionPhysicalIramBootCodeImage.GetSize());
+ }
+
+ void WaitForNxBootloader(const pkg1::SecureMonitorParameters ¶ms, pkg1::BootloaderState state) {
+ /* Check NX Bootloader's state once per microsecond until it's advanced enough. */
+ while (params.bootloader_state < state) {
+ util::WaitMicroSeconds(1);
+ }
+ }
+
+ void LoadBootConfig(const void *src) {
+ pkg1::BootConfig * const dst = secmon::impl::GetBootConfigStorage();
+
+ if (pkg1::IsProduction()) {
+ std::memset(dst, 0, sizeof(*dst));
+ } else {
+ hw::FlushDataCache(src, sizeof(*dst));
+ hw::DataSynchronizationBarrierInnerShareable();
+ std::memcpy(dst, src, sizeof(*dst));
+ }
+ }
+
+ void VerifyOrClearBootConfig() {
+ /* On production hardware, the boot config is already cleared. */
+ if (pkg1::IsProduction()) {
+ return;
+ }
+
+ pkg1::BootConfig * const bc = secmon::impl::GetBootConfigStorage();
+
+ /* Determine if the bc is valid for the device. */
+ bool valid_for_device = false;
+ {
+ const bool valid_signature = secmon::boot::VerifyBootConfigSignature(*bc, secmon::boot::GetBootConfigRsaModulus(), se::RsaSize);
+ if (valid_signature) {
+ valid_for_device = secmon::boot::VerifyBootConfigEcid(*bc);
+ }
+ }
+
+ /* If the boot config is not valid for the device, clear its signed data. */
+ if (!valid_for_device) {
+ util::ClearMemory(std::addressof(bc->signed_data), sizeof(bc->signed_data));
+ }
+ }
+
+ void EnableTsc(u64 initial_tsc_value) {
+ /* Write the initial value to the CNTCV registers. */
+ const u32 lo = static_cast(initial_tsc_value >> 0);
+ const u32 hi = static_cast(initial_tsc_value >> 32);
+
+ reg::Write(SYSCTR0 + SYSCTR0_CNTCV0, lo);
+ reg::Write(SYSCTR0 + SYSCTR0_CNTCV1, hi);
+
+ /* Configure the system counter control register. */
+ reg::Write(SYSCTR0 + SYSCTR0_CNTCR, SYSCTR0_REG_BITS_ENUM(CNTCR_HDBG, ENABLE),
+ SYSCTR0_REG_BITS_ENUM(CNTCR_EN, ENABLE));
+ }
+
+ void WriteGpuCarveoutMagicNumbers() {
+ /* Define the magic numbers. */
+ constexpr u32 GpuMagicNumber = 0xC0EDBBCC;
+ constexpr u32 SkuInfo = 0x83;
+ constexpr u32 HdcpMicroCodeVersion = 0x2;
+
+ /* Get our pointers. */
+ u32 *gpu_magic = MemoryRegionDramGpuCarveout.GetEndPointer() - (0x004 / sizeof(*gpu_magic));
+ u32 *tsec_magic = MemoryRegionDramGpuCarveout.GetEndPointer() - (0x100 / sizeof(*tsec_magic));
+
+ /* Write the gpu magic number. */
+ gpu_magic[0] = GpuMagicNumber;
+
+ /* Write the tsec magic numbers. */
+ tsec_magic[0] = SkuInfo;
+ tsec_magic[1] = HdcpMicroCodeVersion;
+ tsec_magic[2] = GetChipId();
+
+ /* Flush the magic numbers. */
+ hw::FlushDataCache(gpu_magic, 1 * sizeof(u32));
+ hw::FlushDataCache(tsec_magic, 3 * sizeof(u32));
+ hw::DataSynchronizationBarrierInnerShareable();
+ }
+
+ void UpdateBootConfigForPackage2Header(const pkg2::Package2Header &header) {
+ /* Check for all-zeroes signature. */
+ bool is_decrypted = header.signature[0] == 0;
+ is_decrypted &= crypto::IsSameBytes(header.signature, header.signature + 1, sizeof(header.signature) - 1);
+
+ /* Check for valid magic. */
+ is_decrypted &= crypto::IsSameBytes(header.meta.magic, pkg2::Package2Meta::Magic::String, sizeof(header.meta.magic));
+
+ /* Set the setting in boot config. */
+ secmon::impl::GetBootConfigStorage()->signed_data.SetPackage2Decrypted(is_decrypted);
+ }
+
+ void VerifyPackage2HeaderSignature(pkg2::Package2Header &header, bool verify) {
+ const u8 * const mod = secmon::boot::GetPackage2RsaModulus(pkg1::IsProductionForPublicKey());
+ const size_t mod_size = se::RsaSize;
+ if (verify) {
+ CheckVerifyResult(secmon::boot::VerifyPackage2Signature(header, mod, mod_size), pkg1::ErrorInfo_InvalidPackage2Signature, "package2 header sign verification failed");
+ }
+ }
+
+ void DecryptPackage2Header(pkg2::Package2Meta *dst, const pkg2::Package2Meta &src, bool encrypted) {
+ if (encrypted) {
+ constexpr int IvSize = 0x10;
+
+ /* Decrypt the header. */
+ DecryptPackage2(dst, sizeof(*dst), std::addressof(src), sizeof(src), secmon::boot::GetPackage2AesKey(), crypto::AesEncryptor128::KeySize, std::addressof(src), IvSize, src.GetKeyGeneration());
+
+ /* Copy back the iv, which encodes encrypted metadata. */
+ std::memcpy(dst, std::addressof(src), IvSize);
+ } else {
+ std::memcpy(dst, std::addressof(src), sizeof(*dst));
+ }
+ }
+
+ void VerifyPackage2Header(const pkg2::Package2Meta &meta) {
+ /* Validate the metadata. */
+ CheckVerifyResult(VerifyPackage2Meta(meta), pkg1::ErrorInfo_InvalidPackage2Meta, "package2 meta verification failed");
+
+ /* Validate the version. */
+ CheckVerifyResult(VerifyPackage2Version(meta), pkg1::ErrorInfo_InvalidPackage2Version, "package2 version verification failed");
+ }
+
+ void DecryptAndLoadPackage2Payloads(uintptr_t dst, const pkg2::Package2Meta &meta, uintptr_t src, bool encrypted) {
+ /* Get the key generation for crypto. */
+ const u8 key_generation = meta.GetKeyGeneration();
+ /* Decrypt or load each payload in order. */
+ for (int i = 0; i < pkg2::PayloadCount; ++i) {
+ if (encrypted) {
+ DecryptPayload(dst + meta.payload_offsets[i], src, meta.payload_sizes[i], meta.payload_ivs[i], sizeof(meta.payload_ivs[i]), key_generation);
+ } else {
+ std::memcpy(reinterpret_cast(dst + meta.payload_offsets[i]), reinterpret_cast(src), meta.payload_sizes[i]);
+ }
+
+ src += meta.payload_sizes[i];
+ }
+ }
+
+}
diff --git a/exosphere/program/source/boot/secmon_boot_functions.hpp b/exosphere/program/source/boot/secmon_boot_functions.hpp
new file mode 100644
index 0000000..c03a04b
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_boot_functions.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::secmon::boot {
+
+ void ClearIram();
+
+ void WaitForNxBootloader(const pkg1::SecureMonitorParameters ¶ms, pkg1::BootloaderState state);
+
+ void LoadBootConfig(const void *src);
+ void VerifyOrClearBootConfig();
+
+ void EnableTsc(u64 initial_tsc_value);
+
+ void WriteGpuCarveoutMagicNumbers();
+
+ void UpdateBootConfigForPackage2Header(const pkg2::Package2Header &header);
+ void VerifyPackage2HeaderSignature(pkg2::Package2Header &header, bool verify);
+ void DecryptPackage2Header(pkg2::Package2Meta *dst, const pkg2::Package2Meta &src, bool encrypted);
+
+ void VerifyPackage2Header(const pkg2::Package2Meta &meta);
+
+ void DecryptAndLoadPackage2Payloads(uintptr_t dst, const pkg2::Package2Meta &meta, uintptr_t src, bool encrypted);
+
+ void CheckVerifyResult(bool verify_result, pkg1::ErrorInfo error_info, const char *message);
+
+}
diff --git a/exosphere/program/source/boot/secmon_boot_key_data.s b/exosphere/program/source/boot/secmon_boot_key_data.s
new file mode 100644
index 0000000..2990ea3
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_boot_key_data.s
@@ -0,0 +1,77 @@
+/*
+ * 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 .volatile_keys._ZN3ams6secmon4boot15VolatileKeyDataE, "aw", %progbits
+.global _ZN3ams6secmon4boot15VolatileKeyDataE
+_ZN3ams6secmon4boot15VolatileKeyDataE:
+/* BootConfig Rsa Modulus. */
+.byte 0xB5, 0x96, 0x87, 0x31, 0x39, 0xAA, 0xBB, 0x3C, 0x28, 0xF3, 0xF0, 0x65, 0xF1, 0x50, 0x70, 0x64
+.byte 0xE6, 0x6C, 0x97, 0x50, 0xCD, 0xA6, 0xEE, 0xEA, 0xC3, 0x8F, 0xE6, 0xB5, 0x81, 0x54, 0x65, 0x33
+.byte 0x1B, 0x88, 0x4B, 0xCE, 0x9F, 0x53, 0xDF, 0xE4, 0xF6, 0xAD, 0xC3, 0x78, 0xD7, 0x3C, 0xD1, 0xDB
+.byte 0x27, 0x21, 0xA0, 0x24, 0x30, 0x2D, 0x98, 0x41, 0xA8, 0xDF, 0x50, 0x7D, 0xAB, 0xCE, 0x00, 0xD9
+.byte 0xCB, 0xAC, 0x8F, 0x37, 0xF5, 0x53, 0xE4, 0x97, 0x1F, 0x13, 0x3C, 0x19, 0xFF, 0x05, 0xA7, 0x3B
+.byte 0xF6, 0xF4, 0x01, 0xDE, 0xF0, 0xC3, 0x77, 0x7B, 0x83, 0xBA, 0xAF, 0x99, 0x30, 0x94, 0x87, 0x25
+.byte 0x4E, 0x54, 0x42, 0x3F, 0xAC, 0x27, 0xF9, 0xCC, 0x87, 0xDD, 0xAE, 0xF2, 0x54, 0xF3, 0x97, 0x49
+.byte 0xF4, 0xB0, 0xF8, 0x6D, 0xDA, 0x60, 0xE0, 0xFD, 0x57, 0xAE, 0x55, 0xA9, 0x76, 0xEA, 0x80, 0x24
+.byte 0xA0, 0x04, 0x7D, 0xBE, 0xD1, 0x81, 0xD3, 0x0C, 0x95, 0xCF, 0xB7, 0xE0, 0x2D, 0x21, 0x21, 0xFF
+.byte 0x97, 0x1E, 0xB3, 0xD7, 0x9F, 0xBB, 0x33, 0x0C, 0x23, 0xC5, 0x88, 0x4A, 0x33, 0xB9, 0xC9, 0x4E
+.byte 0x1E, 0x65, 0x51, 0x45, 0xDE, 0xF9, 0x64, 0x7C, 0xF0, 0xBF, 0x11, 0xB4, 0x93, 0x8D, 0x5D, 0xC6
+.byte 0xAB, 0x37, 0x9E, 0xE9, 0x39, 0xC1, 0xC8, 0xDB, 0xB9, 0xFE, 0x45, 0xCE, 0x7B, 0xDD, 0x72, 0xD9
+.byte 0x6F, 0x68, 0x13, 0xC0, 0x4B, 0xBA, 0x00, 0xF4, 0x1E, 0x89, 0x71, 0x91, 0x26, 0xA6, 0x46, 0x12
+.byte 0xDF, 0x29, 0x6B, 0xC2, 0x5A, 0x53, 0xAF, 0xB9, 0x5B, 0xFD, 0x13, 0x9F, 0xD1, 0x8A, 0x7C, 0xB5
+.byte 0x04, 0xFD, 0x69, 0xEA, 0x23, 0xB4, 0x6D, 0x16, 0x21, 0x98, 0x54, 0xB4, 0xDF, 0xE6, 0xAB, 0x93
+.byte 0x36, 0xB6, 0xD2, 0x43, 0xCF, 0x2B, 0x98, 0x1D, 0x45, 0xC9, 0xBB, 0x20, 0x42, 0xB1, 0x9D, 0x1D
+
+/* Package2 Development Rsa Modulus. */
+.byte 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99
+.byte 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7
+.byte 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6
+.byte 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38
+.byte 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83
+.byte 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0
+.byte 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6
+.byte 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50
+.byte 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7
+.byte 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42
+.byte 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E
+.byte 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8
+.byte 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7
+.byte 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4
+.byte 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46
+.byte 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B
+
+/* Package2 Production Rsa Modulus. */
+.byte 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59
+.byte 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE
+.byte 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5
+.byte 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74
+.byte 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48
+.byte 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE
+.byte 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32
+.byte 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A
+.byte 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B
+.byte 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3
+.byte 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9
+.byte 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47
+.byte 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D
+.byte 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2
+.byte 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF
+.byte 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F
+
+/* Package2 Aes Key Source. */
+.byte 0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7
diff --git a/exosphere/program/source/boot/secmon_boot_rsa.cpp b/exosphere/program/source/boot/secmon_boot_rsa.cpp
new file mode 100644
index 0000000..344196c
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_boot_rsa.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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"
+
+namespace ams::secmon::boot {
+
+ namespace {
+
+ constinit const u8 RsaPublicKeyExponent[] = {
+ 0x00, 0x01, 0x00, 0x01,
+ };
+
+ constexpr inline u8 TailMagic = 0xBC;
+
+ bool VerifyRsaPssSha256(const u8 *sig, const void *msg, size_t msg_size) {
+ /* Define constants. */
+ constexpr int EmBits = 2047;
+ constexpr int EmLen = util::DivideUp(EmBits, BITSIZEOF(u8));
+ constexpr int SaltLen = 0x20;
+ constexpr int HashLen = se::Sha256HashSize;
+
+ /* Define a work buffer. */
+ u8 work[EmLen];
+ ON_SCOPE_EXIT { util::ClearMemory(work, sizeof(work)); };
+
+ /* Calculate the message hash, first flushing cache to ensure SE sees correct data. */
+ se::Sha256Hash msg_hash;
+ hw::FlushDataCache(msg, msg_size);
+ hw::DataSynchronizationBarrierInnerShareable();
+ se::CalculateSha256(std::addressof(msg_hash), msg, msg_size);
+
+ /* Verify the tail magic. */
+ bool is_valid = sig[EmLen - 1] == TailMagic;
+
+ /* Determine extents of masked db and h. */
+ const u8 *masked_db = std::addressof(sig[0]);
+ const u8 *h = std::addressof(sig[EmLen - HashLen - 1]);
+
+ /* Verify the extra bits are zero. */
+ is_valid &= (masked_db[0] >> (BITSIZEOF(u8) - (BITSIZEOF(u8) * EmLen - EmBits))) == 0;
+
+ /* Calculate the db mask. */
+ {
+ constexpr int MaskLen = EmLen - HashLen - 1;
+ constexpr int HashIters = util::DivideUp(MaskLen, HashLen);
+
+ u8 mgf1_buf[sizeof(u32) + HashLen];
+
+ std::memcpy(std::addressof(mgf1_buf[0]), h, HashLen);
+ std::memset(std::addressof(mgf1_buf[HashLen]), 0, sizeof(u32));
+
+ for (int i = 0; i < HashIters; ++i) {
+ /* Set the counter for this iteration. */
+ mgf1_buf[sizeof(mgf1_buf) - 1] = i;
+
+ /* Calculate the sha256 to the appropriate place in the work buffer. */
+ auto *mgf1_dst = reinterpret_cast(std::addressof(work[HashLen * i]));
+
+ hw::FlushDataCache(mgf1_buf, sizeof(mgf1_buf));
+ hw::DataSynchronizationBarrierInnerShareable();
+ se::CalculateSha256(mgf1_dst, mgf1_buf, sizeof(mgf1_buf));
+ }
+ }
+
+ /* Decrypt masked db using the mask we just generated. */
+ for (int i = 0; i < EmLen - HashLen - 1; ++i) {
+ work[i] ^= masked_db[i];
+ }
+
+ /* Mask out the top bits. */
+ u8 *db = work;
+ db[0] &= 0xFF >> (BITSIZEOF(u8) * EmLen - EmBits);
+
+ /* Verify that DB is of the form 0000...0001 */
+ constexpr int DbLen = EmLen - HashLen - 1;
+ int salt_ofs = 0;
+ {
+ int looking_for_one = 1;
+ int invalid_db_padding = 0;
+ int is_zero;
+ int is_one;
+ for (size_t i = 0; i < DbLen; /* ... */) {
+ is_zero = (db[i] == 0);
+ is_one = (db[i] == 1);
+ salt_ofs += (looking_for_one & is_one) * (static_cast(++i));
+ looking_for_one &= ~is_one;
+ invalid_db_padding |= (looking_for_one & ~is_zero);
+ }
+
+ is_valid &= (invalid_db_padding == 0);
+ }
+
+ /* Verify salt. */
+ is_valid &= (DbLen - salt_ofs) == SaltLen;
+
+ /* Setup the message to verify. */
+ const u8 *salt = std::addressof(db[DbLen - SaltLen]);
+ u8 verif_msg[8 + HashLen + SaltLen];
+ ON_SCOPE_EXIT { util::ClearMemory(verif_msg, sizeof(verif_msg)); };
+
+ util::ClearMemory(std::addressof(verif_msg[0]), 8);
+ std::memcpy(std::addressof(verif_msg[8]), std::addressof(msg_hash), HashLen);
+ std::memcpy(std::addressof(verif_msg[8 + HashLen]), salt, SaltLen);
+
+ /* Verify the final hash. */
+ return VerifyHash(h, reinterpret_cast(std::addressof(verif_msg[0])), sizeof(verif_msg));
+ }
+
+ bool VerifyRsaPssSha256(int slot, void *sig, size_t sig_size, const void *msg, size_t msg_size) {
+ /* Exponentiate the signature, using the signature as the destination buffer. */
+ se::ModularExponentiate(sig, sig_size, slot, sig, sig_size);
+
+ /* Verify the pss padding. */
+ return VerifyRsaPssSha256(static_cast(sig), msg, msg_size);
+ }
+
+ }
+
+ bool VerifySignature(void *sig, size_t sig_size, const void *mod, size_t mod_size, const void *msg, size_t msg_size) {
+ /* Load the public key into a temporary keyslot. */
+ const int slot = pkg1::RsaKeySlot_Temporary;
+ se::SetRsaKey(slot, mod, mod_size, RsaPublicKeyExponent, util::size(RsaPublicKeyExponent));
+
+ return VerifyRsaPssSha256(slot, sig, sig_size, msg, msg_size);
+ }
+
+ bool VerifyHash(const void *hash, uintptr_t msg, size_t msg_size) {
+ /* Zero-sized messages are always valid. */
+ if (msg_size == 0) {
+ return true;
+ }
+
+ /* Ensure that the SE sees correct data for the message. */
+ hw::FlushDataCache(reinterpret_cast(msg), msg_size);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Calculate the hash. */
+ se::Sha256Hash calc_hash;
+ se::CalculateSha256(std::addressof(calc_hash), reinterpret_cast(msg), msg_size);
+
+ /* Verify the result. */
+ return crypto::IsSameBytes(std::addressof(calc_hash), hash, sizeof(calc_hash));
+ }
+
+}
diff --git a/exosphere/program/source/boot/secmon_boot_setup.cpp b/exosphere/program/source/boot/secmon_boot_setup.cpp
new file mode 100644
index 0000000..1f56214
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_boot_setup.cpp
@@ -0,0 +1,386 @@
+/*
+ * 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_cache.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(i - 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 = IsProduction();
+
+ /* 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();
+ }
+
+ }
+
+ namespace {
+
+ using namespace ams::mmu;
+
+ constexpr void UnmapPhysicalIdentityMappingImpl(u64 *l1, u64 *l2, u64 *l3) {
+ /* Invalidate the L3 entries for the tzram and iram boot code regions. */
+ InvalidateL3Entries(l3, MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize());
+ InvalidateL3Entries(l3, MemoryRegionPhysicalIramBootCode.GetAddress(), MemoryRegionPhysicalIramBootCode.GetSize());
+
+ /* Unmap the L2 entries corresponding to those L3 entries. */
+ InvalidateL2Entries(l2, MemoryRegionPhysicalIramL2.GetAddress(), MemoryRegionPhysicalIramL2.GetSize());
+ InvalidateL2Entries(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2.GetSize());
+
+ /* Unmap the L1 entry corresponding to to those L2 entries. */
+ InvalidateL1Entries(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysical.GetSize());
+ }
+
+ constexpr void UnmapDramImpl(u64 *l1, u64 *l2, u64 *l3) {
+ /* Unmap the L1 entry corresponding to to the Dram entries. */
+ InvalidateL1Entries(l1, MemoryRegionDram.GetAddress(), MemoryRegionDram.GetSize());
+ }
+
+ }
+
+ 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();
+ }
+
+ void UnmapPhysicalIdentityMapping() {
+ /* Get the tables. */
+ u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer();
+ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer();
+
+ /* Unmap. */
+ UnmapPhysicalIdentityMappingImpl(l1, l2_l3, l2_l3);
+
+ /* Ensure the mappings are consistent. */
+ secmon::boot::EnsureMappingConsistency();
+ }
+
+ void UnmapDram() {
+ /* Get the tables. */
+ u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer();
+ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer();
+
+ /* Unmap. */
+ UnmapDramImpl(l1, l2_l3, l2_l3);
+
+ /* Ensure the mappings are consistent. */
+ secmon::boot::EnsureMappingConsistency();
+ }
+
+}
diff --git a/exosphere/program/source/boot/secmon_crt0.s b/exosphere/program/source/boot/secmon_crt0.s
new file mode 100644
index 0000000..44d24eb
--- /dev/null
+++ b/exosphere/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/exosphere/program/source/boot/secmon_crt0_cpp.cpp b/exosphere/program/source/boot/secmon_crt0_cpp.cpp
new file mode 100644
index 0000000..35b993e
--- /dev/null
+++ b/exosphere/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/exosphere/program/source/boot/secmon_main.cpp b/exosphere/program/source/boot/secmon_main.cpp
new file mode 100644
index 0000000..6769cd9
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_main.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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 "../smc/secmon_random_cache.hpp"
+#include "../secmon_cache.hpp"
+#include "../secmon_cpu_context.hpp"
+#include "../secmon_misc.hpp"
+#include "../secmon_setup.hpp"
+
+namespace ams::secmon {
+
+ namespace {
+
+ constexpr inline const uintptr_t Package2LoadAddress = MemoryRegionDramPackage2Payloads.GetAddress();
+
+ }
+
+ void Main() {
+ /* Set library register addresses. */
+ actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress());
+ clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress());
+ flow::SetRegisterAddress(MemoryRegionVirtualDeviceFlowController.GetAddress());
+ 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(MemoryRegionVirtualDeviceApbMisc.GetAddress(), MemoryRegionVirtualDeviceGpio.GetAddress());
+ 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();
+
+ /* Setup the Cpu core context. */
+ secmon::SetupCpuCoreContext();
+
+ /* 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;
+ }
+
+ /* Wait for NX Bootloader to finish loading the BootConfig. */
+ secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_LoadedBootConfig);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Load the bootconfig. */
+ secmon::boot::LoadBootConfig(MemoryRegionPhysicalIramBootConfig.GetPointer());
+
+ /* Verify or clear the boot config. */
+ secmon::boot::VerifyOrClearBootConfig();
+
+ /* Get the boot config. */
+ const auto &bc = secmon::GetBootConfig();
+
+ /* Set the tsc value by the boot config. */
+ {
+ constexpr u64 TscMask = (static_cast(1) << 55) - 1;
+
+ secmon::boot::EnableTsc(bc.data.GetInitialTscValue() & TscMask);
+ }
+
+ /* Wait for NX Bootloader to initialize DRAM. */
+ secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_InitializedDram);
+
+ /* Secure the PMC and MC. */
+ secmon::SetupPmcAndMcSecure();
+
+ /* Copy warmboot to dram. */
+ {
+ /* Define warmboot extents. */
+ const void * const src = MemoryRegionPhysicalIramWarmbootBin.GetPointer();
+ void * const dst = MemoryRegionVirtualDramSecureDataStoreWarmbootFirmware.GetPointer();
+ const size_t size = MemoryRegionPhysicalIramWarmbootBin.GetSize();
+
+ /* Ensure we copy the correct data. */
+ hw::FlushDataCache(src, size);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Copy warmboot.bin to its secure dram location. */
+ std::memcpy(dst, src, size);
+ }
+
+ /* Unmap the identity mapping. */
+ secmon::boot::UnmapPhysicalIdentityMapping();
+
+ /* Setup the GPU carveout's magic numbers. */
+ secmon::boot::WriteGpuCarveoutMagicNumbers();
+
+ /* Wait for NX bootloader to load Package2. */
+ secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_LoadedPackage2);
+
+ /* Parse and decrypt the package2 header. */
+ pkg2::Package2Meta &pkg2_meta = secmon::boot::GetEphemeralPackage2Meta();
+ const uintptr_t pkg2_payloads_start = MemoryRegionDramPackage2.GetAddress() + sizeof(pkg2::Package2Header);
+ {
+ /* Read the encrypred header. */
+ pkg2::Package2Header encrypted_header;
+
+ const auto *dram_header = MemoryRegionDramPackage2.GetPointer();
+ hw::FlushDataCache(dram_header, sizeof(*dram_header));
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ std::memcpy(std::addressof(encrypted_header), dram_header, sizeof(encrypted_header));
+
+ /* Atmosphere extension: support plaintext package2, identified by all-zeroes signature and decrypted header. */
+ secmon::boot::UpdateBootConfigForPackage2Header(encrypted_header);
+
+ /* Verify the package2 header's signature. */
+ secmon::boot::VerifyPackage2HeaderSignature(encrypted_header, !bc.signed_data.IsPackage2SignatureVerificationDisabled());
+
+ /* Decrypt the package2 header. */
+ secmon::boot::DecryptPackage2Header(std::addressof(pkg2_meta), encrypted_header.meta, !bc.signed_data.IsPackage2EncryptionDisabled());
+ }
+
+ /* Verify the package2 header. */
+ secmon::boot::VerifyPackage2Header(pkg2_meta);
+
+ /* Save the package2 hash if in recovery boot. */
+ if (secmon::IsRecoveryBoot()) {
+ se::Sha256Hash hash;
+ secmon::boot::CalculatePackage2Hash(std::addressof(hash), pkg2_meta, MemoryRegionDramPackage2.GetAddress());
+ secmon::SetPackage2Hash(hash);
+ }
+
+ /* Verify the package2 payloads. */
+ secmon::boot::CheckVerifyResult(secmon::boot::VerifyPackage2Payloads(pkg2_meta, pkg2_payloads_start), pkg1::ErrorInfo_InvalidPackage2Payload, "package2 payload verification failed");
+
+ /* Decrypt/Move the package2 payloads to the right places. */
+ secmon::boot::DecryptAndLoadPackage2Payloads(Package2LoadAddress, pkg2_meta, pkg2_payloads_start, !bc.signed_data.IsPackage2EncryptionDisabled());
+
+ /* Ensure that the CPU sees correct package2 data. */
+ secmon::FlushEntireDataCache();
+ secmon::EnsureInstructionConsistency();
+
+ /* Set the core's entrypoint and argument. */
+ secmon::SetEntryContext(0, Package2LoadAddress + pkg2_meta.entrypoint, 0);
+
+ /* Unmap DRAM. */
+ secmon::boot::UnmapDram();
+
+ /* Wait for NX bootloader to be done. */
+ secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_Done);
+
+ /* Perform final initialization. */
+ secmon::SetupSocProtections();
+ secmon::SetupCpuSErrorDebug();
+ }
+
+}
diff --git a/exosphere/program/source/boot/secmon_make_page_table.cpp b/exosphere/program/source/boot/secmon_make_page_table.cpp
new file mode 100644
index 0000000..4309cf9
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_make_page_table.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 Debug region. */
+ /* NOTE: This region is reserved for debug. By default it will be the last 0x8000 bytes of IRAM, but this is subject to change. */
+ /* If you are doing development work for exosphere, feel free to locally change this to whatever is useful. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualDebug.GetAddress(), MemoryRegionPhysicalIram.GetEndAddress() - 0x8000, 0x8000, MappingAttributesEl3SecureDevice);
+
+ /* 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, MappingAttributesEl3SecureRwData);
+ 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);
+ }
+
+}
diff --git a/exosphere/program/source/boot/secmon_package2.cpp b/exosphere/program/source/boot/secmon_package2.cpp
new file mode 100644
index 0000000..32e8e8a
--- /dev/null
+++ b/exosphere/program/source/boot/secmon_package2.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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_key_storage.hpp"
+#include "secmon_boot.hpp"
+
+namespace ams::secmon::boot {
+
+ void CalculatePackage2Hash(se::Sha256Hash *dst, const pkg2::Package2Meta &meta, uintptr_t package2_start) {
+ /* Determine the region to hash. */
+ const void *data = reinterpret_cast(package2_start);
+ const size_t size = meta.GetSize();
+
+ /* Flush to ensure the SE sees the correct data. */
+ hw::FlushDataCache(data, size);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Calculate the hash. */
+ se::CalculateSha256(dst, data, size);
+ }
+
+ bool VerifyPackage2Signature(pkg2::Package2Header &header, const void *mod, size_t mod_size) {
+ return VerifySignature(header.signature, sizeof(header.signature), mod, mod_size, std::addressof(header.meta), sizeof(header.meta));
+ }
+
+ void DecryptPackage2(void *dst, size_t dst_size, const void *src, size_t src_size, const void *key, size_t key_size, const void *iv, size_t iv_size, u8 key_generation) {
+ /* Ensure that the SE sees consistent data. */
+ hw::FlushDataCache(key, key_size);
+ hw::FlushDataCache(src, src_size);
+ hw::FlushDataCache(dst, dst_size);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Load the needed master key into the temporary keyslot. */
+ secmon::LoadMasterKey(pkg1::AesKeySlot_Temporary, key_generation);
+
+ /* Load the package2 key into the temporary keyslot. */
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Temporary, pkg1::AesKeySlot_Temporary, key, key_size);
+
+ /* Decrypt the data. */
+ se::ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Temporary, src, src_size, iv, iv_size);
+
+ /* Clear the keyslot we just used. */
+ se::ClearAesKeySlot(pkg1::AesKeySlot_Temporary);
+
+ /* Ensure that the cpu sees consistent data. */
+ hw::DataSynchronizationBarrierInnerShareable();
+ hw::FlushDataCache(dst, dst_size);
+ hw::DataSynchronizationBarrierInnerShareable();
+ }
+
+ bool VerifyPackage2Meta(const pkg2::Package2Meta &meta) {
+ /* Get the obfuscated metadata. */
+ const size_t size = meta.GetSize();
+ const u8 key_generation = meta.GetKeyGeneration();
+
+ /* Check that size is big enough for the header. */
+ if (size <= sizeof(pkg2::Package2Header)) {
+ return false;
+ }
+
+ /* Check that the size isn't larger than what we allow. */
+ if (size > pkg2::Package2SizeMax) {
+ return false;
+ }
+
+ /* Check that the key generation is one that we can use. */
+ static_assert(pkg1::KeyGeneration_Count == 11);
+ if (key_generation >= pkg1::KeyGeneration_Count) {
+ return false;
+ }
+
+ /* Check the magic number. */
+ if (!crypto::IsSameBytes(meta.magic, pkg2::Package2Meta::Magic::String, sizeof(meta.magic))) {
+ return false;
+ }
+
+ /* Check the payload alignments. */
+ if ((meta.entrypoint % pkg2::PayloadAlignment) != 0) {
+ return false;
+ }
+
+ for (int i = 0; i < pkg2::PayloadCount; ++i) {
+ if ((meta.payload_sizes[i] % pkg2::PayloadAlignment) != 0) {
+ return false;
+ }
+ }
+
+ /* Check that the sizes sum to the total. */
+ if (size != sizeof(pkg2::Package2Header) + meta.payload_sizes[0] + meta.payload_sizes[1] + meta.payload_sizes[2]) {
+ return false;
+ }
+
+ /* Check that the payloads do not overflow. */
+ for (int i = 0; i < pkg2::PayloadCount; ++i) {
+ if (meta.payload_offsets[i] > meta.payload_offsets[i] + meta.payload_sizes[i]) {
+ return false;
+ }
+ }
+
+ /* Verify that no payloads overlap. */
+ for (int i = 0; i < pkg2::PayloadCount - 1; ++i) {
+ for (int j = i + 1; j < pkg2::PayloadCount; ++j) {
+ if (util::HasOverlap(meta.payload_offsets[i], meta.payload_sizes[i], meta.payload_offsets[j], meta.payload_sizes[j])) {
+ return false;
+ }
+ }
+ }
+
+ /* Check whether any payload contains the entrypoint. */
+ for (int i = 0; i < pkg2::PayloadCount; ++i) {
+ if (util::Contains(meta.payload_offsets[i], meta.payload_sizes[i], meta.entrypoint)) {
+ return true;
+ }
+ }
+
+ /* No payload contains the entrypoint, so we're not valid. */
+ return false;
+ }
+
+ bool VerifyPackage2Version(const pkg2::Package2Meta &meta) {
+ return meta.bootloader_version <= pkg2::CurrentBootloaderVersion && meta.package2_version >= pkg2::MinimumValidDataVersion;
+ }
+
+ bool VerifyPackage2Payloads(const pkg2::Package2Meta &meta, uintptr_t payload_address) {
+ /* Verify hashes match for all payloads. */
+ for (int i = 0; i < pkg2::PayloadCount; ++i) {
+ if (!VerifyHash(meta.payload_hashes[i], payload_address, meta.payload_sizes[i])) {
+ return false;
+ }
+
+ payload_address += meta.payload_sizes[i];
+ }
+
+ return true;
+ }
+
+}
diff --git a/exosphere/program/source/boot/secmon_size_data.s b/exosphere/program/source/boot/secmon_size_data.s
new file mode 100644
index 0000000..0943ef9
--- /dev/null
+++ b/exosphere/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/exosphere/program/source/secmon_cache.cpp b/exosphere/program/source/secmon_cache.cpp
new file mode 100644
index 0000000..02fd043
--- /dev/null
+++ b/exosphere/program/source/secmon_cache.cpp
@@ -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
+#include "secmon_cache.hpp"
+
+namespace ams::secmon {
+
+ #include "secmon_cache_impl.inc"
+
+}
\ No newline at end of file
diff --git a/exosphere/program/source/secmon_cache.hpp b/exosphere/program/source/secmon_cache.hpp
new file mode 100644
index 0000000..3050c38
--- /dev/null
+++ b/exosphere/program/source/secmon_cache.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 {
+
+ #include "secmon_cache.inc"
+
+}
\ No newline at end of file
diff --git a/exosphere/program/source/secmon_cache.inc b/exosphere/program/source/secmon_cache.inc
new file mode 100644
index 0000000..8ae241a
--- /dev/null
+++ b/exosphere/program/source/secmon_cache.inc
@@ -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 .
+ */
+
+void FlushEntireDataCache();
+void FlushEntireDataCacheLocal();
+void InvalidateEntireDataCache();
+
+void EnsureMappingConsistency();
+void EnsureMappingConsistency(uintptr_t address);
+void EnsureInstructionConsistency();
diff --git a/exosphere/program/source/secmon_cache_impl.inc b/exosphere/program/source/secmon_cache_impl.inc
new file mode 100644
index 0000000..a305ce6
--- /dev/null
+++ b/exosphere/program/source/secmon_cache_impl.inc
@@ -0,0 +1,172 @@
+/*
+ * 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 FlushDataCacheFrom(int loc) {
+ for (int level = loc - 1; level >= 0; --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 FlushEntireDataCacheLocal() {
+ util::BitPack32 clidr;
+ HW_CPU_GET_CLIDR_EL1(clidr);
+ FlushDataCacheFrom(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();
+}
diff --git a/exosphere/program/source/secmon_cpu_context.cpp b/exosphere/program/source/secmon_cpu_context.cpp
new file mode 100644
index 0000000..d2185a6
--- /dev/null
+++ b/exosphere/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/exosphere/program/source/secmon_cpu_context.hpp b/exosphere/program/source/secmon_cpu_context.hpp
new file mode 100644
index 0000000..a19bd8a
--- /dev/null
+++ b/exosphere/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/exosphere/program/source/secmon_error.cpp b/exosphere/program/source/secmon_error.cpp
new file mode 100644
index 0000000..c05bf4c
--- /dev/null
+++ b/exosphere/program/source/secmon_error.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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() {
+ /* TODO: This is here for debugging. Remove this when exo2 is working. */
+#if 1
+ {
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x00) = 0xDDDDDDDD;
+
+ u64 temp_reg;
+ __asm__ __volatile__("mov %0, lr" : "=r"(temp_reg) :: "memory");
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast(temp_reg >> 0);
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x14) = static_cast(temp_reg >> 32);
+
+
+ __asm__ __volatile__("mov %0, sp" : "=r"(temp_reg) :: "memory");
+ for (int i = 0; i < 0x100; i += 4) {
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x20 + i) = *(volatile u32 *)(temp_reg + i);
+ }
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02;
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10;
+
+ util::WaitMicroSeconds(1000);
+ }
+#endif
+
+ 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() {
+ /* TODO: This is here for debugging. Remove this when exo2 is working. */
+#if 1
+ {
+ u64 temp_reg;
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x00) = 0x5A5A5A5A;
+
+ __asm__ __volatile__("mrs %0, esr_el3" : "=r"(temp_reg) :: "memory");
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x08) = static_cast(temp_reg >> 0);
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x0C) = static_cast(temp_reg >> 32);
+
+ __asm__ __volatile__("mrs %0, elr_el3" : "=r"(temp_reg) :: "memory");
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast(temp_reg >> 0);
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x14) = static_cast(temp_reg >> 32);
+
+ __asm__ __volatile__("mrs %0, far_el3" : "=r"(temp_reg) :: "memory");
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x18) = static_cast(temp_reg >> 0);
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x1C) = static_cast(temp_reg >> 32);
+
+ __asm__ __volatile__("mov %0, lr" : "=r"(temp_reg) :: "memory");
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x20) = static_cast(temp_reg >> 0);
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x24) = static_cast(temp_reg >> 32);
+
+ __asm__ __volatile__("mov %0, sp" : "=r"(temp_reg) :: "memory");
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x30) = static_cast(temp_reg >> 0);
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x34) = static_cast(temp_reg >> 32);
+
+ for (int i = 0; i < 0x100; i += 4) {
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x40 + i) = *(volatile u32 *)(temp_reg + i);
+ }
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02;
+ *(volatile u32 *)(secmon::MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10;
+
+ util::WaitMicroSeconds(1000);
+ }
+#endif
+
+ /* 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/exosphere/program/source/secmon_error.hpp b/exosphere/program/source/secmon_error.hpp
new file mode 100644
index 0000000..abbfad2
--- /dev/null
+++ b/exosphere/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/exosphere/program/source/secmon_exception_vectors.s b/exosphere/program/source/secmon_exception_vectors.s
new file mode 100644
index 0000000..2749f9f
--- /dev/null
+++ b/exosphere/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 sp, x30
+
+ /* 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/exosphere/program/source/secmon_interrupt_handler.cpp b/exosphere/program/source/secmon_interrupt_handler.cpp
new file mode 100644
index 0000000..41f4666
--- /dev/null
+++ b/exosphere/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/exosphere/program/source/secmon_interrupt_handler.hpp b/exosphere/program/source/secmon_interrupt_handler.hpp
new file mode 100644
index 0000000..136da02
--- /dev/null
+++ b/exosphere/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/exosphere/program/source/secmon_key_storage.cpp b/exosphere/program/source/secmon_key_storage.cpp
new file mode 100644
index 0000000..207f102
--- /dev/null
+++ b/exosphere/program/source/secmon_key_storage.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 {
+
+ constexpr const u8 RsaPublicKey[] = { 0x00, 0x01, 0x00, 0x01 };
+
+ 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 LoadProvisionalRsaPublicKey(int slot, ImportRsaKey which) {
+ se::SetRsaKey(slot, GetRsaKeyModulus(which), se::RsaSize, RsaPublicKey, sizeof(RsaPublicKey));
+ }
+
+ 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::max(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::max(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/exosphere/program/source/secmon_key_storage.hpp b/exosphere/program/source/secmon_key_storage.hpp
new file mode 100644
index 0000000..076756d
--- /dev/null
+++ b/exosphere/program/source/secmon_key_storage.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::secmon {
+
+ /* NOTE: Lotus and EsDrmCert are switched here versus official enum, */
+ /* however, this considerably simplifies logic. */
+ enum ImportRsaKey {
+ ImportRsaKey_Lotus = 0,
+ ImportRsaKey_EsDrmCert = 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 LoadProvisionalRsaPublicKey(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/exosphere/program/source/secmon_map.cpp b/exosphere/program/source/secmon_map.cpp
new file mode 100644
index 0000000..96ccf84
--- /dev/null
+++ b/exosphere/program/source/secmon_map.cpp
@@ -0,0 +1,298 @@
+/*
+ * 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_cache.hpp"
+#include "secmon_setup.hpp"
+#include "secmon_spinlock.hpp"
+#include "secmon_map.hpp"
+#include "smc/secmon_smc_info.hpp"
+
+namespace ams::secmon {
+
+ namespace {
+
+ constexpr inline const uintptr_t BootCodeAddress = MemoryRegionVirtualTzramBootCode.GetAddress();
+ constexpr inline const size_t BootCodeSize = MemoryRegionVirtualTzramBootCode.GetSize();
+
+ constinit uintptr_t g_smc_user_page_physical_address = 0;
+ constinit uintptr_t g_ams_iram_page_physical_address = 0;
+ constinit uintptr_t g_ams_user_page_physical_address = 0;
+
+ constinit SpinLockType g_ams_iram_page_spin_lock = {};
+ constinit SpinLockType g_ams_user_page_spin_lock = {};
+
+ using namespace ams::mmu;
+
+ constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexNormal);
+ constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureDevice = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexDevice);
+
+ constexpr void UnmapBootCodeImpl(u64 *l1, u64 *l2, u64 *l3, uintptr_t boot_code, size_t boot_code_size) {
+ /* Unmap the L3 entries corresponding to the boot code. */
+ InvalidateL3Entries(l3, boot_code, boot_code_size);
+ }
+
+ constexpr void UnmapTzramImpl(u64 *l1, u64 *l2, u64 *l3) {
+ /* Unmap the L3 entries corresponding to tzram. */
+ InvalidateL3Entries(l3, MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize());
+
+ /* Unmap the L2 entries corresponding to those L3 entries. */
+ InvalidateL2Entries(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2.GetSize());
+
+ /* Unmap the L1 entry corresponding to to those L2 entries. */
+ InvalidateL1Entries(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysical.GetSize());
+ }
+
+ constexpr void MapSmcUserPageImpl(u64 *l3, uintptr_t address) {
+ /* Set the L3 entry. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualSmcUserPage.GetAddress(), address, MemoryRegionVirtualSmcUserPage.GetSize(), MappingAttributesEl3NonSecureRwData);
+ }
+
+ constexpr void UnmapSmcUserPageImpl(u64 *l3) {
+ /* Unmap the L3 entry. */
+ InvalidateL3Entries(l3, MemoryRegionVirtualSmcUserPage.GetAddress(), MemoryRegionVirtualSmcUserPage.GetSize());
+ }
+
+ constexpr void MapAtmosphereIramPageImpl(u64 *l3, uintptr_t address) {
+ /* Set the L3 entry. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualAtmosphereIramPage.GetAddress(), address, MemoryRegionVirtualAtmosphereIramPage.GetSize(), MappingAttributesEl3NonSecureDevice);
+ }
+
+ constexpr void UnmapAtmosphereIramPageImpl(u64 *l3) {
+ /* Unmap the L3 entry. */
+ InvalidateL3Entries(l3, MemoryRegionVirtualAtmosphereIramPage.GetAddress(), MemoryRegionVirtualAtmosphereIramPage.GetSize());
+ }
+
+ constexpr void MapAtmosphereUserPageImpl(u64 *l3, uintptr_t address) {
+ /* Set the L3 entry. */
+ SetL3BlockEntry(l3, MemoryRegionVirtualAtmosphereUserPage.GetAddress(), address, MemoryRegionVirtualAtmosphereUserPage.GetSize(), MappingAttributesEl3NonSecureRwData);
+ }
+
+ constexpr void UnmapAtmosphereUserPageImpl(u64 *l3) {
+ /* Unmap the L3 entry. */
+ InvalidateL3Entries(l3, MemoryRegionVirtualAtmosphereUserPage.GetAddress(), MemoryRegionVirtualAtmosphereUserPage.GetSize());
+ }
+
+ void ClearLow(uintptr_t address, size_t size) {
+ /* Clear the low part. */
+ util::ClearMemory(reinterpret_cast(address), size / 2);
+ }
+
+ void ClearHigh(uintptr_t address, size_t size) {
+ /* Clear the high part. */
+ util::ClearMemory(reinterpret_cast(address + size / 2), size / 2);
+ }
+
+ bool IsPhysicalMemoryAddress(uintptr_t address) {
+ return (address - MemoryRegionDram.GetAddress()) < GetPhysicalMemorySize();
+ }
+
+ }
+
+ void ClearBootCodeHigh() {
+ ClearHigh(BootCodeAddress, BootCodeSize);
+ }
+
+ void UnmapBootCode() {
+ /* Get the tables. */
+ u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer();
+ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer();
+
+ /* Clear the low boot code region; high was already cleared by a previous call. */
+ ClearLow(BootCodeAddress, BootCodeSize);
+
+ /* Unmap. */
+ UnmapBootCodeImpl(l1, l2_l3, l2_l3, BootCodeAddress, BootCodeSize);
+
+ /* Ensure the mappings are consistent. */
+ secmon::EnsureMappingConsistency();
+ }
+
+ size_t GetPhysicalMemorySize() {
+ switch (smc::GetPhysicalMemorySize()) {
+ case pkg1::MemorySize_4GB: return 4_GB;
+ case pkg1::MemorySize_6GB: return 6_GB;
+ case pkg1::MemorySize_8GB: return 8_GB;
+ AMS_UNREACHABLE_DEFAULT_CASE();
+ }
+ }
+
+ void UnmapTzram() {
+ /* Get the tables. */
+ u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer();
+ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer();
+
+ /* Unmap. */
+ UnmapTzramImpl(l1, l2_l3, l2_l3);
+
+ /* Ensure the mappings are consistent. */
+ secmon::EnsureMappingConsistency();
+ }
+
+ uintptr_t MapSmcUserPage(uintptr_t address) {
+ if (g_smc_user_page_physical_address == 0) {
+ if (!IsPhysicalMemoryAddress(address)) {
+ return 0;
+ }
+
+ if (!util::IsAligned(address, 4_KB)) {
+ return 0;
+ }
+
+ g_smc_user_page_physical_address = address;
+
+ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer();
+
+ MapSmcUserPageImpl(l2_l3, address);
+
+ /* Ensure the mappings are consistent. */
+ secmon::EnsureMappingConsistency(MemoryRegionVirtualSmcUserPage.GetAddress());
+ } else {
+ AMS_ABORT_UNLESS(address == g_smc_user_page_physical_address);
+ }
+
+ return MemoryRegionVirtualSmcUserPage.GetAddress();
+ }
+
+ void UnmapSmcUserPage() {
+ if (g_smc_user_page_physical_address == 0) {
+ return;
+ }
+
+ /* Ensure that the page is no longer in cache. */
+ hw::FlushDataCache(MemoryRegionVirtualSmcUserPage.GetPointer(), MemoryRegionVirtualSmcUserPage.GetSize());
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer();
+
+ UnmapSmcUserPageImpl(l2_l3);
+
+ /* Ensure the mappings are consistent. */
+ secmon::EnsureMappingConsistency(MemoryRegionVirtualSmcUserPage.GetAddress());
+
+ g_smc_user_page_physical_address = 0;
+ }
+
+ uintptr_t MapAtmosphereIramPage(uintptr_t address) {
+ /* Acquire the ams iram spinlock. */
+ AcquireSpinLock(g_ams_iram_page_spin_lock);
+ auto lock_guard = SCOPE_GUARD { ReleaseSpinLock(g_ams_iram_page_spin_lock); };
+
+ /* Validate that the page is an IRAM page. */
+ if (!MemoryRegionPhysicalIram.Contains(address, 1)) {
+ return 0;
+ }
+
+ /* Validate that the page is aligned. */
+ if (!util::IsAligned(address, 4_KB)) {
+ return 0;
+ }
+
+ /* Map the page. */
+ g_ams_iram_page_physical_address = address;
+
+ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer();
+
+ MapAtmosphereIramPageImpl(l2_l3, address);
+
+ /* Ensure the mappings are consistent. */
+ secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereIramPage.GetAddress());
+
+ /* Hold the lock. */
+ lock_guard.Cancel();
+
+ return MemoryRegionVirtualAtmosphereIramPage.GetAddress();
+ }
+
+ void UnmapAtmosphereIramPage() {
+ /* Can't unmap if nothing's unmapped. */
+ if (g_ams_iram_page_physical_address == 0) {
+ return;
+ }
+
+ /* Ensure that the page is no longer in cache. */
+ hw::FlushDataCache(MemoryRegionVirtualAtmosphereIramPage.GetPointer(), MemoryRegionVirtualAtmosphereIramPage.GetSize());
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Unmap the page. */
+ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer();
+
+ UnmapAtmosphereIramPageImpl(l2_l3);
+
+ /* Ensure the mappings are consistent. */
+ secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereIramPage.GetAddress());
+
+ /* Release the page. */
+ g_ams_iram_page_physical_address = 0;
+
+ ReleaseSpinLock(g_ams_iram_page_spin_lock);
+ }
+
+ uintptr_t MapAtmosphereUserPage(uintptr_t address) {
+ /* Acquire the ams user spinlock. */
+ AcquireSpinLock(g_ams_user_page_spin_lock);
+ auto lock_guard = SCOPE_GUARD { ReleaseSpinLock(g_ams_user_page_spin_lock); };
+
+ /* Validate that the page is a dram page. */
+ if (!IsPhysicalMemoryAddress(address)) {
+ return 0;
+ }
+
+ /* Validate that the page is aligned. */
+ if (!util::IsAligned(address, 4_KB)) {
+ return 0;
+ }
+
+ /* Map the page. */
+ g_ams_user_page_physical_address = address;
+
+ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer();
+
+ MapAtmosphereUserPageImpl(l2_l3, address);
+
+ /* Ensure the mappings are consistent. */
+ secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereUserPage.GetAddress());
+
+ /* Hold the lock. */
+ lock_guard.Cancel();
+
+ return MemoryRegionVirtualAtmosphereUserPage.GetAddress();
+ }
+
+ void UnmapAtmosphereUserPage() {
+ /* Can't unmap if nothing's unmapped. */
+ if (g_ams_user_page_physical_address == 0) {
+ return;
+ }
+
+ /* Ensure that the page is no longer in cache. */
+ hw::FlushDataCache(MemoryRegionVirtualAtmosphereUserPage.GetPointer(), MemoryRegionVirtualAtmosphereUserPage.GetSize());
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Unmap the page. */
+ u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer();
+
+ UnmapAtmosphereUserPageImpl(l2_l3);
+
+ /* Ensure the mappings are consistent. */
+ secmon::EnsureMappingConsistency(MemoryRegionVirtualAtmosphereUserPage.GetAddress());
+
+ /* Release the page. */
+ g_ams_user_page_physical_address = 0;
+
+ ReleaseSpinLock(g_ams_user_page_spin_lock);
+ }
+
+}
diff --git a/exosphere/program/source/secmon_map.hpp b/exosphere/program/source/secmon_map.hpp
new file mode 100644
index 0000000..a6bdbc4
--- /dev/null
+++ b/exosphere/program/source/secmon_map.hpp
@@ -0,0 +1,34 @@
+/*
+ * 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 {
+
+ size_t GetPhysicalMemorySize();
+
+ void UnmapTzram();
+
+ uintptr_t MapSmcUserPage(uintptr_t address);
+ void UnmapSmcUserPage();
+
+ uintptr_t MapAtmosphereIramPage(uintptr_t address);
+ void UnmapAtmosphereIramPage();
+
+ uintptr_t MapAtmosphereUserPage(uintptr_t address);
+ void UnmapAtmosphereUserPage();
+
+}
\ No newline at end of file
diff --git a/exosphere/program/source/secmon_misc.cpp b/exosphere/program/source/secmon_misc.cpp
new file mode 100644
index 0000000..eed73b5
--- /dev/null
+++ b/exosphere/program/source/secmon_misc.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 {
+
+ constinit pkg1::BctParameters g_bct_params = {};
+ constinit se::Sha256Hash g_package2_hash = {};
+
+ constinit u32 g_deprecated_boot_reason_value = {};
+ constinit u8 g_deprecated_boot_reason_state = {};
+
+ }
+
+ void SaveBootInfo(const pkg1::SecureMonitorParameters &secmon_params) {
+ /* Save the BCT parameters. */
+ g_bct_params = secmon_params.bct_params;
+
+ /* Save the deprecated boot reason. */
+ g_deprecated_boot_reason_value = secmon_params.deprecated_boot_reason_value;
+ g_deprecated_boot_reason_state = secmon_params.deprecated_boot_reason_state;
+ }
+
+ 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;
+ }
+
+ void GetPackage2Hash(se::Sha256Hash *out) {
+ *out = g_package2_hash;
+ }
+
+ void SetPackage2Hash(const se::Sha256Hash &hash) {
+ g_package2_hash = hash;
+ }
+
+ u32 GetDeprecatedBootReason() {
+ return (static_cast(g_deprecated_boot_reason_state) << 24) | (g_deprecated_boot_reason_value & 0x00FFFFFF);
+ }
+
+}
diff --git a/exosphere/program/source/secmon_misc.hpp b/exosphere/program/source/secmon_misc.hpp
new file mode 100644
index 0000000..f637d0a
--- /dev/null
+++ b/exosphere/program/source/secmon_misc.hpp
@@ -0,0 +1,34 @@
+/*
+ * 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();
+
+ void GetPackage2Hash(se::Sha256Hash *out);
+ void SetPackage2Hash(const se::Sha256Hash &hash);
+
+ u32 GetDeprecatedBootReason();
+
+}
\ No newline at end of file
diff --git a/exosphere/program/source/secmon_page_mapper.cpp b/exosphere/program/source/secmon_page_mapper.cpp
new file mode 100644
index 0000000..93452d4
--- /dev/null
+++ b/exosphere/program/source/secmon_page_mapper.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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_map.hpp"
+#include "secmon_page_mapper.hpp"
+
+namespace ams::secmon {
+
+ namespace impl {
+
+ void *PageMapperImpl::GetPointerTo(uintptr_t phys, size_t size) const {
+ /* Ensure we stay within the page. */
+ if (util::AlignDown(phys, 4_KB) != this->physical_address) {
+ return nullptr;
+ }
+ if (size != 0) {
+ if (util::AlignDown(phys + size - 1, 4_KB) != this->physical_address) {
+ return nullptr;
+ }
+ }
+
+ return reinterpret_cast(phys + (this->virtual_address - this->physical_address));
+ }
+
+ bool PageMapperImpl::CopyToMapping(uintptr_t dst_phys, const void *src, size_t size) const {
+ void * const dst = this->GetPointerTo(dst_phys, size);
+ if (dst == nullptr) {
+ return false;
+ }
+
+ std::memcpy(dst, src, size);
+ return true;
+ }
+
+ bool PageMapperImpl::CopyFromMapping(void *dst, uintptr_t src_phys, size_t size) const {
+ const void * const src = this->GetPointerTo(src_phys, size);
+ if (src == nullptr) {
+ return false;
+ }
+
+ std::memcpy(dst, src, size);
+ return true;
+ }
+
+ }
+
+ bool UserPageMapper::Map() {
+ return this->MapImpl();
+ }
+
+ bool AtmosphereIramPageMapper::Map() {
+ return this->MapImpl();
+ }
+
+ bool AtmosphereUserPageMapper::Map() {
+ return this->MapImpl();
+ }
+
+ AtmosphereIramPageMapper::~AtmosphereIramPageMapper() {
+ this->UnmapImpl();
+ }
+
+ AtmosphereUserPageMapper::~AtmosphereUserPageMapper() {
+ this->UnmapImpl();
+ }
+
+}
diff --git a/exosphere/program/source/secmon_page_mapper.hpp b/exosphere/program/source/secmon_page_mapper.hpp
new file mode 100644
index 0000000..1da95eb
--- /dev/null
+++ b/exosphere/program/source/secmon_page_mapper.hpp
@@ -0,0 +1,76 @@
+/*
+ * 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 {
+
+ namespace impl {
+
+ class PageMapperImpl {
+ private:
+ uintptr_t physical_address;
+ uintptr_t virtual_address;
+ public:
+ constexpr PageMapperImpl(uintptr_t phys) : physical_address(util::AlignDown(phys, 4_KB)), virtual_address() { /* ... */ }
+
+ void *GetPointerTo(uintptr_t phys, size_t size) const;
+
+ bool CopyToMapping(uintptr_t dst_phys, const void *src, size_t size) const;
+ bool CopyFromMapping(void *dst, uintptr_t src_phys, size_t size) const;
+
+ ALWAYS_INLINE bool CopyToUser(uintptr_t dst_phys, const void *src, size_t size) const { return CopyToMapping(dst_phys, src, size); }
+ ALWAYS_INLINE bool CopyFromUser(void *dst, uintptr_t src_phys, size_t size) const { return CopyFromMapping(dst, src_phys, size); }
+
+ template
+ bool MapImpl() {
+ this->virtual_address = F(this->physical_address);
+ return this->virtual_address != 0;
+ }
+
+ template
+ void UnmapImpl() {
+ F();
+ this->virtual_address = 0;
+ }
+ };
+
+ }
+
+ class UserPageMapper : public impl::PageMapperImpl {
+ public:
+ constexpr UserPageMapper(uintptr_t phys) : PageMapperImpl(phys) { /* ... */ }
+
+ bool Map();
+ };
+
+ class AtmosphereIramPageMapper : public impl::PageMapperImpl {
+ public:
+ constexpr AtmosphereIramPageMapper(uintptr_t phys) : PageMapperImpl(phys) { /* ... */ }
+ ~AtmosphereIramPageMapper();
+
+ bool Map();
+ };
+
+ class AtmosphereUserPageMapper : public impl::PageMapperImpl {
+ public:
+ constexpr AtmosphereUserPageMapper(uintptr_t phys) : PageMapperImpl(phys) { /* ... */ }
+ ~AtmosphereUserPageMapper();
+
+ bool Map();
+ };
+
+}
diff --git a/exosphere/program/source/secmon_setup.cpp b/exosphere/program/source/secmon_setup.cpp
new file mode 100644
index 0000000..254b8e5
--- /dev/null
+++ b/exosphere/program/source/secmon_setup.cpp
@@ -0,0 +1,1220 @@
+/*
+ * 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_map.hpp"
+#include "secmon_cpu_context.hpp"
+#include "secmon_interrupt_handler.hpp"
+#include "secmon_misc.hpp"
+#include "smc/secmon_random_cache.hpp"
+#include "smc/secmon_smc_power_management.hpp"
+#include "smc/secmon_smc_se_lock.hpp"
+
+namespace ams::secmon {
+
+ namespace {
+
+ constexpr inline const uintptr_t TIMER = secmon::MemoryRegionVirtualDeviceTimer.GetAddress();
+ constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionVirtualDeviceSystem.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();
+ constexpr inline const uintptr_t EVP = secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress();
+ constexpr inline const uintptr_t CLK_RST = secmon::MemoryRegionVirtualDeviceClkRst.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 = (1 << pkg1::AesKeySlot_UserEnd) - 1,
+ .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) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 6: Unused keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */
+ (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 7: Unused keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, 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) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 14: Unused keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, 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;
+ }
+
+ /* Cache the fuse info for quick access. */
+ ctx.secmon_cfg.SetFuseInfo();
+ }
+
+ 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 = 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_TLB_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 SetupSecureEl2AndEl1SystemRegisters() {
+ /* Setup actlr_el2 and actlr_el3. */
+ {
+ util::BitPack32 actlr = {};
+
+ actlr.Set(1); /* Enable access to cpuactlr from lower EL. */
+ actlr.Set(1); /* Enable access to cpuectlr from lower EL. */
+ actlr.Set(1); /* Enable access to l2ctlr from lower EL. */
+ actlr.Set(1); /* Enable access to l2actlr from lower EL. */
+ actlr.Set(1); /* Enable access to l2ectlr from lower EL. */
+
+ HW_CPU_SET_ACTLR_EL3(actlr);
+ HW_CPU_SET_ACTLR_EL2(actlr);
+ }
+
+ /* Setup hcr_el2. */
+ {
+ util::BitPack64 hcr = {};
+
+ hcr.Set(1); /* EL1 is aarch64 mode. */
+
+ HW_CPU_SET_HCR_EL2(hcr);
+ }
+
+ /* Configure all domain access permissions as manager. */
+ HW_CPU_SET_DACR32_EL2(~0u);
+
+ /* Setup sctlr_el1. */
+ {
+ util::BitPack64 sctlr = { hw::SctlrEl1::Res1 };
+
+ sctlr.Set(0); /* Globally disable the MMU. */
+ sctlr.Set(0); /* Disable alignment fault checking. */
+ sctlr.Set(0); /* Globally disable the data and unified caches. */
+ sctlr.Set(1); /* Enable stack alignment checking. */
+ sctlr.Set(1); /* Enable el0 stack alignment checking. */
+ sctlr.Set(1); /* Enable cp15 barrier operations. */
+ sctlr.Set(0); /* Disable ThumbEE. */
+ sctlr.Set(0); /* Enable itd instructions. */
+ sctlr.Set(0); /* Enable setend instruction. */
+ sctlr.Set(0); /* Disable el0 interrupt mask access. */
+ sctlr.Set(0); /* Globally disable the instruction cache. */
+ sctlr.Set(0); /* Disable el0 access to dc zva instruction. */
+ sctlr.Set(1); /* wfi instructions in el0 trap. */
+ sctlr.Set(1); /* wfe instructions in el0 trap. */
+ sctlr.Set(0); /* Do not force writable pages to be ExecuteNever. */
+ sctlr.Set(0); /* Data accesses in el0 are little endian. */
+ sctlr.Set(0); /* Exceptions should be little endian. */
+ sctlr.Set(0); /* Disable el0 access to dc cvau, dc civac, dc cvac, ic ivau. */
+
+ HW_CPU_SET_SCTLR_EL1(sctlr);
+ }
+
+ /* Setup sctlr_el2. */
+ {
+ util::BitPack64 sctlr = { hw::SctlrEl2::Res1 };
+
+ sctlr.Set(0); /* Globally disable the MMU. */
+ sctlr.Set(0); /* Disable alignment fault checking. */
+ sctlr.Set(0); /* Globally disable the data and unified caches. */
+ sctlr.Set(1); /* Enable stack alignment checking. */
+ sctlr.Set(0); /* Globally disable the instruction cache. */
+ sctlr.Set(0); /* Do not force writable pages to be ExecuteNever. */
+ sctlr.Set(0); /* Exceptions should be little endian. */
+
+ HW_CPU_SET_SCTLR_EL2(sctlr);
+ }
+
+ /* Ensure instruction consistency. */
+ hw::InstructionSynchronizationBarrier();
+ }
+
+ void SetupNonSecureSystemRegisters(u32 tsc_frequency) {
+ /* Set cntfrq_el0. */
+ HW_CPU_SET_CNTFRQ_EL0(tsc_frequency);
+
+ /* Set cnthctl_el2. */
+ {
+ util::BitPack32 cnthctl = {};
+
+ cnthctl.Set(1); /* Do not trap accesses to cntpct_el0. */
+ cnthctl.Set(1); /* Do not trap accesses to cntp_ctl_el0, cntp_cval_el0, and cntp_tval_el0. */
+ cnthctl.Set(0); /* Disable the event stream. */
+ cnthctl.Set(0); /* Trigger events on 0 -> 1 transition. */
+ cnthctl.Set(0); /* Select bit0 of cntpct_el0 as the event stream trigger. */
+
+ HW_CPU_SET_CNTHCTL_EL2(cnthctl);
+ }
+
+ /* Ensure instruction consistency. */
+ hw::InstructionSynchronizationBarrier();
+ }
+
+ void SetupGpuCarveout() {
+ /* Configure carveout 2. */
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_BOM, static_cast(MemoryRegionDramGpuCarveout.GetAddress() >> 0));
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_BOM_HI, static_cast(MemoryRegionDramGpuCarveout.GetAddress() >> BITSIZEOF(u32)));
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_SIZE_128KB, MemoryRegionDramGpuCarveout.GetSize() / 128_KB);
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2, MC_REG_BITS_ENUM (CLIENT_ACCESS2_GPUSRD, ENABLE),
+ MC_REG_BITS_ENUM (CLIENT_ACCESS2_GPUSWR, ENABLE),
+ MC_REG_BITS_ENUM (CLIENT_ACCESS2_TSECSRD, ENABLE));
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4, MC_REG_BITS_ENUM (CLIENT_ACCESS4_GPUSRD2, ENABLE),
+ MC_REG_BITS_ENUM (CLIENT_ACCESS4_GPUSWR2, ENABLE));
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4, 0);
+ reg::Write(MC + MC_SECURITY_CARVEOUT2_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, 2),
+ 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));
+ }
+
+ void DisableArc() {
+ /* Configure IRAM top/bottom to point to memory ends (disabling redirection). */
+ reg::Write(MC + MC_IRAM_BOM, (~0u) & MC_IRAM_BOM_WRITE_MASK);
+ reg::Write(MC + MC_IRAM_TOM, ( 0u) & MC_IRAM_TOM_WRITE_MASK);
+
+ /* Lock the IRAM aperture. */
+ reg::ReadWrite(MC + MC_IRAM_REG_CTRL, MC_REG_BITS_ENUM(IRAM_REG_CTRL_IRAM_CFG_WRITE_ACCESS, DISABLED));
+
+ /* Disable the ARC clock gate override. */
+ reg::ReadWrite(CLK_RST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD, CLK_RST_REG_BITS_ENUM(LVL2_CLK_GATE_OVRD_ARC_CLK_OVR_ON, OFF));
+
+ /* Read IRAM REG CTRL to make sure our writes take. */
+ reg::Read(MC + MC_IRAM_REG_CTRL);
+ }
+
+ void FinalizeCarveoutSecureScratchRegisters() {
+ /* Define carveout scratch values. */
+ constexpr uintptr_t WarmbootCarveoutAddress = MemoryRegionDram.GetAddress();
+ constexpr size_t WarmbootCarveoutSize = 128_KB;
+
+ #define MC_ENABLE_CLIENT_ACCESS(INDEX, WHICH) MC_REG_BITS_ENUM(CLIENT_ACCESS##INDEX##_##WHICH, ENABLE)
+
+ constexpr u32 WarmbootCarveoutClientAccess0 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(0, AVPCARM7R),
+ MC_ENABLE_CLIENT_ACCESS(0, PPCSAHBSLVR));
+
+ constexpr u32 WarmbootCarveoutClientAccess1 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(1, AVPCARM7W));
+
+ #undef MC_ENABLE_CLIENT_ACCESS
+
+ constexpr u32 WarmbootCarveoutForceInternalAccess0 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS0_AVPCARM7R, ENABLE),
+ MC_REG_BITS_ENUM(CLIENT_ACCESS0_PPCSAHBSLVR, ENABLE));
+
+ constexpr u32 WarmbootCarveoutForceInternalAccess1 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS1_AVPCARM7W, ENABLE));
+
+ constexpr u32 WarmbootCarveoutConfig = 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, UNLOCKED),
+ MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, LOCKBIT_SECURE));
+
+ /* Save the carveout values into secure scratch. */
+
+ /* Save MC_SECURITY_CARVEOUT4_BOM. */
+ reg::ReadWrite(PMC + APBDEV_PMC_SECURE_SCRATCH51, REG_BITS_VALUE( 0, 15, WarmbootCarveoutAddress >> 17));
+
+ /* Save MC_SECURITY_CARVEOUT4_BOM_HI. */
+ reg::ReadWrite(PMC + APBDEV_PMC_SECURE_SCRATCH16, REG_BITS_VALUE(30, 2, WarmbootCarveoutAddress >> 32));
+
+ /* Save MC_SECURITY_CARVEOUT4_SIZE_128KB. */
+ reg::ReadWrite(PMC + APBDEV_PMC_SECURE_SCRATCH55, REG_BITS_VALUE(12, 12, WarmbootCarveoutSize / 128_KB));
+
+ /* Save MC_SECURITY_CARVEOUT4_CLIENT_ACCESS. */
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH74, WarmbootCarveoutClientAccess0);
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH75, WarmbootCarveoutClientAccess1);
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH76, 0);
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH77, 0);
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH78, 0);
+
+ /* Save MC_SECURITY_CARVEOUT4_FORCE_INTERNAL_ACCESS. */
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH99, WarmbootCarveoutForceInternalAccess0);
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH100, WarmbootCarveoutForceInternalAccess1);
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH101, 0);
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH102, 0);
+ reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH103, 0);
+
+ /* Save MC_SECURITY_CARVEOUT4_CFG0. */
+ reg::ReadWrite(PMC + APBDEV_PMC_SECURE_SCRATCH39, REG_BITS_VALUE(0, 27, WarmbootCarveoutConfig));
+ }
+
+ void EnableBpmpSmmu() {
+ /* Define the ASID contents. */
+ constexpr int BpmpAsid = 1;
+ constexpr uintptr_t BpmpAsidPde = MemoryRegionPhysicalDeviceSecurityEngine.GetAddress();
+
+ /* Configure the ASID. */
+ reg::Write(MC + MC_SMMU_PTB_ASID, MC_REG_BITS_VALUE(SMMU_PTB_ASID_CURRENT_ASID, BpmpAsid));
+
+ reg::Write(MC + MC_SMMU_PTB_DATA, MC_REG_BITS_VALUE(SMMU_PTB_DATA_ASID_PDE_BASE, BpmpAsidPde / 4_KB),
+ MC_REG_BITS_ENUM (SMMU_PTB_DATA_ASID_NONSECURE, DISABLE),
+ MC_REG_BITS_ENUM (SMMU_PTB_DATA_ASID_WRITABLE, DISABLE),
+ MC_REG_BITS_ENUM (SMMU_PTB_DATA_ASID_READABLE, DISABLE));
+
+ /* Configure the BPMP and PPCS1 to use the asid. */
+ reg::Write(MC + MC_SMMU_AVPC_ASID, MC_REG_BITS_ENUM(SMMU_AVPC_ASID_AVPC_SMMU_ENABLE, ENABLE), MC_REG_BITS_VALUE(SMMU_AVPC_ASID_AVPC_ASID, BpmpAsid));
+ reg::Write(MC + MC_SMMU_PPCS1_ASID, MC_REG_BITS_ENUM(SMMU_PPCS1_ASID_PPCS1_SMMU_ENABLE, ENABLE), MC_REG_BITS_VALUE(SMMU_PPCS1_ASID_PPCS1_ASID, BpmpAsid));
+
+ /* 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_TLB_FLUSH, 0);
+ reg::Read (MC + MC_SMMU_TLB_CONFIG);
+ }
+
+ void ValidateResetExpected() {
+ /* We're coming out of reset, so check that we expected to come out of reset. */
+ if (!IsResetExpected()) {
+ secmon::SetError(pkg1::ErrorInfo_UnexpectedReset);
+ AMS_ABORT("unexpected reset");
+ }
+ SetResetExpected(false);
+ }
+
+ void ActmonInterruptHandler() {
+ SetError(pkg1::ErrorInfo_ActivityMonitorInterrupt);
+ AMS_ABORT("actmon observed bpmp wakeup");
+ }
+
+ void ExitChargerHiZMode() {
+ /* Setup I2c-1. */
+ pinmux::SetupI2c1();
+ clkrst::EnableI2c1Clock();
+
+ /* Initialize I2c-1. */
+ i2c::Initialize(i2c::Port_1);
+
+ /* Exit Hi-Z mode. */
+ charger::ExitHiZMode();
+
+ /* Disable clock to I2c-1. */
+ clkrst::DisableI2c1Clock();
+ }
+
+ bool IsExitLp0() {
+ return reg::Read(MC + MC_SECURITY_CFG3) == 0;
+ }
+
+ void LogExitLp0() {
+ /* NOTE: Nintendo only does this on dev, but we will always do it. */
+ if (true /* !pkg1::IsProduction() */) {
+ log::Initialize();
+ log::SendText("OHAYO\n", 6);
+ log::Flush();
+ }
+ }
+
+ void SetupForLp0Exit() {
+ /* Exit HiZ mode in charger, if we need to. */
+ if (smc::IsChargerHiZModeEnabled()) {
+ ExitChargerHiZMode();
+ }
+
+ /* Refill the random cache, which is volatile and thus wiped on warmboot. */
+ smc::FillRandomCache();
+
+ /* Unlock the security engine. */
+ secmon::smc::UnlockSecurityEngine();
+ }
+
+ }
+
+ 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 Setup1ForWarmboot() {
+ /* 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_TzramSaveKek);
+
+ /* Initialize pmc secure scratch. */
+ pmc::InitializeRandomScratch();
+
+ /* Setup secure registers. */
+ SetupSecureRegisters();
+
+ /* Setup the smmu. */
+ SetupSmmu();
+
+ /* Clear the cpu reset vector. */
+ reg::Write(EVP + 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 | 1);
+ 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 SetupSocSecurityWarmboot() {
+ /* Check that we're allowed to continue. */
+ ValidateResetExpected();
+
+ /* Unmap the tzram identity mapping. */
+ UnmapTzram();
+
+ /* If we're exiting LP0, there's a little more work for us to do. */
+ if (IsExitLp0()) {
+ /* Log that we're exiting LP0. */
+ LogExitLp0();
+
+ /* Perform initial setup. */
+ Setup1ForWarmboot();
+
+ /* Generate a random srk. */
+ se::GenerateSrk();
+
+ /* Setup the Soc security. */
+ SetupSocSecurity();
+
+ /* Set the PMC and MC as secure-only. */
+ SetupPmcAndMcSecure();
+
+ /* Perform Lp0-exit specific init. */
+ SetupForLp0Exit();
+
+ /* Setup the Soc protections. */
+ SetupSocProtections();
+ }
+
+ /* Perform remaining CPU initialization. */
+ SetupCpuCoreContext();
+ SetupCpuSErrorDebug();
+ }
+
+ void SetupSocProtections() {
+ /* Setup the GPU carveout. */
+ SetupGpuCarveout();
+
+ /* Disable the ARC. */
+ DisableArc();
+
+ /* Finalize and lock the carveout scratch registers. */
+ FinalizeCarveoutSecureScratchRegisters();
+ pmc::LockSecureRegister(pmc::SecureRegister_Carveout);
+
+ /* Clear all the BPMP exception vectors to a fixed value. */
+ constexpr u32 BpmpExceptionVector = 0x7D000000;
+ reg::Write(EVP + EVP_COP_RESET_VECTOR, BpmpExceptionVector);
+ reg::Write(EVP + EVP_COP_UNDEF_VECTOR, BpmpExceptionVector);
+ reg::Write(EVP + EVP_COP_SWI_VECTOR, BpmpExceptionVector);
+ reg::Write(EVP + EVP_COP_PREFETCH_ABORT_VECTOR, BpmpExceptionVector);
+ reg::Write(EVP + EVP_COP_DATA_ABORT_VECTOR, BpmpExceptionVector);
+ reg::Write(EVP + EVP_COP_RSVD_VECTOR, BpmpExceptionVector);
+ reg::Write(EVP + EVP_COP_IRQ_VECTOR, BpmpExceptionVector);
+ reg::Write(EVP + EVP_COP_FIQ_VECTOR, BpmpExceptionVector);
+
+ /* Disable arbitration for the bpmp. */
+ reg::ReadWrite(SYSTEM + AHB_ARBITRATION_DISABLE, AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_COP, DISABLE));
+
+ /* Turn on the SMMU for the BPMP. */
+ EnableBpmpSmmu();
+
+ /* Wait until the flow controller reports that the BPMP is halted. */
+ while (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP))) {
+ util::WaitMicroSeconds(1);
+ }
+
+ /* If JTAG is disabled, disable JTAG. */
+ if (!secmon::IsJtagEnabled()) {
+ reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
+ FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_JTAG, DISABLED));
+
+ /* If version is above 4.0.0, turn on the activity monitor to prevent booting up the bpmp. */
+ if (GetTargetFirmware() >= TargetFirmware_4_0_0) {
+ clkrst::EnableActmonClock();
+ actmon::StartMonitoringBpmp(ActmonInterruptHandler);
+ }
+ }
+ }
+
+ void SetupPmcAndMcSecure() {
+ /* Set the PMC secure. */
+ reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, ENABLE));
+
+ /* Set the MC secure. */
+ reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0, SLAVE_SECURITY_REG_BITS_ENUM(1, MC0, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, MC1, ENABLE),
+ SLAVE_SECURITY_REG_BITS_ENUM(1, MCB, ENABLE));
+ }
+
+ void SetupCpuCoreContext() {
+ /* Get the tsc frequency. */
+ const u32 tsc_frequency = reg::Read(MemoryRegionVirtualDeviceSysCtr0.GetAddress() + SYSCTR0_CNTFID0);
+
+ /* Setup the secure EL2/EL1 system registers. */
+ SetupSecureEl2AndEl1SystemRegisters();
+
+ /* Setup the non-secure system registers. */
+ SetupNonSecureSystemRegisters(tsc_frequency);
+
+ /* Reset the cpu flow controller registers. */
+ flow::ResetCpuRegisters(hw::GetCurrentCoreId());
+
+ /* Initialize the core unique gic registers. */
+ gic::InitializeCoreUnique();
+
+ /* Configure cpu fiq. */
+ constexpr int FiqInterruptId = 28;
+ gic::SetPriority (FiqInterruptId, gic::HighestPriority);
+ gic::SetInterruptGroup(FiqInterruptId, 0);
+ gic::SetEnable (FiqInterruptId, true);
+
+ /* Restore the cpu's debug registers. */
+ RestoreDebugRegisters();
+ }
+
+ void SetupCpuSErrorDebug() {
+ /* Get whether we should enable SError debug. */
+ const auto &bc_data = secmon::GetBootConfig().data;
+ const bool enabled = bc_data.IsDevelopmentFunctionEnabled() && bc_data.IsSErrorDebugEnabled();
+
+ /* Get and set scr_el3. */
+ {
+ util::BitPack32 scr;
+ HW_CPU_GET_SCR_EL3(scr);
+
+ scr.Set(enabled ? 0 : 1);
+ HW_CPU_SET_SCR_EL3(scr);
+ }
+
+ /* Prevent reordering instructions around this call. */
+ hw::InstructionSynchronizationBarrier();
+ }
+
+ void SetKernelCarveoutRegion(int index, uintptr_t address, size_t size) {
+ /* Configure the carveout. */
+ auto &carveout = g_kernel_carveouts[index];
+ carveout.address = address;
+ carveout.size = size;
+
+ SetupKernelCarveouts();
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere/program/source/secmon_setup.hpp b/exosphere/program/source/secmon_setup.hpp
new file mode 100644
index 0000000..f6e1f37
--- /dev/null
+++ b/exosphere/program/source/secmon_setup.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 {
+
+ constexpr inline u64 MemoryAttributeIndexNormal = 0;
+ constexpr inline u64 MemoryAttributeIndexDevice = 1;
+
+ constexpr inline int KernelCarveoutCount = 2;
+
+ constexpr size_t CarveoutSizeMax = 512_MB - 128_KB;
+
+ void SetupCpuMemoryControllersEnableMmu();
+ void SetupCpuCoreContext();
+ void SetupCpuSErrorDebug();
+
+ void SetupSocDmaControllers();
+ void SetupSocSecurity();
+ void SetupSocProtections();
+
+ void SetupPmcAndMcSecure();
+
+ void Setup1();
+
+ void SaveSecurityEngineAesKeySlotTestVector();
+
+ void SetKernelCarveoutRegion(int index, uintptr_t address, size_t size);
+
+}
\ No newline at end of file
diff --git a/exosphere/program/source/secmon_setup_warm.cpp b/exosphere/program/source/secmon_setup_warm.cpp
new file mode 100644
index 0000000..5603593
--- /dev/null
+++ b/exosphere/program/source/secmon_setup_warm.cpp
@@ -0,0 +1,286 @@
+/*
+ * 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 {
+
+ constexpr inline uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress();
+
+ using namespace ams::mmu;
+
+ constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRwCode = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwCode, MemoryAttributeIndexNormal);
+
+ 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::BitPack64 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();
+ }
+
+ bool IsExitLp0() {
+ return reg::Read(MC + MC_SECURITY_CFG3) == 0;
+ }
+
+ constexpr void AddPhysicalTzramIdentityMappingImpl(u64 *l1, u64 *l2, u64 *l3) {
+ /* Define extents. */
+ const uintptr_t start_address = MemoryRegionPhysicalTzram.GetAddress();
+ const size_t size = MemoryRegionPhysicalTzram.GetSize();
+ const uintptr_t end_address = start_address + size;
+
+ /* Flush cache for the L3 page table entries. */
+ {
+ const uintptr_t start = GetL3EntryIndex(start_address);
+ const uintptr_t end = GetL3EntryIndex(end_address);
+ for (uintptr_t i = start; i < end; i += hw::DataCacheLineSize / sizeof(*l3)) {
+ if (!std::is_constant_evaluated()) { hw::FlushDataCacheLine(l3 + i); }
+ }
+ }
+
+ /* Flush cache for the L2 page table entry. */
+ if (!std::is_constant_evaluated()) { hw::FlushDataCacheLine(l2 + GetL2EntryIndex(start_address)); }
+
+ /* Flush cache for the L1 page table entry. */
+ if (!std::is_constant_evaluated()) { hw::FlushDataCacheLine(l1 + GetL1EntryIndex(start_address)); }
+
+ /* Add the L3 mappings. */
+ SetL3BlockEntry(l3, start_address, start_address, size, MappingAttributesEl3SecureRwCode);
+
+ /* Add the L2 entry for the physical tzram region. */
+ SetL2TableEntry(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode);
+
+ /* Add the L1 entry for the physical region. */
+ SetL1TableEntry(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode);
+ static_assert(GetL1EntryIndex(MemoryRegionPhysical.GetAddress()) == 1);
+
+ /* Invalidate the data cache for the L3 page table entries. */
+ {
+ const uintptr_t start = GetL3EntryIndex(start_address);
+ const uintptr_t end = GetL3EntryIndex(end_address);
+ for (uintptr_t i = start; i < end; i += hw::DataCacheLineSize / sizeof(*l3)) {
+ if (!std::is_constant_evaluated()) { hw::InvalidateDataCacheLine(l3 + i); }
+ }
+ }
+
+ /* Flush cache for the L2 page table entry. */
+ if (!std::is_constant_evaluated()) { hw::InvalidateDataCacheLine(l2 + GetL2EntryIndex(start_address)); }
+
+ /* Flush cache for the L1 page table entry. */
+ if (!std::is_constant_evaluated()) { hw::InvalidateDataCacheLine(l1 + GetL1EntryIndex(start_address)); }
+ }
+
+ void AddPhysicalTzramIdentityMapping() {
+ /* Get page table extents. */
+ u64 * const l1 = MemoryRegionPhysicalTzramL1PageTable.GetPointer();
+ u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer();
+
+ /* Add the mapping. */
+ AddPhysicalTzramIdentityMappingImpl(l1, l2_l3, l2_l3);
+
+ /* Ensure that mappings are consistent. */
+ setup::EnsureMappingConsistency();
+ }
+
+ }
+
+ 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));
+ }
+ }
+
+ void SetupSocDmaControllersCpuMemoryControllersEnableMmuWarmboot() {
+ /* If this is being called from lp0 exit, we want to setup the soc dma controllers. */
+ if (IsExitLp0()) {
+ SetupSocDmaControllers();
+ }
+
+ /* Add a physical TZRAM identity map. */
+ AddPhysicalTzramIdentityMapping();
+
+ /* Initialize cpu memory controllers and the MMU. */
+ SetupCpuMemoryControllersEnableMmu();
+ }
+
+}
\ No newline at end of file
diff --git a/exosphere/program/source/secmon_spinlock.hpp b/exosphere/program/source/secmon_spinlock.hpp
new file mode 100644
index 0000000..3c095da
--- /dev/null
+++ b/exosphere/program/source/secmon_spinlock.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::secmon {
+
+ using SpinLockType = u32;
+
+ void AcquireSpinLock(SpinLockType &lock);
+ void ReleaseSpinLock(SpinLockType &lock);
+
+}
diff --git a/exosphere/program/source/secmon_spinlock.s b/exosphere/program/source/secmon_spinlock.s
new file mode 100644
index 0000000..0e4ef7f
--- /dev/null
+++ b/exosphere/program/source/secmon_spinlock.s
@@ -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 .
+ */
+
+.section .text._ZN3ams6secmon15AcquireSpinLockERj, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon15AcquireSpinLockERj
+_ZN3ams6secmon15AcquireSpinLockERj:
+ /* 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._ZN3ams6secmon15ReleaseSpinLockERj, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon15ReleaseSpinLockERj
+_ZN3ams6secmon15ReleaseSpinLockERj:
+ /* Release the spinlock. */
+ stlr wzr, [x0]
+
+ /* Return. */
+ ret
diff --git a/exosphere/program/source/secmon_stack_warm.s b/exosphere/program/source/secmon_stack_warm.s
new file mode 100644
index 0000000..85dad85
--- /dev/null
+++ b/exosphere/program/source/secmon_stack_warm.s
@@ -0,0 +1,21 @@
+/*
+ * 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 .warmboot.data._ZN3ams6secmon23CommonWarmbootStackLockE, "aw", %progbits
+.global _ZN3ams6secmon23CommonWarmbootStackLockE
+_ZN3ams6secmon23CommonWarmbootStackLockE:
+ /* Define storage for the global common warmboot stack bakery lock. */
+ .word 0
diff --git a/exosphere/program/source/secmon_start_virtual.s b/exosphere/program/source/secmon_start_virtual.s
new file mode 100644
index 0000000..d8b1356
--- /dev/null
+++ b/exosphere/program/source/secmon_start_virtual.s
@@ -0,0 +1,212 @@
+/*
+ * 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 4
+.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, #0
+ ldr x20, =0x1F01C0800
+ mov sp, x20
+
+ /* Setup X18 to point to the global context. */
+ ldr x18, =0x1F01FA000
+
+ /* Invoke main. */
+ bl _ZN3ams6secmon4MainEv
+
+ /* Clear boot code high. */
+ bl _ZN3ams6secmon17ClearBootCodeHighEv
+
+ /* Set the stack pointer to the core 3 exception stack address. */
+ ldr x20, =0x1F01F9000
+ mov sp, x20
+
+ /* Unmap the boot code region (and clear the low part). */
+ bl _ZN3ams6secmon13UnmapBootCodeEv
+
+ /* Initialize the random cache. */
+ /* NOTE: Nintendo does this much earlier, but we reuse volatile space. */
+ bl _ZN3ams6secmon3smc15FillRandomCacheEv
+
+ /* Jump to lower exception level. */
+ b _ZN3ams6secmon25JumpToLowerExceptionLevelEv
+
+.section .text._ZN3ams6secmon20StartWarmbootVirtualEv, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon20StartWarmbootVirtualEv
+_ZN3ams6secmon20StartWarmbootVirtualEv:
+ /* Set the stack pointer to the shared warmboot stack address. */
+ ldr x20, =0x1F01F67C0
+ mov sp, x20
+
+ /* Setup X18 to point to the global context. */
+ ldr x18, =0x1F01FA000
+
+ /* Perform final warmboot setup. */
+ bl _ZN3ams6secmon24SetupSocSecurityWarmbootEv
+
+ /* Jump to lower exception level. */
+ b _ZN3ams6secmon25JumpToLowerExceptionLevelEv
+
+.section .text._ZN3ams6secmon25JumpToLowerExceptionLevelEv, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon25JumpToLowerExceptionLevelEv
+_ZN3ams6secmon25JumpToLowerExceptionLevelEv:
+ /* Get the EntryContext. */
+ sub sp, sp, #0x10
+ mov x0, sp
+ bl _ZN3ams6secmon15GetEntryContextEPNS0_12EntryContextE
+
+ /* Load the entrypoint and argument from the context. */
+ ldr x19, [sp, #0x00]
+ ldr x0, [sp, #0x08]
+
+ /* Set the exception return address. */
+ msr elr_el3, x0
+
+ /* Get the core exception stack. */
+ bl _ZN3ams6secmon28GetCoreExceptionStackVirtualEv
+ mov sp, x0
+
+ /* Release our exclusive access to the common warmboot stack. */
+ bl _ZN3ams6secmon26ReleaseCommonWarmbootStackEv
+
+ /* Configure SPSR_EL3. */
+ mov x0, #0x3C5
+ msr spsr_el3, x0
+
+ /* Set x0 to the entry argument. */
+ mov x0, x19
+
+ /* Ensure instruction reordering doesn't happen around this point. */
+ isb
+
+ /* Return to lower level. */
+ eret
+
+ /* Infinite loop, though we should never get here. */
+ 1: b 1b
+
+.section .text._ZN3ams6secmon28GetCoreExceptionStackVirtualEv, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon28GetCoreExceptionStackVirtualEv
+_ZN3ams6secmon28GetCoreExceptionStackVirtualEv:
+ /* Get the current core id. */
+ mrs x0, mpidr_el1
+ and x0, x0, #3
+
+ /* Jump to the appropriate core's stack handler. */
+ cmp x0, #3
+ b.eq 3f
+
+ cmp x0, #2
+ b.eq 2f
+
+ cmp x0, #1
+ b.eq 1f
+
+ /* cmp x0, #0 */
+ /* b.eq 0f */
+
+ 0:
+ ldr x0, =0x1F01F6F00
+ ret
+ 1:
+ ldr x0, =0x1F01F6F80
+ ret
+ 2:
+ ldr x0, =0x1F01F7000
+ ret
+ 3:
+ ldr x0, =0x1F01F9000
+ ret
+
+.section .text._ZN3ams6secmon25AcquireCommonSmcStackLockEv, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon25AcquireCommonSmcStackLockEv
+_ZN3ams6secmon25AcquireCommonSmcStackLockEv:
+ /* Get the address of the lock. */
+ ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE
+
+ /* Take the lock. */
+ b _ZN3ams6secmon15AcquireSpinLockERj
+
+.section .text._ZN3ams6secmon25ReleaseCommonSmcStackLockEv, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon25ReleaseCommonSmcStackLockEv
+_ZN3ams6secmon25ReleaseCommonSmcStackLockEv:
+ /* Get the address of the lock. */
+ ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE
+
+ /* Release the lock. */
+ b _ZN3ams6secmon15ReleaseSpinLockERj
+
+.section .text._ZN3ams6secmon26ReleaseCommonWarmbootStackEv, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon26ReleaseCommonWarmbootStackEv
+_ZN3ams6secmon26ReleaseCommonWarmbootStackEv:
+ /* Get the virtual address of the lock. */
+ ldr x0, =_ZN3ams6secmon23CommonWarmbootStackLockE
+ ldr x1, =(0x1F00C0000 - 0x07C012000)
+ add x1, x1, x0
+
+ /* Get the bakery value for our core. */
+ mrs x0, mpidr_el1
+ and x0, x0, #3
+ ldrb w2, [x1, x0]
+
+ /* Clear our ticket number. */
+ and w2, w2, #(~0x7F)
+ strb w2, [x1, x0]
+
+ /* Flush the cache. */
+ dc civac, x1
+
+ /* Synchronize data for all cores. */
+ dsb sy
+
+ /* Send an event. */
+ sev
+
+ /* Return. */
+ ret
+
+.section .text._ZN3ams6secmon19PivotStackAndInvokeEPvPFvvE, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon19PivotStackAndInvokeEPvPFvvE
+_ZN3ams6secmon19PivotStackAndInvokeEPvPFvvE:
+ /* Pivot to use the provided stack pointer. */
+ mov sp, x0
+
+ /* Release our lock on the common smc stack. */
+ mov x19, x1
+ bl _ZN3ams6secmon25ReleaseCommonSmcStackLockEv
+
+ /* Invoke the function with the new stack. */
+ br x19
+
+.section .data._ZN3ams6secmon18CommonSmcStackLockE, "aw", %progbits
+.global _ZN3ams6secmon18CommonSmcStackLockE
+_ZN3ams6secmon18CommonSmcStackLockE:
+ /* Define storage for the global common smc stack spinlock. */
+ .word 0
diff --git a/exosphere/program/source/secmon_start_warm.s b/exosphere/program/source/secmon_start_warm.s
new file mode 100644
index 0000000..74eaa53
--- /dev/null
+++ b/exosphere/program/source/secmon_start_warm.s
@@ -0,0 +1,212 @@
+/*
+ * 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 .warmboot.text.start, "ax", %progbits
+.align 4
+.global _start_warm
+_start_warm:
+ /* mask all interrupts */
+ msr daifset, #0xF
+
+ /* Fixup hardware erratum */
+ ERRATUM_INVALIDATE_BTB_AT_BOOT
+
+ /* Acquire exclusive access to the common warmboot stack. */
+ bl _ZN3ams6secmon26AcquireCommonWarmbootStackEv
+
+ /* Set the stack pointer to the common warmboot stack address. */
+ msr spsel, #1
+ ldr x20, =0x7C0107C0
+ mov sp, x20
+
+ /* Perform warmboot setup. */
+ bl _ZN3ams6secmon59SetupSocDmaControllersCpuMemoryControllersEnableMmuWarmbootEv
+
+ /* Jump to the newly-mapped virtual address. */
+ b _ZN3ams6secmon20StartWarmbootVirtualEv
+
+
+/* void ams::secmon::AcquireCommonWarmbootStack() { */
+/* NOTE: This implements critical section enter via https://en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm */
+/* This algorithm is used because the MMU is not awake yet, so exclusive load/store instructions are not usable. */
+/* NOTE: Nintendo attempted to implement this algorithm themselves, but did not really understand how it works. */
+/* They use the same ticket number for all cores; this can lead to starvation and other problems. */
+.section .warmboot.text._ZN3ams6secmon26AcquireCommonWarmbootStackEv, "ax", %progbits
+.align 4
+.global _ZN3ams6secmon26AcquireCommonWarmbootStackEv
+_ZN3ams6secmon26AcquireCommonWarmbootStackEv:
+ /* BakeryLock *lock = std::addressof(secmon::CommonWarmBootStackLock); */
+ ldr x0, =_ZN3ams6secmon23CommonWarmbootStackLockE
+
+ /* const u32 id = GetCurrentCoreId(); */
+ mrs x8, mpidr_el1
+ and x8, x8, #3
+
+ /* lock->customers[id].is_entering = true; */
+ ldrb w2, [x0, x8]
+ orr w2, w2, #~0x7F
+ strb w2, [x0, x8]
+
+ /* const u8 ticket_0 = lock->customers[0].ticket_number; */
+ ldrb w4, [x0, #0]
+ and w4, w4, #0x7F
+
+ /* const u8 ticket_1 = lock->customers[1].ticket_number; */
+ ldrb w5, [x0, #1]
+ and w5, w5, #0x7F
+
+ /* const u8 ticket_2 = lock->customers[2].ticket_number; */
+ ldrb w6, [x0, #2]
+ and w6, w6, #0x7F
+
+ /* const u8 ticket_3 = lock->customers[3].ticket_number; */
+ ldrb w7, [x0, #3]
+ and w7, w7, #0x7F
+
+ /* u8 biggest_ticket = std::max(std::max(ticket_0, ticket_1), std::max(ticket_2, ticket_3)) */
+ cmp w4, w5
+ csel w2, w4, w5, hi
+ cmp w6, w7
+ csel w3, w6, w7, hi
+ cmp w2, w3
+ csel w2, w2, w3, hi
+
+ /* NOTE: The biggest a ticket can ever be is 4, so the general increment is safe and 7-bit increment is not needed. */
+ /* lock->customers[id] = { .is_entering = false, .ticket_number = ++biggest_ticket }; */
+ add w2, w2, #1
+ strb w2, [x0, x8]
+
+ /* Ensure instructions aren't reordered around this point. */
+ /* hw::DataSynchronizationBarrier(); */
+ dsb sy
+
+ /* hw::SendEvent(); */
+ sev
+
+ /* for (unsigned int i = 0; i < 4; ++i) { */
+ mov w3, #0
+1:
+ /* hw::SendEventLocal(); */
+ sevl
+
+ /* do { */
+2:
+ /* hw::WaitForEvent(); */
+ wfe
+ /* while (lock->customers[i].is_entering); */
+ ldrb w4, [x0, x3]
+ tbnz w4, #7, 2b
+
+ /* u8 their_ticket; */
+
+ /* hw::SendEventLocal(); */
+ sevl
+
+ /* do { */
+2:
+ /* hw::WaitForEvent(); */
+ wfe
+ /* their_ticket = lock->customers[i].ticket_number; */
+ ldrb w4, [x0, x3]
+ ands w4, w4, #0x7F
+ /* if (their_ticket == 0) { break; } */
+ b.eq 3f
+ /* while ((their_ticket > my_ticket) || (their_ticket == my_ticket && id > i)); */
+ cmp w2, w4
+ b.hi 2b
+ ccmp w8, w3, #0, eq
+ b.hi 2b
+
+ /* } */
+3:
+ add w3, w3, #1
+ cmp w3, #4
+ b.ne 1b
+
+ /* hw::DataMemoryBarrier(); */
+ dmb sy
+
+ ret
diff --git a/exosphere/program/source/secmon_user_power_management.cpp b/exosphere/program/source/secmon_user_power_management.cpp
new file mode 100644
index 0000000..4fb063b
--- /dev/null
+++ b/exosphere/program/source/secmon_user_power_management.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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_page_mapper.hpp"
+#include "secmon_user_power_management.hpp"
+
+#include "rebootstub_bin.h"
+
+namespace ams::secmon {
+
+ namespace {
+
+ constexpr inline const uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress();
+
+ constexpr inline const u32 RebootStubPhysicalAddress = MemoryRegionPhysicalIramRebootStub.GetAddress();
+
+ enum RebootStubAction {
+ RebootStubAction_ShutDown = 0,
+ RebootStubAction_JumpToPayload = 1,
+ };
+
+ NORETURN void PerformPmcReboot() {
+ /* Write MAIN_RST. */
+ reg::Write(PMC + APBDEV_PMC_CNTRL, 0x10);
+
+ while (true) {
+ /* ... */
+ }
+ }
+
+ void LoadRebootStub(u32 action) {
+ /* Configure the bootrom to boot to warmboot payload. */
+ reg::Write(PMC + APBDEV_PMC_SCRATCH0, 0x1);
+
+ /* Patch the bootrom to perform an SVC immediately after the second spare write. */
+ reg::Write(PMC + APBDEV_PMC_SCRATCH45, 0x2E38DFFF);
+ reg::Write(PMC + APBDEV_PMC_SCRATCH46, 0x6001DC28);
+
+ /* Patch the bootrom to jump to the reboot stub we'll prepare in iram on SVC. */
+ reg::Write(PMC + APBDEV_PMC_SCRATCH33, RebootStubPhysicalAddress);
+ reg::Write(PMC + APBDEV_PMC_SCRATCH40, 0x6000F208);
+
+ {
+ /* Map the iram page. */
+ AtmosphereIramPageMapper mapper(RebootStubPhysicalAddress);
+ AMS_ABORT_UNLESS(mapper.Map());
+
+ /* Copy the reboot stub. */
+ AMS_ABORT_UNLESS(mapper.CopyToMapping(RebootStubPhysicalAddress, rebootstub_bin, rebootstub_bin_size));
+
+ /* Set the reboot type. */
+ AMS_ABORT_UNLESS(mapper.CopyToMapping(RebootStubPhysicalAddress + 4, std::addressof(action), sizeof(action)));
+ }
+ }
+
+ }
+
+ void PerformUserRebootToRcm() {
+ /* Configure the bootrom to boot to rcm. */
+ reg::Write(PMC + APBDEV_PMC_SCRATCH0, 0x2);
+
+ /* Reboot. */
+ PerformPmcReboot();
+ }
+
+ void PerformUserRebootToPayload() {
+ /* Load our reboot stub to iram. */
+ LoadRebootStub(RebootStubAction_JumpToPayload);
+
+ /* Reboot. */
+ PerformPmcReboot();
+ }
+
+ void PerformUserShutDown() {
+ /* Load our reboot stub to iram. */
+ LoadRebootStub(RebootStubAction_ShutDown);
+
+ /* Reboot. */
+ PerformPmcReboot();
+ }
+
+}
diff --git a/exosphere/program/source/secmon_user_power_management.hpp b/exosphere/program/source/secmon_user_power_management.hpp
new file mode 100644
index 0000000..9a50e82
--- /dev/null
+++ b/exosphere/program/source/secmon_user_power_management.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
+
+namespace ams::secmon {
+
+ enum UserRebootType {
+ UserRebootType_None = 0,
+ UserRebootType_ToRcm = 1,
+ UserRebootType_ToPayload = 2,
+ };
+
+ void PerformUserRebootToRcm();
+ void PerformUserRebootToPayload();
+ void PerformUserShutDown();
+
+}
diff --git a/exosphere/program/source/smc/secmon_define_access_table.inc b/exosphere/program/source/smc/secmon_define_access_table.inc
new file mode 100644
index 0000000..d887523
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_define_access_table.inc
@@ -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 .
+ */
+
+using __ACCESS_TABLE_NAME__ = AccessTable<__ACCESS_TABLE_ADDRESS__, [] {
+ /* Declare a table. */
+ std::array table = {};
+
+ /* Declare a helper. */
+ auto SetRegisterAllowed = [&](uintptr_t reg) { SetRegisterTableAllowed(table, reg); };
+
+ /* Populate the table. */
+ #include __ACCESS_TABLE_INC__
+
+ return table;
+}()>;
+
+static_assert(__ACCESS_TABLE_NAME__::Address >= __ACCESS_TABLE_ADDRESS__);
diff --git a/exosphere/program/source/smc/secmon_define_mc01_access_table.inc b/exosphere/program/source/smc/secmon_define_mc01_access_table.inc
new file mode 100644
index 0000000..ecb142b
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_define_mc01_access_table.inc
@@ -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 .
+ */
+
+#define __ACCESS_TABLE_NAME__ Mc01AccessTable
+#define __ACCESS_TABLE_ADDRESS__ 0
+#define __ACCESS_TABLE_INC__ "secmon_mc01_access_table_data.inc"
+
+#include "secmon_define_access_table.inc"
+
+#undef __ACCESS_TABLE_INC__
+#undef __ACCESS_TABLE_ADDRESS__
+#undef __ACCESS_TABLE_NAME__
diff --git a/exosphere/program/source/smc/secmon_define_mc_access_table.inc b/exosphere/program/source/smc/secmon_define_mc_access_table.inc
new file mode 100644
index 0000000..bfc3da5
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_define_mc_access_table.inc
@@ -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 .
+ */
+
+#define __ACCESS_TABLE_NAME__ McAccessTable
+#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceMemoryController.GetAddress()
+#define __ACCESS_TABLE_INC__ "secmon_mc_access_table_data.inc"
+
+#include "secmon_define_access_table.inc"
+
+#undef __ACCESS_TABLE_INC__
+#undef __ACCESS_TABLE_ADDRESS__
+#undef __ACCESS_TABLE_NAME__
diff --git a/exosphere/program/source/smc/secmon_define_pmc_access_table.inc b/exosphere/program/source/smc/secmon_define_pmc_access_table.inc
new file mode 100644
index 0000000..9ce0f52
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_define_pmc_access_table.inc
@@ -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 .
+ */
+
+#define __ACCESS_TABLE_NAME__ PmcAccessTable
+#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDevicePmc.GetAddress()
+#define __ACCESS_TABLE_INC__ "secmon_pmc_access_table_data.inc"
+
+#include "secmon_define_access_table.inc"
+
+#undef __ACCESS_TABLE_INC__
+#undef __ACCESS_TABLE_ADDRESS__
+#undef __ACCESS_TABLE_NAME__
diff --git a/exosphere/program/source/smc/secmon_mc01_access_table_data.inc b/exosphere/program/source/smc/secmon_mc01_access_table_data.inc
new file mode 100644
index 0000000..4e4f68d
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_mc01_access_table_data.inc
@@ -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 .
+ */
+
+SetRegisterAllowed(MC_STAT_CONTROL); /* 0x100 */
+SetRegisterAllowed(MC_STAT_EMC_CLOCK_LIMIT); /* 0x108 */
+SetRegisterAllowed(MC_STAT_EMC_CLOCK_LIMIT_MSBS); /* 0x10C */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_LO); /* 0x118 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_HI); /* 0x11C */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_SPARE); /* 0x124 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_0); /* 0x128 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_1); /* 0x12C */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_2); /* 0x130 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_3); /* 0x134 */
+SetRegisterAllowed(MC_STAT_EMC_SET0_COUNT); /* 0x138 */
+SetRegisterAllowed(MC_STAT_EMC_SET0_COUNT_MSBS); /* 0x13C */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_LO); /* 0x158 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_HI); /* 0x15C */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_SPARE); /* 0x164 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_0); /* 0x168 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_1); /* 0x16C */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_2); /* 0x170 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_3); /* 0x174 */
+SetRegisterAllowed(MC_STAT_EMC_SET1_COUNT); /* 0x178 */
+SetRegisterAllowed(MC_STAT_EMC_SET1_COUNT_MSBS); /* 0x17C */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_UPPER); /* 0xA20 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_UPPER); /* 0xA24 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_4); /* 0xB88 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_4); /* 0xB8C */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_5); /* 0xBC4 */
+SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_5); /* 0xBC8 */
\ No newline at end of file
diff --git a/exosphere/program/source/smc/secmon_mc_access_table_data.inc b/exosphere/program/source/smc/secmon_mc_access_table_data.inc
new file mode 100644
index 0000000..5b97b8f
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_mc_access_table_data.inc
@@ -0,0 +1,195 @@
+/*
+ * 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 .
+ */
+
+SetRegisterAllowed(MC_INTSTATUS); /* 0x000 */
+SetRegisterAllowed(MC_INTMASK); /* 0x004 */
+SetRegisterAllowed(MC_ERR_STATUS); /* 0x008 */
+SetRegisterAllowed(MC_ERR_ADR); /* 0x00C */
+SetRegisterAllowed(MC_SMMU_CONFIG); /* 0x010 */
+SetRegisterAllowed(MC_SMMU_PTB_ASID); /* 0x01C */
+SetRegisterAllowed(MC_SMMU_PTB_DATA); /* 0x020 */
+SetRegisterAllowed(MC_SMMU_TLB_FLUSH); /* 0x030 */
+SetRegisterAllowed(MC_SMMU_PTC_FLUSH); /* 0x034 */
+SetRegisterAllowed(MC_EMEM_CFG); /* 0x050 */
+SetRegisterAllowed(MC_EMEM_ADR_CFG); /* 0x054 */
+SetRegisterAllowed(MC_EMEM_ARB_CFG); /* 0x090 */
+SetRegisterAllowed(MC_EMEM_ARB_OUTSTANDING_REQ); /* 0x094 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_RCD); /* 0x098 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_RP); /* 0x09C */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_RC); /* 0x0A0 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_RAS); /* 0x0A4 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_FAW); /* 0x0A8 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_RRD); /* 0x0AC */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_RAP2PRE); /* 0x0B0 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_WAP2PRE); /* 0x0B4 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_R2R); /* 0x0B8 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_W2W); /* 0x0BC */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_R2W); /* 0x0C0 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_W2R); /* 0x0C4 */
+SetRegisterAllowed(MC_EMEM_ARB_MISC2); /* 0x0C8 */
+SetRegisterAllowed(MC_EMEM_ARB_DA_TURNS); /* 0x0D0 */
+SetRegisterAllowed(MC_EMEM_ARB_DA_COVERS); /* 0x0D4 */
+SetRegisterAllowed(MC_EMEM_ARB_MISC0); /* 0x0D8 */
+SetRegisterAllowed(MC_EMEM_ARB_MISC1); /* 0x0DC */
+SetRegisterAllowed(MC_EMEM_ARB_RING1_THROTTLE); /* 0x0E0 */
+SetRegisterAllowed(MC_CLIENT_HOTRESET_CTRL); /* 0x200 */
+SetRegisterAllowed(MC_CLIENT_HOTRESET_STATUS); /* 0x204 */
+SetRegisterAllowed(MC_SMMU_AFI_ASID); /* 0x238 */
+SetRegisterAllowed(MC_SMMU_DC_ASID); /* 0x240 */
+SetRegisterAllowed(MC_SMMU_DCB_ASID); /* 0x244 */
+SetRegisterAllowed(MC_SMMU_HC_ASID); /* 0x250 */
+SetRegisterAllowed(MC_SMMU_HDA_ASID); /* 0x254 */
+SetRegisterAllowed(MC_SMMU_ISP2_ASID); /* 0x258 */
+SetRegisterAllowed(MC_SMMU_NVENC_ASID); /* 0x264 */
+SetRegisterAllowed(MC_SMMU_NV_ASID); /* 0x268 */
+SetRegisterAllowed(MC_SMMU_NV2_ASID); /* 0x26C */
+SetRegisterAllowed(MC_SMMU_PPCS_ASID); /* 0x270 */
+SetRegisterAllowed(MC_SMMU_SATA_ASID); /* 0x274 */
+SetRegisterAllowed(MC_SMMU_VI_ASID); /* 0x280 */
+SetRegisterAllowed(MC_SMMU_VIC_ASID); /* 0x284 */
+SetRegisterAllowed(MC_SMMU_XUSB_HOST_ASID); /* 0x288 */
+SetRegisterAllowed(MC_SMMU_XUSB_DEV_ASID); /* 0x28C */
+SetRegisterAllowed(MC_SMMU_TSEC_ASID); /* 0x294 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_AVPC_0); /* 0x2E4 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DC_0); /* 0x2E8 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DC_1); /* 0x2EC */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DCB_0); /* 0x2F4 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DCB_1); /* 0x2F8 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_HC_0); /* 0x310 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_HC_1); /* 0x314 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_MPCORE_0); /* 0x320 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_NVENC_0); /* 0x328 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_PPCS_0); /* 0x344 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_PPCS_1); /* 0x348 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_ISP2_0); /* 0x370 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_ISP2_1); /* 0x374 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_XUSB_0); /* 0x37C */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_XUSB_1); /* 0x380 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_TSEC_0); /* 0x390 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_VIC_0); /* 0x394 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_VI2_0); /* 0x398 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_GPU_0); /* 0x3AC */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMCA_0); /* 0x3B8 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMCAA_0); /* 0x3BC */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMC_0); /* 0x3C0 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMCAB_0); /* 0x3C4 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_NVDEC_0); /* 0x3D8 */
+SetRegisterAllowed(MC_LATENCY_ALLOWANCE_GPU2_0); /* 0x3E8 */
+SetRegisterAllowed(MC_DIS_PTSA_RATE); /* 0x41C */
+SetRegisterAllowed(MC_DIS_PTSA_MIN); /* 0x420 */
+SetRegisterAllowed(MC_DIS_PTSA_MAX); /* 0x424 */
+SetRegisterAllowed(MC_DISB_PTSA_RATE); /* 0x428 */
+SetRegisterAllowed(MC_DISB_PTSA_MIN); /* 0x42C */
+SetRegisterAllowed(MC_DISB_PTSA_MAX); /* 0x430 */
+SetRegisterAllowed(MC_VE_PTSA_RATE); /* 0x434 */
+SetRegisterAllowed(MC_VE_PTSA_MIN); /* 0x438 */
+SetRegisterAllowed(MC_VE_PTSA_MAX); /* 0x43C */
+SetRegisterAllowed(MC_MLL_MPCORER_PTSA_RATE); /* 0x44C */
+SetRegisterAllowed(MC_RING1_PTSA_RATE); /* 0x47C */
+SetRegisterAllowed(MC_RING1_PTSA_MIN); /* 0x480 */
+SetRegisterAllowed(MC_RING1_PTSA_MAX); /* 0x484 */
+SetRegisterAllowed(MC_PCX_PTSA_RATE); /* 0x4AC */
+SetRegisterAllowed(MC_PCX_PTSA_MIN); /* 0x4B0 */
+SetRegisterAllowed(MC_PCX_PTSA_MAX); /* 0x4B4 */
+SetRegisterAllowed(MC_MSE_PTSA_RATE); /* 0x4C4 */
+SetRegisterAllowed(MC_MSE_PTSA_MIN); /* 0x4C8 */
+SetRegisterAllowed(MC_MSE_PTSA_MAX); /* 0x4CC */
+SetRegisterAllowed(MC_AHB_PTSA_RATE); /* 0x4DC */
+SetRegisterAllowed(MC_AHB_PTSA_MIN); /* 0x4E0 */
+SetRegisterAllowed(MC_AHB_PTSA_MAX); /* 0x4E4 */
+SetRegisterAllowed(MC_APB_PTSA_RATE); /* 0x4E8 */
+SetRegisterAllowed(MC_APB_PTSA_MIN); /* 0x4EC */
+SetRegisterAllowed(MC_APB_PTSA_MAX); /* 0x4F0 */
+SetRegisterAllowed(MC_FTOP_PTSA_RATE); /* 0x50C */
+SetRegisterAllowed(MC_HOST_PTSA_RATE); /* 0x518 */
+SetRegisterAllowed(MC_HOST_PTSA_MIN); /* 0x51C */
+SetRegisterAllowed(MC_HOST_PTSA_MAX); /* 0x520 */
+SetRegisterAllowed(MC_USBX_PTSA_RATE); /* 0x524 */
+SetRegisterAllowed(MC_USBX_PTSA_MIN); /* 0x528 */
+SetRegisterAllowed(MC_USBX_PTSA_MAX); /* 0x52C */
+SetRegisterAllowed(MC_USBD_PTSA_RATE); /* 0x530 */
+SetRegisterAllowed(MC_USBD_PTSA_MIN); /* 0x534 */
+SetRegisterAllowed(MC_USBD_PTSA_MAX); /* 0x538 */
+SetRegisterAllowed(MC_GK_PTSA_RATE); /* 0x53C */
+SetRegisterAllowed(MC_GK_PTSA_MIN); /* 0x540 */
+SetRegisterAllowed(MC_GK_PTSA_MAX); /* 0x544 */
+SetRegisterAllowed(MC_AUD_PTSA_RATE); /* 0x548 */
+SetRegisterAllowed(MC_AUD_PTSA_MIN); /* 0x54C */
+SetRegisterAllowed(MC_AUD_PTSA_MAX); /* 0x550 */
+SetRegisterAllowed(MC_VICPC_PTSA_RATE); /* 0x554 */
+SetRegisterAllowed(MC_VICPC_PTSA_MIN); /* 0x558 */
+SetRegisterAllowed(MC_VICPC_PTSA_MAX); /* 0x55C */
+SetRegisterAllowed(MC_JPG_PTSA_RATE); /* 0x584 */
+SetRegisterAllowed(MC_JPG_PTSA_MIN); /* 0x588 */
+SetRegisterAllowed(MC_JPG_PTSA_MAX); /* 0x58C */
+SetRegisterAllowed(MC_GK2_PTSA_RATE); /* 0x610 */
+SetRegisterAllowed(MC_GK2_PTSA_MIN); /* 0x614 */
+SetRegisterAllowed(MC_GK2_PTSA_MAX); /* 0x618 */
+SetRegisterAllowed(MC_SDM_PTSA_RATE); /* 0x61C */
+SetRegisterAllowed(MC_SDM_PTSA_MIN); /* 0x620 */
+SetRegisterAllowed(MC_SDM_PTSA_MAX); /* 0x624 */
+SetRegisterAllowed(MC_HDAPC_PTSA_RATE); /* 0x628 */
+SetRegisterAllowed(MC_HDAPC_PTSA_MIN); /* 0x62C */
+SetRegisterAllowed(MC_HDAPC_PTSA_MAX); /* 0x630 */
+SetRegisterAllowed(MC_SEC_CARVEOUT_BOM); /* 0x670 */
+SetRegisterAllowed(MC_SEC_CARVEOUT_SIZE_MB); /* 0x674 */
+SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A); /* 0x690 */
+SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB); /* 0x694 */
+SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B); /* 0x698 */
+SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB); /* 0x69C */
+SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C); /* 0x6A0 */
+SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB); /* 0x6A4 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_RFCPB); /* 0x6C0 */
+SetRegisterAllowed(MC_EMEM_ARB_TIMING_CCDMW); /* 0x6C4 */
+SetRegisterAllowed(MC_EMEM_ARB_REFPB_HP_CTRL); /* 0x6F0 */
+SetRegisterAllowed(MC_EMEM_ARB_REFPB_BANK_CTRL); /* 0x6F4 */
+SetRegisterAllowed(MC_PTSA_GRANT_DECREMENT); /* 0x960 */
+SetRegisterAllowed(MC_CLIENT_HOTRESET_CTRL_1); /* 0x970 */
+SetRegisterAllowed(MC_CLIENT_HOTRESET_STATUS_1); /* 0x974 */
+SetRegisterAllowed(MC_SMMU_PTC_FLUSH_1); /* 0x9B8 */
+SetRegisterAllowed(MC_SMMU_DC1_ASID); /* 0xA88 */
+SetRegisterAllowed(MC_SMMU_SDMMC1A_ASID); /* 0xA94 */
+SetRegisterAllowed(MC_SMMU_SDMMC2A_ASID); /* 0xA98 */
+SetRegisterAllowed(MC_SMMU_SDMMC3A_ASID); /* 0xA9C */
+SetRegisterAllowed(MC_SMMU_SDMMC4A_ASID); /* 0xAA0 */
+SetRegisterAllowed(MC_SMMU_ISP2B_ASID); /* 0xAA4 */
+SetRegisterAllowed(MC_SMMU_GPU_ASID); /* 0xAA8 */
+SetRegisterAllowed(MC_SMMU_GPUB_ASID); /* 0xAAC */
+SetRegisterAllowed(MC_SMMU_PPCS2_ASID); /* 0xAB0 */
+SetRegisterAllowed(MC_SMMU_NVDEC_ASID); /* 0xAB4 */
+SetRegisterAllowed(MC_SMMU_APE_ASID); /* 0xAB8 */
+SetRegisterAllowed(MC_SMMU_SE_ASID); /* 0xABC */
+SetRegisterAllowed(MC_SMMU_NVJPG_ASID); /* 0xAC0 */
+SetRegisterAllowed(MC_SMMU_HC1_ASID); /* 0xAC4 */
+SetRegisterAllowed(MC_SMMU_SE1_ASID); /* 0xAC8 */
+SetRegisterAllowed(MC_SMMU_AXIAP_ASID); /* 0xACC */
+SetRegisterAllowed(MC_SMMU_ETR_ASID); /* 0xAD0 */
+SetRegisterAllowed(MC_SMMU_TSECB_ASID); /* 0xAD4 */
+SetRegisterAllowed(MC_SMMU_TSEC1_ASID); /* 0xAD8 */
+SetRegisterAllowed(MC_SMMU_TSECB1_ASID); /* 0xADC */
+SetRegisterAllowed(MC_SMMU_NVDEC1_ASID); /* 0xAE0 */
+SetRegisterAllowed(MC_EMEM_ARB_DHYST_CTRL); /* 0xBCC */
+SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0); /* 0xBD0 */
+SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1); /* 0xBD4 */
+SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2); /* 0xBD8 */
+SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3); /* 0xBDC */
+SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4); /* 0xBE0 */
+SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5); /* 0xBE4 */
+SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6); /* 0xBE8 */
+SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7); /* 0xBEC */
+SetRegisterAllowed(MC_ERR_GENERALIZED_CARVEOUT_STATUS); /* 0xC00 */
+SetRegisterAllowed(MC_SECURITY_CARVEOUT2_BOM); /* 0xC5C */
+SetRegisterAllowed(MC_SECURITY_CARVEOUT3_BOM); /* 0xCAC */
\ No newline at end of file
diff --git a/exosphere/program/source/smc/secmon_pmc_access_table_data.inc b/exosphere/program/source/smc/secmon_pmc_access_table_data.inc
new file mode 100644
index 0000000..e94be6d
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_pmc_access_table_data.inc
@@ -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 .
+ */
+
+SetRegisterAllowed(APBDEV_PMC_CNTRL); /* 0x000 */
+SetRegisterAllowed(APBDEV_PMC_WAKE_MASK); /* 0x00C */
+SetRegisterAllowed(APBDEV_PMC_WAKE_LVL); /* 0x010 */
+SetRegisterAllowed(APBDEV_PMC_WAKE_STATUS); /* 0x014 */
+SetRegisterAllowed(APBDEV_PMC_DPD_PADS_ORIDE); /* 0x01C */
+SetRegisterAllowed(APBDEV_PMC_DPD_SAMPLE); /* 0x020 */
+SetRegisterAllowed(APBDEV_PMC_CLAMP_STATUS); /* 0x02C */
+SetRegisterAllowed(APBDEV_PMC_PWRGATE_TOGGLE); /* 0x030 */
+SetRegisterAllowed(APBDEV_PMC_REMOVE_CLAMPING_CMD ); /* 0x034 */
+SetRegisterAllowed(APBDEV_PMC_PWRGATE_STATUS); /* 0x038 */
+SetRegisterAllowed(APBDEV_PMC_PWRGOOD_TIMER); /* 0x03C */
+SetRegisterAllowed(APBDEV_PMC_BLINK_TIMER); /* 0x040 */
+SetRegisterAllowed(APBDEV_PMC_NO_IOPOWER); /* 0x044 */
+SetRegisterAllowed(APBDEV_PMC_PWR_DET); /* 0x048 */
+SetRegisterAllowed(APBDEV_PMC_AUTO_WAKE_LVL_MASK); /* 0x0DC */
+SetRegisterAllowed(APBDEV_PMC_WAKE_DELAY); /* 0x0E0 */
+SetRegisterAllowed(APBDEV_PMC_PWR_DET_VAL); /* 0x0E4 */
+SetRegisterAllowed(APBDEV_PMC_WAKE2_MASK); /* 0x160 */
+SetRegisterAllowed(APBDEV_PMC_WAKE2_LVL); /* 0x164 */
+SetRegisterAllowed(APBDEV_PMC_WAKE2_STATUS); /* 0x168 */
+SetRegisterAllowed(APBDEV_PMC_AUTO_WAKE2_LVL_MASK ); /* 0x170 */
+SetRegisterAllowed(APBDEV_PMC_CLK_OUT_CNTRL); /* 0x1A8 */
+SetRegisterAllowed(APBDEV_PMC_IO_DPD_REQ); /* 0x1B8 */
+SetRegisterAllowed(APBDEV_PMC_IO_DPD_STATUS); /* 0x1BC */
+SetRegisterAllowed(APBDEV_PMC_IO_DPD2_REQ); /* 0x1C0 */
+SetRegisterAllowed(APBDEV_PMC_IO_DPD2_STATUS); /* 0x1C4 */
+SetRegisterAllowed(APBDEV_PMC_SEL_DPD_TIM); /* 0x1C8 */
+SetRegisterAllowed(APBDEV_PMC_TSC_MULT); /* 0x2B4 */
+SetRegisterAllowed(APBDEV_PMC_GPU_RG_CNTRL); /* 0x2D4 */
+SetRegisterAllowed(APBDEV_PMC_CNTRL2); /* 0x440 */
+SetRegisterAllowed(APBDEV_PMC_WAKE_DEBOUNCE_EN); /* 0x4D8 */
\ No newline at end of file
diff --git a/exosphere/program/source/smc/secmon_random_cache.cpp b/exosphere/program/source/smc/secmon_random_cache.cpp
new file mode 100644
index 0000000..f14330b
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_random_cache.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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_smc_common.hpp"
+#include "secmon_random_cache.hpp"
+
+namespace ams::secmon::smc {
+
+ namespace {
+
+ constinit int g_random_offset_low = 0;
+ constinit int g_random_offset_high = 0;
+
+ void FillRandomCache(int offset, int size) {
+ /* Get the cache. */
+ u8 * const random_cache_loc = GetRandomBytesCache() + offset;
+
+ /* Flush the region we're about to fill to ensure consistency with the SE. */
+ hw::FlushDataCache(random_cache_loc, size);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Generate random bytes. */
+ se::GenerateRandomBytes(random_cache_loc, size);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Flush to ensure the CPU sees consistent data for the region. */
+ hw::FlushDataCache(random_cache_loc, size);
+ hw::DataSynchronizationBarrierInnerShareable();
+ }
+
+ }
+
+ void FillRandomCache() {
+ /* Fill the cache. */
+ FillRandomCache(0, GetRandomBytesCacheSize());
+
+ /* Set the extents. */
+ g_random_offset_low = 0;
+ g_random_offset_high = GetRandomBytesCacheSize() - 1;
+ }
+
+ void RefillRandomCache() {
+ /* Check that we need to do any refilling. */
+ if (const int used_start = (g_random_offset_high + 1) % GetRandomBytesCacheSize(); used_start != g_random_offset_low) {
+ if (used_start < g_random_offset_low) {
+ /* The region we need to fill is after used_start but before g_random_offset_low. */
+ const auto size = g_random_offset_low - used_start;
+ FillRandomCache(used_start, size);
+ g_random_offset_high += size;
+ } else {
+ /* We need to fill the space from high to the end and from low to start. */
+ const int high_size = GetRandomBytesCacheSize() - used_start;
+ if (high_size > 0) {
+ FillRandomCache(used_start, high_size);
+ g_random_offset_high += high_size;
+ }
+
+ const int low_size = g_random_offset_low;
+ if (low_size > 0) {
+ FillRandomCache(0, low_size);
+ g_random_offset_high += low_size;
+ }
+
+ }
+
+ g_random_offset_high %= GetRandomBytesCacheSize();
+ }
+ }
+
+ void GetRandomFromCache(void *dst, size_t size) {
+ /* Copy out the requested size. */
+ std::memcpy(dst, GetRandomBytesCache() + g_random_offset_low, size);
+
+ /* Advance. */
+ g_random_offset_low += size;
+
+ /* Ensure that at all times g_random_offset_low is not within 0x38 bytes of the end of the pool. */
+ if (g_random_offset_low + MaxRandomBytes >= GetRandomBytesCacheSize()) {
+ g_random_offset_low = 0;
+ }
+ }
+
+}
diff --git a/exosphere/program/source/smc/secmon_random_cache.hpp b/exosphere/program/source/smc/secmon_random_cache.hpp
new file mode 100644
index 0000000..e1e0082
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_random_cache.hpp
@@ -0,0 +1,28 @@
+/*
+ * 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 {
+
+ constexpr inline size_t MaxRandomBytes = sizeof(SmcArguments) - sizeof(SmcArguments{}.r[0]);
+
+ void FillRandomCache();
+ void RefillRandomCache();
+ void GetRandomFromCache(void *dst, size_t size);
+
+}
diff --git a/exosphere/program/source/smc/secmon_smc_aes.cpp b/exosphere/program/source/smc/secmon_smc_aes.cpp
new file mode 100644
index 0000000..cfb992e
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_smc_aes.cpp
@@ -0,0 +1,768 @@
+/*
+ * 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_key_storage.hpp"
+#include "../secmon_misc.hpp"
+#include "../secmon_page_mapper.hpp"
+#include "secmon_smc_aes.hpp"
+#include "secmon_smc_device_unique_data.hpp"
+#include "secmon_smc_se_lock.hpp"
+
+namespace ams::secmon::smc {
+
+ namespace {
+
+ constexpr inline auto AesKeySize = se::AesBlockSize;
+ constexpr inline size_t CmacSizeMax = 1_KB;
+ constexpr inline size_t DeviceUniqueDataSizeMin = 0x130;
+ constexpr inline size_t DeviceUniqueDataSizeMax = 0x240;
+
+ enum SealKey {
+ SealKey_LoadAesKey = 0,
+ SealKey_DecryptDeviceUniqueData = 1,
+ SealKey_ImportLotusKey = 2,
+ SealKey_ImportEsDeviceKey = 3,
+ SealKey_ReencryptDeviceUniqueData = 4,
+ SealKey_ImportSslKey = 5,
+ SealKey_ImportEsClientCertKey = 6,
+
+ SealKey_Count,
+ };
+
+ enum KeyType {
+ KeyType_Default = 0,
+ KeyType_NormalOnly = 1,
+ KeyType_RecoveryOnly = 2,
+ KeyType_NormalAndRecovery = 3,
+
+ KeyType_Count,
+ };
+
+ enum CipherMode {
+ CipherMode_CbcEncryption = 0,
+ CipherMode_CbcDecryption = 1,
+ CipherMode_Ctr = 2,
+ CipherMode_Cmac = 3,
+ };
+
+ enum SpecificAesKey {
+ SpecificAesKey_CalibrationEncryption0 = 0,
+ SpecificAesKey_CalibrationEncryption1 = 1,
+
+ SpecificAesKey_Count,
+ };
+
+ enum DeviceUniqueData {
+ DeviceUniqueData_DecryptDeviceUniqueData = 0,
+ DeviceUniqueData_ImportLotusKey = 1,
+ DeviceUniqueData_ImportEsDeviceKey = 2,
+ DeviceUniqueData_ImportSslKey = 3,
+ DeviceUniqueData_ImportEsClientCertKey = 4,
+
+ DeviceUniqueData_Count,
+ };
+
+ /* Ensure that our "subtract one" simplification is valid for the cases we care about. */
+ static_assert(DeviceUniqueData_ImportLotusKey - 1 == ImportRsaKey_Lotus);
+ static_assert(DeviceUniqueData_ImportEsDeviceKey - 1 == ImportRsaKey_EsDrmCert);
+ static_assert(DeviceUniqueData_ImportSslKey - 1 == ImportRsaKey_Ssl);
+ static_assert(DeviceUniqueData_ImportEsClientCertKey - 1 == ImportRsaKey_EsClientCert);
+
+ constexpr ImportRsaKey ConvertToImportRsaKey(DeviceUniqueData data) {
+ /* Not necessary, but if this is invoked at compile-time this will force a compile-time error. */
+ AMS_ASSUME(data != DeviceUniqueData_DecryptDeviceUniqueData);
+ AMS_ASSUME(data < DeviceUniqueData_Count);
+
+ return static_cast(static_cast(data) - 1);
+ }
+
+ enum SecureData {
+ SecureData_Calibration = 0,
+ SecureData_SafeMode = 1,
+ SecureData_UserSystemProperEncryption = 2,
+ SecureData_UserSystem = 3,
+
+ SecureData_Count,
+ };
+
+ struct GenerateAesKekOption {
+ using IsDeviceUnique = util::BitPack32::Field<0, 1, bool>;
+ using KeyTypeIndex = util::BitPack32::Field<1, 4, KeyType>;
+ using SealKeyIndex = util::BitPack32::Field<5, 3, SealKey>;
+ using Reserved = util::BitPack32::Field<8, 24, u32>;
+ };
+
+ struct ComputeAesOption {
+ using KeySlot = util::BitPack32::Field<0, 3, int>;
+ using CipherModeIndex = util::BitPack32::Field<4, 2, CipherMode>;
+ };
+
+ struct DecryptDeviceUniqueDataOption {
+ using DeviceUniqueDataIndex = util::BitPack32::Field<0, 3, DeviceUniqueData>;
+ using Reserved = util::BitPack32::Field<3, 29, u32>;
+ };
+
+ constexpr const u8 SealKeySources[SealKey_Count][AesKeySize] = {
+ [SealKey_LoadAesKey] = { 0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6 },
+ [SealKey_DecryptDeviceUniqueData] = { 0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06 },
+ [SealKey_ImportLotusKey] = { 0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63 },
+ [SealKey_ImportEsDeviceKey] = { 0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D },
+ [SealKey_ReencryptDeviceUniqueData] = { 0xE1, 0xA8, 0xAA, 0x6A, 0x2D, 0x9C, 0xDE, 0x43, 0x0C, 0xDE, 0xC6, 0x17, 0xF6, 0xC7, 0xF1, 0xDE },
+ [SealKey_ImportSslKey] = { 0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9 },
+ [SealKey_ImportEsClientCertKey] = { 0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE6, 0xD8, 0x9A, 0xCF, 0xE5, 0x3F, 0xB3, 0x4B },
+ };
+
+ constexpr const u8 KeyTypeSources[KeyType_Count][AesKeySize] = {
+ [KeyType_Default] = { 0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9 },
+ [KeyType_NormalOnly] = { 0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77 },
+ [KeyType_RecoveryOnly] = { 0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81 },
+ [KeyType_NormalAndRecovery] = { 0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B },
+ };
+
+ constexpr const u8 SealKeyMasks[SealKey_Count][AesKeySize] = {
+ [SealKey_LoadAesKey] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ [SealKey_DecryptDeviceUniqueData] = { 0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74 },
+ [SealKey_ImportLotusKey] = { 0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C },
+ [SealKey_ImportEsDeviceKey] = { 0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB },
+ [SealKey_ReencryptDeviceUniqueData] = { 0x59, 0xD9, 0x31, 0xF4, 0xA7, 0x97, 0xB8, 0x14, 0x40, 0xD6, 0xA2, 0x60, 0x2B, 0xED, 0x15, 0x31 },
+ [SealKey_ImportSslKey] = { 0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75 },
+ [SealKey_ImportEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 },
+ };
+
+ constexpr const SealKey DeviceUniqueDataToSealKey[DeviceUniqueData_Count] = {
+ [DeviceUniqueData_DecryptDeviceUniqueData] = SealKey_DecryptDeviceUniqueData,
+ [DeviceUniqueData_ImportLotusKey] = SealKey_ImportLotusKey,
+ [DeviceUniqueData_ImportEsDeviceKey] = SealKey_ImportEsDeviceKey,
+ [DeviceUniqueData_ImportSslKey] = SealKey_ImportSslKey,
+ [DeviceUniqueData_ImportEsClientCertKey] = SealKey_ImportEsClientCertKey,
+ };
+
+ constexpr const u8 CalibrationKeySource[AesKeySize] = {
+ 0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95
+ };
+
+ constexpr const u8 EsCommonKeySources[EsCommonKeyType_Count][AesKeySize] = {
+ [EsCommonKeyType_TitleKey] = { 0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B },
+ [EsCommonKeyType_ArchiveKey] = { 0x3B, 0x78, 0xF2, 0x61, 0x0F, 0x9D, 0x5A, 0xE2, 0x7B, 0x4E, 0x45, 0xAF, 0xCB, 0x0B, 0x67, 0x4D },
+ };
+
+ constexpr const u8 EsSealKeySource[AesKeySize] = {
+ 0xCB, 0xB7, 0x6E, 0x38, 0xA1, 0xCB, 0x77, 0x0F, 0xB2, 0xA5, 0xB2, 0x9D, 0xD8, 0x56, 0x9F, 0x76
+ };
+
+ constexpr const u8 SecureDataSource[AesKeySize] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ constexpr const u8 SecureDataCounters[][AesKeySize] = {
+ [SecureData_Calibration] = { 0x3C, 0xD5, 0x92, 0xEC, 0x68, 0x31, 0x4A, 0x06, 0xD4, 0x1B, 0x0C, 0xD9, 0xF6, 0x2E, 0xD9, 0xE9 },
+ [SecureData_SafeMode] = { 0x50, 0x81, 0xCF, 0x77, 0x18, 0x11, 0xD7, 0x0D, 0x13, 0x29, 0x60, 0xED, 0x4B, 0x21, 0x3E, 0xFC },
+ [SecureData_UserSystemProperEncryption] = { 0x98, 0xCB, 0x4C, 0xEB, 0x15, 0xF1, 0x4A, 0x5A, 0x7A, 0x86, 0xB6, 0xF1, 0x94, 0x66, 0xF4, 0x9D },
+ };
+
+ constexpr const u8 SecureDataTweaks[][AesKeySize] = {
+ [SecureData_Calibration] = { 0xAC, 0xCA, 0x9A, 0xCA, 0xFF, 0x2E, 0xB9, 0x22, 0xCC, 0x1F, 0x4F, 0xAD, 0xDD, 0x77, 0x21, 0x1E },
+ [SecureData_SafeMode] = { 0x6E, 0xF8, 0x2A, 0x1A, 0xE0, 0x4F, 0xC3, 0x20, 0x08, 0x7B, 0xBA, 0x50, 0xC0, 0xCD, 0x7B, 0x39 },
+ [SecureData_UserSystemProperEncryption] = { 0x6D, 0x02, 0x56, 0x2D, 0xF4, 0x3D, 0x0A, 0x15, 0xB1, 0x34, 0x5C, 0xC2, 0x84, 0x4C, 0xD4, 0x28 },
+ };
+
+ constexpr const u8 *GetSecureDataCounter(SecureData which) {
+ switch (which) {
+ case SecureData_Calibration:
+ return SecureDataCounters[SecureData_Calibration];
+ case SecureData_SafeMode:
+ return SecureDataCounters[SecureData_SafeMode];
+ case SecureData_UserSystem:
+ case SecureData_UserSystemProperEncryption:
+ return SecureDataCounters[SecureData_UserSystemProperEncryption];
+ default:
+ return nullptr;
+ }
+ }
+
+ constexpr const u8 *GetSecureDataTweak(SecureData which) {
+ switch (which) {
+ case SecureData_Calibration:
+ return SecureDataTweaks[SecureData_Calibration];
+ case SecureData_SafeMode:
+ return SecureDataTweaks[SecureData_SafeMode];
+ case SecureData_UserSystem:
+ case SecureData_UserSystemProperEncryption:
+ return SecureDataTweaks[SecureData_UserSystemProperEncryption];
+ default:
+ return nullptr;
+ }
+ }
+
+ constexpr uintptr_t LinkedListAddressMinimum = secmon::MemoryRegionDram.GetAddress();
+ constexpr size_t LinkedListAddressRangeSize = 4_MB - 2_KB;
+ constexpr uintptr_t LinkedListAddressMaximum = LinkedListAddressMinimum + LinkedListAddressRangeSize;
+
+ constexpr size_t LinkedListSize = 12;
+
+ constexpr bool IsValidLinkedListAddress(uintptr_t address) {
+ return LinkedListAddressMinimum <= address && address <= (LinkedListAddressMaximum - LinkedListSize);
+ }
+
+ constinit bool g_is_compute_aes_completed = false;
+
+ void SecurityEngineDoneHandler() {
+ /* Check that the compute succeeded. */
+ se::ValidateAesOperationResult();
+
+ /* End the asynchronous operation. */
+ g_is_compute_aes_completed = true;
+ EndAsyncOperation();
+ }
+
+ SmcResult GetComputeAesResult(void *dst, size_t size) {
+ /* Arguments are unused. */
+ AMS_UNUSED(dst);
+ AMS_UNUSED(size);
+
+ /* Check that the operation is completed. */
+ SMC_R_UNLESS(g_is_compute_aes_completed, Busy);
+
+ /* Unlock the security engine and succeed. */
+ UnlockSecurityEngine();
+ return SmcResult::Success;
+ }
+
+ int PrepareMasterKey(int generation) {
+ if (generation == GetKeyGeneration()) {
+ return pkg1::AesKeySlot_Master;
+ }
+
+ constexpr int Slot = pkg1::AesKeySlot_Smc;
+ LoadMasterKey(Slot, generation);
+
+ return Slot;
+ }
+
+ int PrepareDeviceMasterKey(int generation) {
+ if (generation == pkg1::KeyGeneration_1_0_0) {
+ return pkg1::AesKeySlot_Device;
+ }
+ if (generation == GetKeyGeneration()) {
+ return pkg1::AesKeySlot_DeviceMaster;
+ }
+
+ constexpr int Slot = pkg1::AesKeySlot_Smc;
+ LoadDeviceMasterKey(Slot, generation);
+
+ return Slot;
+ }
+
+ void GetSecureDataImpl(u8 *dst, SecureData which, bool tweak) {
+ /* Compute the appropriate AES-CTR. */
+ se::ComputeAes128Ctr(dst, AesKeySize, pkg1::AesKeySlot_Device, SecureDataSource, AesKeySize, GetSecureDataCounter(which), AesKeySize);
+
+ /* Tweak, if we should. */
+ if (tweak) {
+ const u8 * const tweak = GetSecureDataTweak(which);
+
+ for (size_t i = 0; i < AesKeySize; ++i) {
+ dst[i] ^= tweak[i];
+ }
+ }
+ }
+
+ SmcResult GenerateAesKekImpl(SmcArguments &args) {
+ /* Decode arguments. */
+ u8 kek_source[AesKeySize];
+ std::memcpy(kek_source, std::addressof(args.r[1]), AesKeySize);
+
+ const int generation = std::max(static_cast(args.r[3]) - 1, pkg1::KeyGeneration_1_0_0);
+
+ const util::BitPack32 option = { static_cast(args.r[4]) };
+ const bool is_device_unique = option.Get();
+ const auto key_type = option.Get();
+ const auto seal_key = option.Get();
+ const u32 reserved = option.Get();
+
+ /* Validate arguments. */
+ SMC_R_UNLESS(reserved == 0, InvalidArgument);
+
+ if (is_device_unique) {
+ SMC_R_UNLESS(pkg1::IsValidDeviceUniqueKeyGeneration(generation), InvalidArgument);
+ } else {
+ SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument);
+ SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument);
+ }
+
+ SMC_R_UNLESS(0 <= key_type && key_type < KeyType_Count, InvalidArgument);
+ SMC_R_UNLESS(0 <= seal_key && seal_key < SealKey_Count, InvalidArgument);
+
+ switch (key_type) {
+ case KeyType_NormalOnly: SMC_R_UNLESS(!IsRecoveryBoot(), InvalidArgument); break;
+ case KeyType_RecoveryOnly: SMC_R_UNLESS( IsRecoveryBoot(), InvalidArgument); break;
+ default: break;
+ }
+
+ /* Declare temporary data storage. */
+ u8 static_source[AesKeySize];
+ u8 generated_key[AesKeySize];
+ u8 access_key[AesKeySize];
+
+ /* Derive the static source. */
+ for (size_t i = 0; i < sizeof(static_source); ++i) {
+ static_source[i] = KeyTypeSources[key_type][i] ^ SealKeyMasks[seal_key][i];
+ }
+
+ /* Get the seal key source. */
+ const u8 * const seal_key_source = SealKeySources[seal_key];
+
+ /* Get the key slot. */
+ const int slot = is_device_unique ? PrepareDeviceMasterKey(generation) : PrepareMasterKey(generation);
+
+ /* Derive a static generation kek. */
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, static_source, sizeof(static_source));
+
+ /* Decrypt the input with the static generation kek. */
+ se::DecryptAes128(generated_key, sizeof(generated_key), pkg1::AesKeySlot_Smc, kek_source, sizeof(kek_source));
+
+ /* Generate the seal key. */
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, seal_key_source, AesKeySize);
+
+ /* Seal the generated key. */
+ se::EncryptAes128(access_key, sizeof(access_key), pkg1::AesKeySlot_Smc, generated_key, sizeof(generated_key));
+
+ /* Copy the access key out. */
+ std::memcpy(std::addressof(args.r[1]), access_key, sizeof(access_key));
+ return SmcResult::Success;
+ }
+
+ SmcResult LoadAesKeyImpl(SmcArguments &args) {
+ /* Decode arguments. */
+ const int slot = args.r[1];
+
+ u8 access_key[AesKeySize];
+ std::memcpy(access_key, std::addressof(args.r[2]), sizeof(access_key));
+
+ u8 key_source[AesKeySize];
+ std::memcpy(key_source, std::addressof(args.r[4]), sizeof(key_source));
+
+ /* Validate arguments. */
+ SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument);
+
+ /* Get the seal key source. */
+ constexpr const u8 * const SealKeySource = SealKeySources[SealKey_LoadAesKey];
+
+ /* Derive the seal key. */
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, SealKeySource, AesKeySize);
+
+ /* Unseal the access key. */
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, access_key, sizeof(access_key));
+
+ /* Derive the key. */
+ se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_Smc, key_source, sizeof(key_source));
+
+ return SmcResult::Success;
+ }
+
+ SmcResult ComputeAesImpl(SmcArguments &args) {
+ /* Decode arguments. */
+ u8 iv[se::AesBlockSize];
+
+ const util::BitPack32 option = { static_cast(args.r[1]) };
+ std::memcpy(iv, std::addressof(args.r[2]), sizeof(iv));
+ const u32 input_address = args.r[4];
+ const u32 output_address = args.r[5];
+ const u32 size = args.r[6];
+
+ const int slot = option.Get();
+ const auto cipher_mode = option.Get();
+
+ /* Validate arguments. */
+ SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument);
+ SMC_R_UNLESS(util::IsAligned(size, se::AesBlockSize), InvalidArgument);
+ SMC_R_UNLESS(IsValidLinkedListAddress(input_address), InvalidArgument);
+ SMC_R_UNLESS(IsValidLinkedListAddress(output_address), InvalidArgument);
+
+ /* We're starting an aes operation, so reset the completion status. */
+ g_is_compute_aes_completed = false;
+
+ /* Dispatch the correct aes operation asynchronously. */
+ switch (cipher_mode) {
+ case CipherMode_CbcEncryption: se::EncryptAes128CbcAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break;
+ case CipherMode_CbcDecryption: se::DecryptAes128CbcAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break;
+ case CipherMode_Ctr: se::ComputeAes128CtrAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break;
+ case CipherMode_Cmac:
+ return SmcResult::NotImplemented;
+ default:
+ return SmcResult::InvalidArgument;
+ }
+
+ return SmcResult::Success;
+ }
+
+ SmcResult GenerateSpecificAesKeyImpl(SmcArguments &args) {
+ /* Decode arguments. */
+ u8 key_source[AesKeySize];
+ std::memcpy(key_source, std::addressof(args.r[1]), sizeof(key_source));
+
+ const int generation = GetTargetFirmware() >= TargetFirmware_4_0_0 ? std::max(static_cast(args.r[3]) - 1, pkg1::KeyGeneration_1_0_0) : pkg1::KeyGeneration_1_0_0;
+ const auto which = static_cast(args.r[4]);
+
+ /* Validate arguments. */
+ SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument);
+ SMC_R_UNLESS(which < SpecificAesKey_Count, InvalidArgument);
+
+ /* Generate the specific aes key. */
+ u8 output_key[AesKeySize];
+ if (fuse::GetPatchVersion() >= fuse::PatchVersion_Odnx02A2) {
+ const int slot = PrepareDeviceMasterKey(generation);
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, CalibrationKeySource, sizeof(CalibrationKeySource));
+ se::DecryptAes128(output_key, sizeof(output_key), pkg1::AesKeySlot_Smc, key_source, sizeof(key_source));
+ } else {
+ GetSecureDataImpl(output_key, SecureData_Calibration, which == SpecificAesKey_CalibrationEncryption1);
+ }
+
+ /* Copy the key to output. */
+ std::memcpy(std::addressof(args.r[1]), output_key, sizeof(output_key));
+ return SmcResult::Success;
+ }
+
+ SmcResult ComputeCmacImpl(SmcArguments &args) {
+ /* Decode arguments. */
+ const int slot = args.r[1];
+ const uintptr_t data_address = args.r[2];
+ const size_t data_size = args.r[3];
+
+ /* Declare buffer for user data. */
+ alignas(8) u8 user_data[CmacSizeMax];
+
+ /* Validate arguments. */
+ SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument);
+ SMC_R_UNLESS(data_size <= sizeof(user_data), InvalidArgument);
+
+ /* Map the user data, and copy to stack. */
+ {
+ UserPageMapper mapper(data_address);
+ SMC_R_UNLESS(mapper.Map(), InvalidArgument);
+ SMC_R_UNLESS(mapper.CopyFromUser(user_data, data_address, data_size), InvalidArgument);
+ }
+
+ /* Ensure the SE sees consistent data. */
+ hw::FlushDataCache(user_data, data_size);
+ hw::DataSynchronizationBarrierInnerShareable();
+
+ /* Compute the mac. */
+ {
+ u8 mac[se::AesBlockSize];
+ se::ComputeAes128Cmac(mac, sizeof(mac), slot, user_data, data_size);
+
+ std::memcpy(std::addressof(args.r[1]), mac, sizeof(mac));
+ }
+
+ return SmcResult::Success;
+ }
+
+ SmcResult LoadPreparedAesKeyImpl(SmcArguments &args) {
+ /* Decode arguments. */
+ u8 access_key[AesKeySize];
+
+ const int slot = args.r[1];
+ std::memcpy(access_key, std::addressof(args.r[2]), sizeof(access_key));
+
+ /* Validate arguments. */
+ SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument);
+
+ /* Derive the seal key. */
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, EsSealKeySource, sizeof(EsSealKeySource));
+
+ /* Unseal the key. */
+ se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_Smc, access_key, sizeof(access_key));
+
+ return SmcResult::Success;
+ }
+
+ SmcResult PrepareEsCommonTitleKeyImpl(SmcArguments &args) {
+ /* Declare variables. */
+ u8 key_source[se::AesBlockSize];
+ u8 key[se::AesBlockSize];
+ u8 access_key[se::AesBlockSize];
+
+ /* Decode arguments. */
+ std::memcpy(key_source, std::addressof(args.r[1]), sizeof(key_source));
+ const int generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, static_cast(args.r[3]) - 1) : 0;
+
+ /* Validate arguments. */
+ SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument);
+ SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument);
+
+ /* Derive the key. */
+ DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), EsCommonKeyType_TitleKey, generation);
+
+ /* Prepare the aes key. */
+ PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key));
+
+ /* Copy the access key to output. */
+ std::memcpy(std::addressof(args.r[1]), access_key, sizeof(access_key));
+
+ return SmcResult::Success;
+ }
+
+ SmcResult ValidateDeviceUniqueDataSize(DeviceUniqueData mode, size_t data_size) {
+ switch (mode) {
+ case DeviceUniqueData_DecryptDeviceUniqueData:
+ {
+ SMC_R_UNLESS(data_size < DeviceUniqueDataSizeMax, InvalidArgument);
+ }
+ break;
+ case DeviceUniqueData_ImportLotusKey:
+ case DeviceUniqueData_ImportEsDeviceKey:
+ case DeviceUniqueData_ImportSslKey:
+ case DeviceUniqueData_ImportEsClientCertKey:
+ {
+ SMC_R_UNLESS(DeviceUniqueDataSizeMin <= data_size && data_size <= DeviceUniqueDataSizeMax, InvalidArgument);
+ }
+ break;
+ default:
+ return SmcResult::InvalidArgument;
+ }
+
+ return SmcResult::Success;
+ }
+
+ SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) {
+ /* Decode arguments. */
+ u8 access_key[se::AesBlockSize];
+ u8 key_source[se::AesBlockSize];
+
+ std::memcpy(access_key, std::addressof(args.r[1]), sizeof(access_key));
+ const util::BitPack32 option = { static_cast(args.r[3]) };
+ const uintptr_t data_address = args.r[4];
+ const size_t data_size = args.r[5];
+ std::memcpy(key_source, std::addressof(args.r[6]), sizeof(key_source));
+
+ const auto mode = option.Get();
+ const auto reserved = option.Get();
+
+ /* Validate arguments. */
+ SMC_R_UNLESS(reserved == 0, InvalidArgument);
+ SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size));
+
+ /* Decrypt the device unique data. */
+ alignas(8) u8 work_buffer[DeviceUniqueDataSizeMax];
+ ON_SCOPE_EXIT { crypto::ClearMemory(work_buffer, sizeof(work_buffer)); };
+ {
+ /* Map and copy in the encrypted data. */
+ UserPageMapper mapper(data_address);
+ SMC_R_UNLESS(mapper.Map(), InvalidArgument);
+ SMC_R_UNLESS(mapper.CopyFromUser(work_buffer, data_address, data_size), InvalidArgument);
+
+ /* Determine the seal key to use. */
+ const auto seal_key_type = DeviceUniqueDataToSealKey[mode];
+ const u8 * const seal_key_source = SealKeySources[seal_key_type];
+
+ /* Decrypt the data. */
+ if (!DecryptDeviceUniqueData(work_buffer, data_size, nullptr, seal_key_source, se::AesBlockSize, access_key, sizeof(access_key), key_source, sizeof(key_source), work_buffer, data_size)) {
+ return SmcResult::InvalidArgument;
+ }
+
+ /* Either output the key, or import it. */
+ switch (mode) {
+ case DeviceUniqueData_DecryptDeviceUniqueData:
+ {
+ SMC_R_UNLESS(mapper.CopyToUser(data_address, work_buffer, data_size), InvalidArgument);
+ }
+ break;
+ case DeviceUniqueData_ImportLotusKey:
+ case DeviceUniqueData_ImportSslKey:
+ ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize);
+ break;
+ case DeviceUniqueData_ImportEsDeviceKey:
+ case DeviceUniqueData_ImportEsClientCertKey:
+ ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize);
+ ImportRsaKeyModulusProvisionally(ConvertToImportRsaKey(mode), work_buffer + se::RsaSize, se::RsaSize);
+ CommitRsaKeyModulus(ConvertToImportRsaKey(mode));
+ break;
+ AMS_UNREACHABLE_DEFAULT_CASE();
+ }
+ }
+
+ return SmcResult::Success;
+ }
+
+ SmcResult ReencryptDeviceUniqueDataImpl(SmcArguments &args) {
+ /* Decode arguments. */
+ u8 access_key_dec[se::AesBlockSize];
+ u8 access_key_enc[se::AesBlockSize];
+ u8 key_source_dec[se::AesBlockSize];
+ u8 key_source_enc[se::AesBlockSize];
+
+ const uintptr_t access_key_dec_address = args.r[1];
+ const uintptr_t access_key_enc_address = args.r[2];
+ const util::BitPack32 option = { static_cast(args.r[3]) };
+ const uintptr_t data_address = args.r[4];
+ const size_t data_size = args.r[5];
+ const uintptr_t key_source_dec_address = args.r[6];
+ const uintptr_t key_source_enc_address = args.r[7];
+
+ const auto mode = option.Get();
+ const auto reserved = option.Get();
+
+ /* Validate arguments. */
+ SMC_R_UNLESS(reserved == 0, InvalidArgument);
+ SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size));
+
+ /* Decrypt the device unique data. */
+ alignas(8) u8 work_buffer[DeviceUniqueDataSizeMax];
+ ON_SCOPE_EXIT { crypto::ClearMemory(work_buffer, sizeof(work_buffer)); };
+ {
+ /* Map and copy in the encrypted data. */
+ UserPageMapper mapper(data_address);
+ SMC_R_UNLESS(mapper.Map(), InvalidArgument);
+ SMC_R_UNLESS(mapper.CopyFromUser(work_buffer, data_address, data_size), InvalidArgument);
+ SMC_R_UNLESS(mapper.CopyFromUser(access_key_dec, access_key_dec_address, sizeof(access_key_dec)), InvalidArgument);
+ SMC_R_UNLESS(mapper.CopyFromUser(access_key_enc, access_key_enc_address, sizeof(access_key_enc)), InvalidArgument);
+ SMC_R_UNLESS(mapper.CopyFromUser(key_source_dec, key_source_dec_address, sizeof(key_source_dec)), InvalidArgument);
+ SMC_R_UNLESS(mapper.CopyFromUser(key_source_enc, key_source_enc_address, sizeof(key_source_enc)), InvalidArgument);
+
+ /* Decrypt the data. */
+ u8 device_id_high;
+ {
+ /* Determine the seal key to use. */
+ const u8 * const seal_key_source = SealKeySources[SealKey_ReencryptDeviceUniqueData];
+
+ if (!DecryptDeviceUniqueData(work_buffer, data_size, std::addressof(device_id_high), seal_key_source, se::AesBlockSize, access_key_dec, sizeof(access_key_dec), key_source_dec, sizeof(key_source_dec), work_buffer, data_size)) {
+ return SmcResult::InvalidArgument;
+ }
+ }
+
+ /* Reencrypt the data. */
+ {
+ /* Determine the seal key to use. */
+ const auto seal_key_type = DeviceUniqueDataToSealKey[mode];
+ const u8 * const seal_key_source = SealKeySources[seal_key_type];
+
+ /* Encrypt the data. */
+ EncryptDeviceUniqueData(work_buffer, data_size, seal_key_source, se::AesBlockSize, access_key_enc, sizeof(access_key_enc), key_source_enc, sizeof(key_source_enc), work_buffer, data_size - DeviceUniqueDataTotalMetaSize, device_id_high);
+ }
+
+ /* Copy the reencrypted data back to user. */
+ SMC_R_UNLESS(mapper.CopyToUser(data_address, work_buffer, data_size), InvalidArgument);
+ }
+
+ return SmcResult::Success;
+ }
+
+ SmcResult GetSecureDataImpl(SmcArguments &args) {
+ /* Decode arguments. */
+ const auto which = static_cast(args.r[1]);
+
+ /* Validate arguments/conditions. */
+ SMC_R_UNLESS(fuse::GetPatchVersion() < fuse::PatchVersion_Odnx02A2, NotImplemented);
+ SMC_R_UNLESS(which < SecureData_Count, NotImplemented);
+
+ /* Use a temporary buffer. */
+ u8 secure_data[AesKeySize];
+ GetSecureDataImpl(secure_data, which, false);
+
+ /* Copy out. */
+ std::memcpy(std::addressof(args.r[1]), secure_data, sizeof(secure_data));
+ return SmcResult::Success;
+ }
+
+ }
+
+ /* Aes functionality. */
+ SmcResult SmcGenerateAesKek(SmcArguments &args) {
+ return LockSecurityEngineAndInvoke(args, GenerateAesKekImpl);
+ }
+
+ SmcResult SmcLoadAesKey(SmcArguments &args) {
+ return LockSecurityEngineAndInvoke(args, LoadAesKeyImpl);
+ }
+
+ SmcResult SmcComputeAes(SmcArguments &args) {
+ return LockSecurityEngineAndInvokeAsync(args, ComputeAesImpl, GetComputeAesResult);
+ }
+
+ SmcResult SmcGenerateSpecificAesKey(SmcArguments &args) {
+ return LockSecurityEngineAndInvoke(args, GenerateSpecificAesKeyImpl);
+ }
+
+ SmcResult SmcComputeCmac(SmcArguments &args) {
+ return LockSecurityEngineAndInvoke(args, ComputeCmacImpl);
+ }
+
+ SmcResult SmcLoadPreparedAesKey(SmcArguments &args) {
+ return LockSecurityEngineAndInvoke(args, LoadPreparedAesKeyImpl);
+ }
+
+ SmcResult SmcPrepareEsCommonTitleKey(SmcArguments &args) {
+ return LockSecurityEngineAndInvoke(args, PrepareEsCommonTitleKeyImpl);
+ }
+
+ /* Device unique data functionality. */
+ SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args) {
+ return LockSecurityEngineAndInvoke(args, DecryptDeviceUniqueDataImpl);
+ }
+
+ SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) {
+ return LockSecurityEngineAndInvoke(args, ReencryptDeviceUniqueDataImpl);
+ }
+
+ /* Legacy APIs. */
+ SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args) {
+ /* TODO */
+ return SmcResult::NotImplemented;
+ }
+
+ /* Es encryption utilities. */
+ void DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation) {
+ /* Validate pre-conditions. */
+ AMS_ABORT_UNLESS(dst_size == AesKeySize);
+ AMS_ABORT_UNLESS(src_size == AesKeySize);
+ AMS_ABORT_UNLESS(0 <= type && type < EsCommonKeyType_Count);
+
+ /* Prepare the master key for the generation. */
+ const int slot = PrepareMasterKey(generation);
+
+ /* Derive the es common key. */
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, EsCommonKeySources[type], AesKeySize);
+
+ /* Decrypt the input using the common key. */
+ se::DecryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size);
+ }
+
+ void PrepareEsAesKey(void *dst, size_t dst_size, const void *src, size_t src_size) {
+ /* Validate pre-conditions. */
+ AMS_ABORT_UNLESS(dst_size == AesKeySize);
+ AMS_ABORT_UNLESS(src_size == AesKeySize);
+
+ /* Derive the seal key. */
+ se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, EsSealKeySource, sizeof(EsSealKeySource));
+
+ /* Seal the key. */
+ se::EncryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size);
+ }
+
+ /* 'Tis the last rose of summer, / Left blooming alone; */
+ /* Oh! who would inhabit / This bleak world alone? */
+ SmcResult SmcGetSecureData(SmcArguments &args) {
+ return LockSecurityEngineAndInvoke(args, GetSecureDataImpl);
+ }
+
+}
diff --git a/exosphere/program/source/smc/secmon_smc_aes.hpp b/exosphere/program/source/smc/secmon_smc_aes.hpp
new file mode 100644
index 0000000..b502ee6
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_smc_aes.hpp
@@ -0,0 +1,53 @@
+/*
+ * 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 EsCommonKeyType {
+ EsCommonKeyType_TitleKey = 0,
+ EsCommonKeyType_ArchiveKey = 1,
+
+ EsCommonKeyType_Count,
+ };
+
+ /* General Aes functionality. */
+ SmcResult SmcGenerateAesKek(SmcArguments &args);
+ SmcResult SmcLoadAesKey(SmcArguments &args);
+ SmcResult SmcComputeAes(SmcArguments &args);
+ SmcResult SmcGenerateSpecificAesKey(SmcArguments &args);
+ SmcResult SmcComputeCmac(SmcArguments &args);
+ SmcResult SmcLoadPreparedAesKey(SmcArguments &args);
+ SmcResult SmcPrepareEsCommonTitleKey(SmcArguments &args);
+
+ /* Device unique data functionality. */
+ SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args);
+ SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args);
+
+ /* Legacy device unique data functionality. */
+ SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args);
+ SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args);
+
+ /* Es encryption utilities. */
+ void DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation);
+ void PrepareEsAesKey(void *dst, size_t dst_size, const void *src, size_t src_size);
+
+ /* The last rose of summer. */
+ SmcResult SmcGetSecureData(SmcArguments &args);
+
+}
diff --git a/exosphere/program/source/smc/secmon_smc_carveout.cpp b/exosphere/program/source/smc/secmon_smc_carveout.cpp
new file mode 100644
index 0000000..120ae90
--- /dev/null
+++ b/exosphere/program/source/smc/secmon_smc_carveout.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_error.hpp"
+#include "../secmon_setup.hpp"
+#include "secmon_smc_carveout.hpp"
+
+namespace ams::secmon::smc {
+
+ SmcResult SmcSetKernelCarveoutRegion(SmcArguments &args) {
+ /* Decode arguments. */
+ const int index = args.r[1];
+ const uintptr_t address = args.r[2];
+ const size_t size = args.r[3];
+
+ /* Validate arguments. */
+ SMC_R_UNLESS(0 <= index && index < KernelCarveoutCount, InvalidArgument);
+ SMC_R_UNLESS(util::IsAligned(address, 128_KB), InvalidArgument);
+ SMC_R_UNLESS(util::IsAligned(size, 128_KB), InvalidArgument);
+ SMC_R_UNLESS(size <= CarveoutSizeMax, InvalidArgument);
+
+ /* Set the carveout. */
+ SetKernelCarveoutRegion(index, address, size);
+
+ return SmcResult::Success;
+ }
+
+}
diff --git a/exosphere/program/source/smc/secmon_smc_carveout.hpp b/exosphere/program/source/smc/secmon_smc_carveout.hpp
new file mode 100644
index 0000000..bc09fcd
--- /dev/null
+++ b/exosphere/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