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 . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcSetKernelCarveoutRegion(SmcArguments &args); + +} diff --git a/exosphere/program/source/smc/secmon_smc_common.hpp b/exosphere/program/source/smc/secmon_smc_common.hpp new file mode 100644 index 0000000..a03658e --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_common.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon::smc { + + enum class SmcResult : u32 { + Success = 0, + NotImplemented = 1, + InvalidArgument = 2, + Busy = 3, + NoAsyncOperation = 4, + InvalidAsyncOperation = 5, + NotPermitted = 6, + NotInitialized = 7, + + PsciSuccess = 0, + PsciNotSupported = static_cast(-1), + PsciInvalidParameters = static_cast(-2), + PsciDenied = static_cast(-3), + PsciAlreadyOn = static_cast(-4), + }; + + #define SMC_R_SUCCEEEDED(res) (res == SmcResult::Success) + #define SMC_R_FAILED(res) (res != SmcResult::Success) + + #define SMC_R_TRY(res_expr) ({ const auto _tmp_r_try_rc = (res_expr); if (SMC_R_FAILED(_tmp_r_try_rc)) { return _tmp_r_try_rc; } }) + #define SMC_R_UNLESS(cond, RES) ({ if (!(cond)) { return SmcResult::RES; }}) + + struct SmcArguments { + u64 r[8]; + }; + + constexpr inline int SecurityEngineUserInterruptId = 44; + constexpr inline u64 InvalidAsyncKey = 0; + +} diff --git a/exosphere/program/source/smc/secmon_smc_cpu_asm.s b/exosphere/program/source/smc/secmon_smc_cpu_asm.s new file mode 100644 index 0000000..dd9f4df --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_cpu_asm.s @@ -0,0 +1,94 @@ +/* + * 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 + +.section .text._ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE, "ax", %progbits +.align 4 +.global _ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE +_ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE: + /* 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 .text._ZN3ams6secmon3smc16FinalizePowerOffEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon3smc16FinalizePowerOffEv +_ZN3ams6secmon3smc16FinalizePowerOffEv: + /* Disable all caches by clearing sctlr_el1.C. */ + mrs x0, sctlr_el1 + and x0, x0, #~(1 << 2) + msr sctlr_el1, x0 + isb + + /* Disable all caches by clearing sctlr_el3.C. */ + mrs x0, sctlr_el3 + and x0, x0, #~(1 << 2) + msr sctlr_el3, x0 + isb + + /* Disable prefetching of page table walking descriptors. */ + mrs x0, cpuectlr_el1 + orr x0, x0, #(1 << 38) + + /* Disable prefetching of instructions. */ + and x0, x0, #~(3 << 35) + + /* Disable prefetching of data. */ + and x0, x0, #~(3 << 32) + msr cpuectlr_el1, x0 + isb + + /* Ensure that all data prefetching prior to our configuration change completes. */ + dsb sy + + /* Flush the entire data cache (local). */ + bl _ZN3ams6secmon25FlushEntireDataCacheLocalEv + + /* Disable receiving instruction cache/TLB maintenance operations. */ + mrs x0, cpuectlr_el1 + and x0, x0, #~(1 << 6) + msr cpuectlr_el1, x0 + + /* Configure the gic to not send interrupts to the current core. */ + ldr x1, =0x1F0043000 + mov w0, #0x1E0 /* Set FIQBypDisGrp1, IRQBypDisGrp1, reserved bits 7/8. */ + str w0, [x1] + + /* Lock the OS Double Lock. */ + mrs x0, osdlr_el1 + orr x0, x0, #(1 << 0) + msr osdlr_el1, x0 + + /* Ensure that our configuration takes. */ + isb + dsb sy + + /* Wait for interrupts, infinitely. */ +0: + wfi + b 0b + + + diff --git a/exosphere/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere/program/source/smc/secmon_smc_device_unique_data.cpp new file mode 100644 index 0000000..6505daa --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_device_unique_data.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_device_unique_data.hpp" + +namespace ams::secmon::smc { + + namespace { + + void GenerateIv(void *dst, size_t dst_size) { + /* Flush the region we're about to fill to ensure consistency with the SE. */ + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes. */ + se::GenerateRandomBytes(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Flush to ensure the CPU sees consistent data for the region. */ + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + } + + void PrepareDeviceUniqueDataKey(const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size) { + /* Derive the seal key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, seal_key_source, seal_key_source_size); + + /* Derive the device unique data kek. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, access_key, access_key_size); + + /* Derive the actual device unique data key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, key_source, key_source_size); + } + + void ComputeAes128Ctr(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { + /* Ensure that the SE sees consistent data. */ + hw::FlushDataCache(src, src_size); + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Use the security engine to transform the data. */ + se::ComputeAes128Ctr(dst, dst_size, slot, src, src_size, iv, iv_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Ensure the CPU sees consistent data. */ + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + } + + void ComputeGmac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *iv, size_t iv_size) { + /* Declare keyslot (as encryptor will need to take it by pointer/reference). */ + constexpr int Slot = pkg1::AesKeySlot_Smc; + + /* Calculate the mac. */ + crypto::Aes128GcmEncryptor gcm; + gcm.Initialize(std::addressof(Slot), sizeof(Slot), iv, iv_size); + gcm.UpdateAad(data, data_size); + gcm.GetMac(dst, dst_size); + } + + constexpr u64 GetDeviceIdLow(u64 device_id) { + /* Mask out the top byte. */ + constexpr u64 ByteMask = (static_cast(1) << BITSIZEOF(u8)) - 1; + constexpr u64 LowMask = ~(ByteMask << (BITSIZEOF(u64) - BITSIZEOF(u8))); + return device_id & LowMask; + } + + constexpr u8 GetDeviceIdHigh(u64 device_id) { + /* Get the top byte. */ + return static_cast(device_id >> (BITSIZEOF(u64) - BITSIZEOF(u8))); + } + + constexpr u64 EncodeDeviceId(u8 device_id_high, u64 device_id_low) { + return (static_cast(device_id_high) << (BITSIZEOF(u64) - BITSIZEOF(u8))) | device_id_low; + } + + } + + bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size) { + /* Determine how much decrypted data there will be. */ + const size_t enc_size = src_size - DeviceUniqueDataOuterMetaSize; + const size_t dec_size = enc_size - DeviceUniqueDataInnerMetaSize; + + /* Ensure that our sizes are allowed. */ + AMS_ABORT_UNLESS(src_size > DeviceUniqueDataTotalMetaSize); + AMS_ABORT_UNLESS(dst_size >= enc_size); + + /* Determine the extents of the data. */ + const u8 * const iv = static_cast(src); + const u8 * const enc = iv + DeviceUniqueDataIvSize; + const u8 * const mac = enc + enc_size; + + /* Decrypt the data. */ + { + /* Declare temporaries. */ + u8 temp_iv[DeviceUniqueDataIvSize]; + u8 calc_mac[DeviceUniqueDataMacSize]; + ON_SCOPE_EXIT { crypto::ClearMemory(temp_iv, sizeof(temp_iv)); crypto::ClearMemory(calc_mac, sizeof(calc_mac)); }; + + /* Prepare the key used to decrypt the data. */ + PrepareDeviceUniqueDataKey(seal_key_source, seal_key_source_size, access_key, access_key_size, key_source, key_source_size); + + /* Copy the iv to stack. */ + std::memcpy(temp_iv, iv, sizeof(temp_iv)); + + /* Decrypt the data. */ + ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Smc, enc, enc_size, temp_iv, DeviceUniqueDataIvSize); + + /* Compute the gmac. */ + ComputeGmac(calc_mac, DeviceUniqueDataMacSize, dst, enc_size, temp_iv, DeviceUniqueDataIvSize); + + /* Validate the gmac. */ + if (!crypto::IsSameBytes(mac, calc_mac, sizeof(calc_mac))) { + return false; + } + } + + /* Validate device id, output device id if needed. */ + { + /* Locate the device id in the decryption output. */ + const u8 * const padding = static_cast(dst) + dec_size; + const u8 * const device_id = padding + DeviceUniqueDataPaddingSize; + + /* Load the big endian device id. */ + const u64 device_id_val = util::LoadBigEndian(static_cast(static_cast(device_id))); + + /* Validate that the device id low matches the value in fuses. */ + if (GetDeviceIdLow(device_id_val) != fuse::GetDeviceId()) { + return false; + } + + /* Set the output device id high, if needed. */ + if (out_device_id_high != nullptr) { + *out_device_id_high = GetDeviceIdHigh(device_id_val); + } + } + + return true; + } + + void EncryptDeviceUniqueData(void *dst, size_t dst_size, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, u8 device_id_high) { + /* Determine metadata locations. */ + u8 * const dst_iv = static_cast(dst); + u8 * const dst_data = dst_iv + DeviceUniqueDataIvSize; + u8 * const dst_pad = dst_data + src_size; + u8 * const dst_did = dst_pad + DeviceUniqueDataPaddingSize; + u8 * const dst_mac = dst_did + DeviceUniqueDataDeviceIdSize; + + /* Verify that our sizes are okay. */ + const size_t enc_size = src_size + DeviceUniqueDataInnerMetaSize; + const size_t res_size = src_size + DeviceUniqueDataTotalMetaSize; + AMS_ABORT_UNLESS(res_size <= dst_size); + + /* Layout the image as expected. */ + { + /* Generate a random iv. */ + util::AlignedBuffer iv; + GenerateIv(iv, DeviceUniqueDataIvSize); + + /* Move the data to the output image. */ + std::memmove(dst_data, src, src_size); + + /* Copy the iv. */ + std::memcpy(dst_iv, iv, DeviceUniqueDataIvSize); + + /* Clear the padding. */ + std::memset(dst_pad, 0, DeviceUniqueDataPaddingSize); + + /* Store the device id. */ + util::StoreBigEndian(reinterpret_cast(dst_did), EncodeDeviceId(device_id_high, fuse::GetDeviceId())); + } + + /* Encrypt and mac. */ + { + + /* Prepare the key used to encrypt the data. */ + PrepareDeviceUniqueDataKey(seal_key_source, seal_key_source_size, access_key, access_key_size, key_source, key_source_size); + + /* Compute the gmac. */ + ComputeGmac(dst_mac, DeviceUniqueDataMacSize, dst_data, enc_size, dst_iv, DeviceUniqueDataIvSize); + + /* Encrypt the data. */ + ComputeAes128Ctr(dst_data, enc_size, pkg1::AesKeySlot_Smc, dst_data, enc_size, dst_iv, DeviceUniqueDataIvSize); + } + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere/program/source/smc/secmon_smc_device_unique_data.hpp new file mode 100644 index 0000000..a8e7901 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_device_unique_data.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 +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + constexpr inline size_t DeviceUniqueDataIvSize = se::AesBlockSize; + constexpr inline size_t DeviceUniqueDataMacSize = se::AesBlockSize; + constexpr inline size_t DeviceUniqueDataDeviceIdSize = sizeof(u64); + constexpr inline size_t DeviceUniqueDataPaddingSize = se::AesBlockSize - DeviceUniqueDataDeviceIdSize; + + constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize + DeviceUniqueDataMacSize; + constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize; + constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize; + + bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size); + void EncryptDeviceUniqueData(void *dst, size_t dst_size, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, u8 device_id_high); + +} diff --git a/exosphere/program/source/smc/secmon_smc_error.cpp b/exosphere/program/source/smc/secmon_smc_error.cpp new file mode 100644 index 0000000..70ea8f1 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_error.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_error.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcShowError(SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_error.hpp b/exosphere/program/source/smc/secmon_smc_error.hpp new file mode 100644 index 0000000..25b74f1 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_error.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcShowError(SmcArguments &args); + +} diff --git a/exosphere/program/source/smc/secmon_smc_handler.cpp b/exosphere/program/source/smc/secmon_smc_handler.cpp new file mode 100644 index 0000000..644139b --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_handler.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "../secmon_misc.hpp" +#include "secmon_smc_common.hpp" +#include "secmon_smc_handler.hpp" +#include "secmon_smc_aes.hpp" +#include "secmon_smc_carveout.hpp" +#include "secmon_smc_device_unique_data.hpp" +#include "secmon_smc_error.hpp" +#include "secmon_smc_info.hpp" +#include "secmon_smc_memory_access.hpp" +#include "secmon_smc_power_management.hpp" +#include "secmon_smc_random.hpp" +#include "secmon_smc_register_access.hpp" +#include "secmon_smc_result.hpp" +#include "secmon_smc_rsa.hpp" + +namespace ams::secmon::smc { + + namespace { + + struct HandlerInfo { + u32 function_id; + u32 restriction_mask; + SmcHandler handler; + }; + + struct HandlerTable { + const HandlerInfo *entries; + size_t count; + }; + + enum HandlerType : int { + HandlerType_User = 0, + HandlerType_Kern = 1, + HandlerType_Count = 2, + }; + + enum Restriction { + Restriction_None = (0 << 0), + Restriction_Normal = (1 << 0), + Restriction_DeviceUniqueDataNotAllowed = (1 << 1), + Restriction_SafeModeNotAllowed = (1 << 2), + }; + + enum SmcCallRange { + SmcCallRange_ArmArch = 0, + SmcCallRange_Cpu = 1, + SmcCallRange_Sip = 2, + SmcCallRange_Oem = 3, + SmcCallRange_Standard = 4, + + SmcCallRange_TrustedApp = 0x30, + }; + + enum SmcArgumentType { + ArgumentType_Integer = 0, + ArgumentType_Pointer = 1, + }; + + enum SmcConvention { + Convention_Smc32 = 0, + Convention_Smc64 = 1, + }; + + enum SmcCallType { + SmcCallType_YieldingCall = 0, + SmcCallType_FastCall = 1, + }; + + struct SmcFunctionId { + using FunctionId = util::BitPack64::Field< 0, 8, u32>; + using ArgumentType0 = util::BitPack64::Field< 8, 1, SmcArgumentType>; + using ArgumentType1 = util::BitPack64::Field< 9, 1, SmcArgumentType>; + using ArgumentType2 = util::BitPack64::Field<10, 1, SmcArgumentType>; + using ArgumentType3 = util::BitPack64::Field<11, 1, SmcArgumentType>; + using ArgumentType4 = util::BitPack64::Field<12, 1, SmcArgumentType>; + using ArgumentType5 = util::BitPack64::Field<13, 1, SmcArgumentType>; + using ArgumentType6 = util::BitPack64::Field<14, 1, SmcArgumentType>; + using ArgumentType7 = util::BitPack64::Field<15, 1, SmcArgumentType>; + using Reserved = util::BitPack64::Field<16, 8, u32>; + using CallRange = util::BitPack64::Field<24, 6, SmcCallRange>; + using Convention = util::BitPack64::Field<30, 1, SmcConvention>; + using CallType = util::BitPack64::Field<31, 1, SmcCallType>; + using Reserved2 = util::BitPack64::Field<32, 32, u32>; + }; + + constinit HandlerInfo g_user_handlers[] = { + { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, + { 0xC3000401, Restriction_SafeModeNotAllowed, SmcSetConfig }, + { 0xC3000002, Restriction_Normal, SmcGetConfigUser }, + { 0xC3000003, Restriction_Normal, SmcGetResult }, + { 0xC3000404, Restriction_Normal, SmcGetResultData }, + { 0xC3000E05, Restriction_SafeModeNotAllowed, SmcModularExponentiate }, + { 0xC3000006, Restriction_Normal, SmcGenerateRandomBytes }, + { 0xC3000007, Restriction_Normal, SmcGenerateAesKek }, + { 0xC3000008, Restriction_Normal, SmcLoadAesKey }, + { 0xC3000009, Restriction_Normal, SmcComputeAes }, + { 0xC300000A, Restriction_Normal, SmcGenerateSpecificAesKey }, + { 0xC300040B, Restriction_Normal, SmcComputeCmac }, + { 0xC300D60C, Restriction_Normal, SmcReencryptDeviceUniqueData }, + { 0xC300100D, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptDeviceUniqueData }, + { 0xC300000E, Restriction_SafeModeNotAllowed, nullptr }, + { 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey }, + { 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey }, + { 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey }, + { 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey } + }; + + constinit HandlerInfo g_kern_handlers[] = { + { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, + { 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu }, + { 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu }, + { 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu }, + { 0xC3000004, Restriction_Normal, SmcGetConfigKern }, + { 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking }, + { 0xC3000006, Restriction_Normal, SmcShowError }, + { 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion }, + { 0xC3000008, Restriction_Normal, SmcReadWriteRegister }, + }; + + constinit HandlerInfo g_ams_handlers[] = { + { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, + { 0xF0000201, Restriction_None, SmcIramCopy }, + { 0xF0000002, Restriction_None, SmcReadWriteRegister }, + { 0xF0000003, Restriction_None, SmcWriteAddress }, + { 0xF0000404, Restriction_None, SmcGetEmummcConfig }, + }; + + constexpr const HandlerInfo GetSecureDataHandlerInfo = { + 0x67891234, Restriction_None, SmcGetSecureData + }; + + constinit HandlerTable g_handler_tables[] = { + { g_user_handlers, util::size(g_user_handlers) }, + { g_kern_handlers, util::size(g_kern_handlers) }, + }; + + constinit HandlerTable g_ams_handler_table = { + g_ams_handlers, util::size(g_ams_handlers) + }; + + NORETURN void InvalidSmcError(u64 id) { + SetError(pkg1::ErrorInfo_UnknownSmc); + AMS_ABORT("Invalid SMC: %lx", id); + } + + const HandlerTable &GetHandlerTable(HandlerType type, u64 id) { + /* Ensure we have a valid handler type. */ + if (AMS_UNLIKELY(!(0 <= type && type < HandlerType_Count))) { + InvalidSmcError(id); + } + + /* Provide support for legacy SmcGetSecureData. */ + if (id == GetSecureDataHandlerInfo.function_id) { + return g_handler_tables[HandlerType_User]; + } + + /* Check if we're a user SMC. */ + if (type == HandlerType_User) { + /* Nintendo uses OEM SMCs. */ + /* We will assign Atmosphere extension SMCs the TrustedApplication range. */ + if (util::BitPack64{id}.Get() == SmcCallRange_TrustedApp) { + return g_ams_handler_table; + } + + /* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */ + if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) { + InvalidSmcError(id); + } + } + + return g_handler_tables[type]; + } + + const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) { + /* Provide support for legacy SmcGetSecureData. */ + if (id == GetSecureDataHandlerInfo.function_id) { + return GetSecureDataHandlerInfo; + } + + /* Get and check the index. */ + const auto index = util::BitPack64{id}.Get(); + if (AMS_UNLIKELY(index >= table.count)) { + InvalidSmcError(id); + } + + /* Get and check the handler info. */ + const auto &handler_info = table.entries[index]; + + /* Check that the handler isn't null. */ + if (AMS_UNLIKELY(handler_info.handler == nullptr)) { + InvalidSmcError(id); + } + + /* Check that the handler's id matches. */ + if (AMS_UNLIKELY(handler_info.function_id != id)) { + InvalidSmcError(id); + } + + return handler_info; + } + + bool IsHandlerRestricted(const HandlerInfo &info) { + return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0; + } + + SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) { + /* Check if the smc is restricted. */ + if (AMS_UNLIKELY(IsHandlerRestricted(info))) { + return SmcResult::NotPermitted; + } + + /* Invoke the smc. */ + return info.handler(args); + } + + constinit std::atomic g_logged = 0; + + constexpr int LogMin = 0x1000000; + constexpr int LogMax = 0x1000000; + + constexpr size_t LogBufSize = 0x5000; + + void DebugLog(SmcArguments &args) { + const int current = g_logged.fetch_add(1); + + if (current == 0) { + std::memset(MemoryRegionVirtualDebug.GetPointer(), 0xCC, LogBufSize); + } + + if (current < LogMin) { + return; + } + + const int ind = current - LogMin; + const int ofs = (ind * sizeof(args)) % LogBufSize; + + for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { + ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + ofs))[i] = reinterpret_cast(std::addressof(args))[i]; + } + + if (current >= LogMax) { + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + + util::WaitMicroSeconds(1000); + } + + } + + } + + void HandleSmc(int type, SmcArguments &args) { + /* Get the table. */ + const auto &table = GetHandlerTable(static_cast(type), args.r[0]); + + if (std::addressof(table) == std::addressof(g_handler_tables[HandlerType_User])) { + DebugLog(args); + } + + /* Get the handler info. */ + const auto &info = GetHandlerInfo(table, args.r[0]); + + /* Set the invocation result. */ + args.r[0] = static_cast(InvokeSmcHandler(info, args)); + + if (std::addressof(table) == std::addressof(g_handler_tables[HandlerType_User])) { + DebugLog(args); + } + +/* TODO: For debugging. Remove this when exo2 is complete. */ +#if 1 + if (args.r[0] == static_cast(SmcResult::NotImplemented)) { + *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xBBBBBBBB; + *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast(info.function_id); + for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { + ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast(std::addressof(args))[i]; + } + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + + util::WaitMicroSeconds(1000); + } + if (args.r[0] != static_cast(SmcResult::Success) && info.function_id != 0xC3000007 /* generate aes key fails during SetupKekAccessKeys */) { + *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xCCCCCCCC; + *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast(info.function_id); + for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { + ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast(std::addressof(args))[i]; + } + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; + *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; + + util::WaitMicroSeconds(1000); + } +#endif + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_handler.hpp b/exosphere/program/source/smc/secmon_smc_handler.hpp new file mode 100644 index 0000000..f051209 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_handler.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + using SmcHandler = SmcResult (*)(SmcArguments &args); + +} diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp new file mode 100644 index 0000000..7cf5026 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "../secmon_misc.hpp" +#include "../secmon_page_mapper.hpp" +#include "../secmon_user_power_management.hpp" +#include "secmon_smc_info.hpp" +#include "secmon_smc_power_management.hpp" + +namespace ams::secmon::smc { + + namespace { + + struct KernelConfiguration { + /* Secure Monitor view. */ + using Flags1 = util::BitPack32::Field< 0, 8>; + using Flags0 = util::BitPack32::Field< 8, 8>; + using PhysicalMemorySize = util::BitPack32::Field<16, 2>; + + /* Kernel view, from libmesosphere. */ + using DebugFillMemory = util::BitPack32::Field<0, 1, bool>; + using EnableUserExceptionHandlers = util::BitPack32::Field; + using EnableUserPmuAccess = util::BitPack32::Field; + using IncreaseThreadResourceLimit = util::BitPack32::Field; + using Reserved4 = util::BitPack32::Field; + using UseSecureMonitorPanicCall = util::BitPack32::Field; + using Reserved9 = util::BitPack32::Field; + using MemorySize = util::BitPack32::Field; /* smc::MemorySize = pkg1::MemorySize */ + }; + + constexpr const pkg1::MemorySize DramIdToMemorySize[fuse::DramId_Count] = { + [fuse::DramId_IcosaSamsung4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IcosaHynix4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IcosaMicron4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_CopperSamsung4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IcosaSamsung6GB] = pkg1::MemorySize_6GB, + [fuse::DramId_CopperHynix4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_CopperMicron4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaX1X2Samsung4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSansung4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung8GB] = pkg1::MemorySize_8GB, + [fuse::DramId_IowaHynix4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaMicron4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_HoagSamsung4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_HoagSamsung8GB] = pkg1::MemorySize_8GB, + [fuse::DramId_HoagHynix4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_HoagMicron4GB] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung4GBY] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung1y4GBX] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung1y8GBX] = pkg1::MemorySize_8GB, + [fuse::DramId_HoagSamsung1y4GBX] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung1y4GBY] = pkg1::MemorySize_4GB, + [fuse::DramId_IowaSamsung1y8GBY] = pkg1::MemorySize_8GB, + [fuse::DramId_IowaSamsung1y4GBA] = pkg1::MemorySize_4GB, + [fuse::DramId_FiveSamsung1y8GBX] = pkg1::MemorySize_8GB, + [fuse::DramId_FiveSamsung1y4GBX] = pkg1::MemorySize_4GB, + }; + + constexpr const pkg1::MemoryMode MemoryModes[] = { + pkg1::MemoryMode_Auto, + + pkg1::MemoryMode_4GB, + pkg1::MemoryMode_4GBAppletDev, + pkg1::MemoryMode_4GBSystemDev, + + pkg1::MemoryMode_6GB, + pkg1::MemoryMode_6GBAppletDev, + + pkg1::MemoryMode_8GB, + }; + + constexpr bool IsValidMemoryMode(pkg1::MemoryMode mode) { + for (const auto known_mode : MemoryModes) { + if (mode == known_mode) { + return true; + } + } + return false; + } + + pkg1::MemoryMode SanitizeMemoryMode(pkg1::MemoryMode mode) { + if (IsValidMemoryMode(mode)) { + return mode; + } + return pkg1::MemoryMode_Auto; + } + + pkg1::MemorySize GetAvailableMemorySize(pkg1::MemorySize size) { + return std::min(GetPhysicalMemorySize(), size); + } + + pkg1::MemoryMode GetMemoryMode(pkg1::MemoryMode mode) { + /* Sanitize the mode. */ + mode = SanitizeMemoryMode(mode); + + /* If the mode is auto, construct the memory mode. */ + if (mode == pkg1::MemoryMode_Auto) { + return pkg1::MakeMemoryMode(GetPhysicalMemorySize(), pkg1::MemoryArrange_Normal); + } else { + const auto mode_size = GetMemorySize(mode); + const auto mode_arrange = GetMemoryArrange(mode); + const auto size = GetAvailableMemorySize(mode_size); + const auto arrange = (size == mode_size) ? mode_arrange : pkg1::MemoryArrange_Normal; + return pkg1::MakeMemoryMode(size, arrange); + } + } + + u32 GetMemoryMode() { + /* Unless development function is enabled, we're 4 GB. */ + u32 memory_mode = pkg1::MemoryMode_4GB; + + if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) { + memory_mode = GetMemoryMode(bcd.GetMemoryMode()); + } + + return memory_mode; + } + + u32 GetKernelConfiguration() { + pkg1::MemorySize memory_size = pkg1::MemorySize_4GB; + util::BitPack32 value = {}; + + if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) { + memory_size = GetMemorySize(GetMemoryMode(bcd.GetMemoryMode())); + + value.Set(bcd.GetKernelFlags1()); + value.Set(bcd.GetKernelFlags0()); + } + + value.Set(memory_size); + + /* Exosphere extensions. */ + const auto &sc = GetSecmonConfiguration(); + + if (!sc.DisableUserModeExceptionHandlers()) { + value.Set(true); + } + + if (sc.EnableUserModePerformanceCounterAccess()) { + value.Set(true); + } + + return value.value; + } + + SmcResult GetConfig(SmcArguments &args, bool kern) { + switch (static_cast(args.r[1])) { + case ConfigItem::DisableProgramVerification: + args.r[1] = GetBootConfig().signed_data.IsProgramVerificationDisabled(); + break; + case ConfigItem::DramId: + args.r[1] = fuse::GetDramId(); + break; + case ConfigItem::SecurityEngineInterruptNumber: + args.r[1] = SecurityEngineUserInterruptId; + break; + case ConfigItem::FuseVersion: + args.r[1] = fuse::GetExpectedFuseVersion(GetTargetFirmware()); + break; + case ConfigItem::HardwareType: + args.r[1] = fuse::GetHardwareType(); + break; + case ConfigItem::HardwareState: + args.r[1] = fuse::GetHardwareState(); + break; + case ConfigItem::IsRecoveryBoot: + args.r[1] = IsRecoveryBoot(); + break; + case ConfigItem::DeviceId: + args.r[1] = fuse::GetDeviceId(); + break; + case ConfigItem::BootReason: + { + /* This was removed in firmware 4.0.0. */ + if (GetTargetFirmware() >= TargetFirmware_4_0_0) { + return SmcResult::InvalidArgument; + } + + args.r[1] = GetDeprecatedBootReason(); + } + break; + case ConfigItem::MemoryMode: + args.r[1] = GetMemoryMode(); + break; + case ConfigItem::IsDevelopmentFunctionEnabled: + args.r[1] = GetSecmonConfiguration().IsDevelopmentFunctionEnabled(kern) || GetBootConfig().data.IsDevelopmentFunctionEnabled(); + break; + case ConfigItem::KernelConfiguration: + args.r[1] = GetKernelConfiguration(); + break; + case ConfigItem::IsChargerHiZModeEnabled: + args.r[1] = IsChargerHiZModeEnabled(); + break; + case ConfigItem::QuestState: + args.r[1] = fuse::GetQuestState(); + break; + case ConfigItem::RegulatorType: + args.r[1] = fuse::GetRegulator(); + break; + case ConfigItem::DeviceUniqueKeyGeneration: + args.r[1] = fuse::GetDeviceUniqueKeyGeneration(); + break; + case ConfigItem::Package2Hash: + { + /* Only allow getting the package2 hash in recovery boot. */ + if (!IsRecoveryBoot()) { + return SmcResult::InvalidArgument; + } + + /* Get the hash. */ + se::Sha256Hash tmp_hash; + GetPackage2Hash(std::addressof(tmp_hash)); + + /* Copy it out. */ + static_assert(sizeof(args) - sizeof(args.r[0]) >= sizeof(tmp_hash)); + std::memcpy(std::addressof(args.r[1]), std::addressof(tmp_hash), sizeof(tmp_hash)); + } + break; + case ConfigItem::ExosphereApiVersion: + /* Get information about the current exosphere version. */ + args.r[1] = (static_cast(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) | + (static_cast(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) | + (static_cast(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) | + (static_cast(GetKeyGeneration()) << 32) | + (static_cast(GetTargetFirmware()) << 00); + break; + case ConfigItem::ExosphereNeedsReboot: + /* We are executing, so we aren't in the process of rebooting. */ + args.r[1] = 0; + break; + case ConfigItem::ExosphereNeedsShutdown: + /* We are executing, so we aren't in the process of shutting down. */ + args.r[1] = 0; + break; + case ConfigItem::ExosphereGitCommitHash: + /* Get information about the current exosphere git commit hash. */ + args.r[1] = ATMOSPHERE_GIT_HASH; + break; + case ConfigItem::ExosphereHasRcmBugPatch: + /* Get information about whether this unit has the RCM bug patched. */ + args.r[1] = fuse::HasRcmVulnerabilityPatch(); + break; + case ConfigItem::ExosphereBlankProdInfo: + /* Get whether this unit should simulate a "blanked" PRODINFO. */ + args.r[1] = GetSecmonConfiguration().ShouldUseBlankCalibrationBinary(); + break; + case ConfigItem::ExosphereAllowCalWrites: + /* Get whether this unit should allow writing to the calibration partition. */ + args.r[1] = (GetEmummcConfiguration().IsEmummcActive() || GetSecmonConfiguration().AllowWritingToCalibrationBinarySysmmc()); + break; + default: + return SmcResult::InvalidArgument; + } + + return SmcResult::Success; + } + + SmcResult SetConfig(SmcArguments &args) { + const auto soc_type = GetSocType(); + + switch (static_cast(args.r[1])) { + case ConfigItem::IsChargerHiZModeEnabled: + /* Configure the HiZ mode. */ + SetChargerHiZModeEnabled(static_cast(args.r[3])); + break; + case ConfigItem::ExosphereNeedsReboot: + if (soc_type == fuse::SocType_Erista) { + switch (static_cast(args.r[3])) { + case UserRebootType_None: + break; + case UserRebootType_ToRcm: + PerformUserRebootToRcm(); + break; + case UserRebootType_ToPayload: + PerformUserRebootToPayload(); + break; + default: + return SmcResult::InvalidArgument; + } + } else /* if (soc_type == fuse::SocType_Mariko) */ { + return SmcResult::NotImplemented; + } + break; + case ConfigItem::ExosphereNeedsShutdown: + if (soc_type == fuse::SocType_Erista) { + if (args.r[3] != 0) { + PerformUserShutDown(); + } + } else /* if (soc_type == fuse::SocType_Mariko) */ { + return SmcResult::NotImplemented; + } + break; + default: + return SmcResult::InvalidArgument; + } + + return SmcResult::Success; + } + + } + + SmcResult SmcGetConfigUser(SmcArguments &args) { + return GetConfig(args, false); + } + + SmcResult SmcGetConfigKern(SmcArguments &args) { + return GetConfig(args, true); + } + + SmcResult SmcSetConfig(SmcArguments &args) { + return SetConfig(args); + } + + /* This is an atmosphere extension smc. */ + SmcResult SmcGetEmummcConfig(SmcArguments &args) { + /* Decode arguments. */ + const auto mmc = static_cast(args.r[1]); + const uintptr_t user_address = args.r[2]; + const uintptr_t user_offset = user_address % 4_KB; + + /* Validate arguments. */ + /* NOTE: In the future, configuration for non-NAND storage may be implemented. */ + SMC_R_UNLESS(mmc == EmummcMmc_Nand, NotImplemented); + SMC_R_UNLESS(user_offset + 2 * sizeof(EmummcFilePath) <= 4_KB, InvalidArgument); + + /* Get the emummc config. */ + const auto &cfg = GetEmummcConfiguration(); + static_assert(sizeof(cfg.file_cfg) == sizeof(EmummcFilePath)); + static_assert(sizeof(cfg.emu_dir_path) == sizeof(EmummcFilePath)); + + /* Clear the output. */ + constexpr size_t InlineOutputSize = sizeof(args) - sizeof(args.r[0]); + u8 * const inline_output = static_cast(static_cast(std::addressof(args.r[1]))); + std::memset(inline_output, 0, InlineOutputSize); + + /* Copy out the configuration. */ + { + /* Map the user output page. */ + AtmosphereUserPageMapper mapper(user_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + + /* Copy the base configuration. */ + static_assert(sizeof(cfg.base_cfg) <= InlineOutputSize); + std::memcpy(inline_output, std::addressof(cfg.base_cfg), sizeof(cfg.base_cfg)); + + /* Copy out type-specific data. */ + switch (cfg.base_cfg.type) { + case EmummcType_None: + /* No additional configuration needs to be copied. */ + break; + case EmummcType_Partition: + /* Copy the partition config. */ + static_assert(sizeof(cfg.base_cfg) + sizeof(cfg.partition_cfg) <= InlineOutputSize); + std::memcpy(inline_output + sizeof(cfg.base_cfg), std::addressof(cfg.partition_cfg), sizeof(cfg.partition_cfg)); + break; + case EmummcType_File: + /* Copy the file config. */ + SMC_R_UNLESS(mapper.CopyToUser(user_address, std::addressof(cfg.file_cfg), sizeof(cfg.file_cfg)), InvalidArgument); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Copy the redirection directory path to the user page. */ + SMC_R_UNLESS(mapper.CopyToUser(user_address + sizeof(EmummcFilePath), std::addressof(cfg.emu_dir_path), sizeof(cfg.emu_dir_path)), InvalidArgument); + } + + return SmcResult::Success; + } + + /* For exosphere's usage. */ + pkg1::MemorySize GetPhysicalMemorySize() { + const auto dram_id = fuse::GetDramId(); + AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count); + return DramIdToMemorySize[dram_id]; + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_info.hpp b/exosphere/program/source/smc/secmon_smc_info.hpp new file mode 100644 index 0000000..d45bf57 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_info.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + enum class ConfigItem : u32 { + /* Standard config items. */ + DisableProgramVerification = 1, + DramId = 2, + SecurityEngineInterruptNumber = 3, + FuseVersion = 4, + HardwareType = 5, + HardwareState = 6, + IsRecoveryBoot = 7, + DeviceId = 8, + BootReason = 9, + MemoryMode = 10, + IsDevelopmentFunctionEnabled = 11, + KernelConfiguration = 12, + IsChargerHiZModeEnabled = 13, + QuestState = 14, + RegulatorType = 15, + DeviceUniqueKeyGeneration = 16, + Package2Hash = 17, + + /* Extension config items for exosphere. */ + ExosphereApiVersion = 65000, + ExosphereNeedsReboot = 65001, + ExosphereNeedsShutdown = 65002, + ExosphereGitCommitHash = 65003, + ExosphereHasRcmBugPatch = 65004, + ExosphereBlankProdInfo = 65005, + ExosphereAllowCalWrites = 65006, + }; + + SmcResult SmcGetConfigUser(SmcArguments &args); + SmcResult SmcGetConfigKern(SmcArguments &args); + SmcResult SmcSetConfig(SmcArguments &args); + + /* This is an atmosphere extension smc. */ + SmcResult SmcGetEmummcConfig(SmcArguments &args); + + /* For other parts of exosphere. */ + pkg1::MemorySize GetPhysicalMemorySize(); + +} diff --git a/exosphere/program/source/smc/secmon_smc_memory_access.cpp b/exosphere/program/source/smc/secmon_smc_memory_access.cpp new file mode 100644 index 0000000..dba249c --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_memory_access.cpp @@ -0,0 +1,75 @@ +/* + * 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_page_mapper.hpp" +#include "secmon_smc_memory_access.hpp" + +namespace ams::secmon::smc { + + namespace { + + enum IramCopyType { + IramCopyType_FromIramToDram = 0, + IramCopyType_FromDramToIram = 1, + IramCopyType_Count, + }; + + struct IramCopyOption { + using CopyType = util::BitPack32::Field<0, 1, IramCopyType>; + }; + + } + + /* This is an atmosphere extension smc. */ + SmcResult SmcIramCopy(SmcArguments &args) { + /* Decode arguments. */ + const uintptr_t dram_address = args.r[1]; + const uintptr_t iram_address = args.r[2]; + const size_t size = args.r[3]; + const util::BitPack32 option = { static_cast(args.r[4]) }; + + const auto copy_type = option.Get(); + + /* Validate arguments. */ + SMC_R_UNLESS(copy_type < IramCopyType_Count, InvalidArgument); + + { + /* Map the pages. */ + AtmosphereUserPageMapper dram_mapper(dram_address); + AtmosphereIramPageMapper iram_mapper(iram_address); + SMC_R_UNLESS(dram_mapper.Map(), InvalidArgument); + SMC_R_UNLESS(iram_mapper.Map(), InvalidArgument); + + /* Get the ranges we're copying. */ + const void * const src = (copy_type == IramCopyType_FromIramToDram) ? iram_mapper.GetPointerTo(iram_address, size) : dram_mapper.GetPointerTo(dram_address, size); + void * const dst = (copy_type == IramCopyType_FromIramToDram) ? dram_mapper.GetPointerTo(dram_address, size) : iram_mapper.GetPointerTo(iram_address, size); + SMC_R_UNLESS(src != nullptr, InvalidArgument); + SMC_R_UNLESS(dst != nullptr, InvalidArgument); + + /* Copy the data. */ + std::memcpy(dst, src, size); + } + + return SmcResult::Success; + } + + SmcResult SmcWriteAddress(SmcArguments &args) { + /* NOTE: This smc was deprecated in Atmosphère 0.13.0. */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_memory_access.hpp b/exosphere/program/source/smc/secmon_smc_memory_access.hpp new file mode 100644 index 0000000..3106576 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_memory_access.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + /* This is an atmosphere extension smc. */ + SmcResult SmcIramCopy(SmcArguments &args); + SmcResult SmcWriteAddress(SmcArguments &args); + +} diff --git a/exosphere/program/source/smc/secmon_smc_power_management.cpp b/exosphere/program/source/smc/secmon_smc_power_management.cpp new file mode 100644 index 0000000..93f0538 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_power_management.cpp @@ -0,0 +1,501 @@ +/* + * 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_cpu_context.hpp" +#include "../secmon_error.hpp" +#include "../secmon_misc.hpp" +#include "secmon_smc_power_management.hpp" +#include "secmon_smc_se_lock.hpp" + +#include "sc7fw_bin.h" + +namespace ams::secmon { + + /* Declare assembly functionality. */ + void *GetCoreExceptionStackVirtual(); + +} + +namespace ams::secmon::smc { + + /* Declare assembly power-management functionality. */ + void PivotStackAndInvoke(void *stack, void (*function)()); + void FinalizePowerOff(); + + namespace { + + constexpr inline const uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); + constexpr inline const uintptr_t APB_MISC = MemoryRegionVirtualDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t GPIO = MemoryRegionVirtualDeviceGpio.GetAddress(); + constexpr inline const uintptr_t CLK_RST = MemoryRegionVirtualDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t EVP = secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress(); + constexpr inline const uintptr_t FLOW_CTLR = MemoryRegionVirtualDeviceFlowController.GetAddress(); + + constexpr inline uintptr_t CommonSmcStackTop = MemoryRegionVirtualTzramVolatileData.GetEndAddress() - (0x80 * (NumCores - 1)); + + enum PowerStateType { + PowerStateType_StandBy = 0, + PowerStateType_PowerDown = 1, + }; + + enum PowerStateId { + PowerStateId_Sc7 = 27, + }; + + /* http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_Coordination_Interface_PDD_v1_1_DEN0022D.pdf Page 46 */ + struct SuspendCpuPowerState { + using StateId = util::BitPack32::Field< 0, 16, PowerStateId>; + using StateType = util::BitPack32::Field<16, 1, PowerStateType>; + using PowerLevel = util::BitPack32::Field<24, 2, u32>; + }; + + constinit bool g_charger_hi_z_mode_enabled = false; + + constinit const reg::BitsMask CpuPowerGateStatusMasks[NumCores] = { + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE0), + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE1), + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE2), + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE3), + }; + + constinit const APBDEV_PMC_PWRGATE_TOGGLE_PARTID CpuPowerGateTogglePartitionIds[NumCores] = { + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE1, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE2, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE3, + }; + + bool IsCpuPoweredOn(const reg::BitsMask mask) { + return reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, REG_BITS_VALUE_FROM_MASK(mask, APBDEV_PMC_PWRGATE_STATUS_STATUS_ON)); + } + + void PowerOnCpu(const reg::BitsMask mask, u32 toggle_partid) { + /* If the cpu is already on, we have nothing to do. */ + if (IsCpuPoweredOn(mask)) { + return; + } + + /* Wait until nothing is being powergated. */ + int timeout = 5000; + while (true) { + if (reg::HasValue(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_START, DISABLE))) { + break; + } + + util::WaitMicroSeconds(1); + if ((--timeout) < 0) { + /* NOTE: Nintendo doesn't do any error handling here... */ + return; + } + } + + /* Toggle on the cpu partition. */ + reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM (PWRGATE_TOGGLE_START, ENABLE), + PMC_REG_BITS_VALUE(PWRGATE_TOGGLE_PARTID, toggle_partid)); + + /* Wait up to 5000 us for the powergate to complete. */ + timeout = 5000; + while (true) { + if (IsCpuPoweredOn(mask)) { + break; + } + + util::WaitMicroSeconds(1); + if ((--timeout) < 0) { + /* NOTE: Nintendo doesn't do any error handling here... */ + return; + } + } + } + + void ResetCpu(int which_core) { + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */ + REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */ + } + + void StartCpu(int which_core) { + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */ + REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */ + } + + void PowerOffCpu() { + /* Get the current core id. */ + const auto core_id = hw::GetCurrentCoreId(); + + /* Configure the flow controller to prepare for shutting down the current core. */ + flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_POWERGATE_CPU_ONLY); + flow::SetHaltCpuEvents(core_id, false); + flow::SetCc4Ctrl(core_id, 0); + + /* Save the core's context for restoration on next power-on. */ + SaveDebugRegisters(); + SetCoreOff(); + + /* Ensure there are no pending memory transactions prior to our power-down. */ + FlushEntireDataCache(); + + /* Finalize our powerdown and wait for an interrupt. */ + FinalizePowerOff(); + } + + void ValidateSocStateForSuspend() { + /* Validate that all other cores are off. */ + AMS_ABORT_UNLESS(reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_VALUE(PWRGATE_STATUS_CE123, 0))); + + /* Validate that the bpmp is appropriately halted. */ + AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) == reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), + FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, IsJtagEnabled(), ENABLED, DISABLED))); + + /* TODO */ + } + + void GenerateCryptographicallyRandomBytes(void * const dst, int size) { + /* Flush the region we're about to fill to ensure consistency with the SE. */ + hw::FlushDataCache(dst, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes. */ + se::GenerateRandomBytes(dst, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Flush to ensure the CPU sees consistent data for the region. */ + hw::FlushDataCache(dst, size); + hw::DataSynchronizationBarrierInnerShareable(); + } + + void SaveSecureContextForErista() { + /* Generate a random key source. */ + util::AlignedBuffer key_source; + GenerateCryptographicallyRandomBytes(key_source, se::AesBlockSize); + + const u32 * const key_source_32 = reinterpret_cast(static_cast(key_source)); + + /* Ensure that the key source registers are not locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) != pmc::LockState::Locked); + + /* Write the key source, lock writes to the key source, and verify that the key source is write-locked. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH24, key_source_32[0]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH25, key_source_32[1]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH26, key_source_32[2]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH27, key_source_32[3]); + pmc::LockSecureRegister(pmc::SecureRegister_KeySourceWrite); + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceWrite) == pmc::LockState::Locked); + + /* Verify the key source is correct in registers, and read-lock the key source registers. */ + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH24) == key_source_32[0]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH25) == key_source_32[1]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH26) == key_source_32[2]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH27) == key_source_32[3]); + pmc::LockSecureRegister(pmc::SecureRegister_KeySourceRead); + + /* Ensure that the key source registers are locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) == pmc::LockState::Locked); + + /* Generate a random kek into keyslot 2. */ + se::SetRandomKey(pkg1::AesKeySlot_TzramSaveKek); + + /* Verify that the se is in a validate state, context save, and validate again. */ + { + se::ValidateErrStatus(); + ON_SCOPE_EXIT { se::ValidateErrStatus(); }; + + { + /* Transition to non-secure mode for the duration of the context save operation. */ + se::SetSecure(false); + ON_SCOPE_EXIT { se::SetSecure(true); }; + + /* Get a pointer to the context storage. */ + se::Context * const context = MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetPointer(); + static_assert(MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetSize() == sizeof(*context)); + + /* Save the context. */ + se::SaveContext(context); + + /* Ensure that the cpu sees consistent data. */ + hw::FlushDataCache(context, sizeof(*context)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Write the context pointer to pmc scratch, so that the bootrom will restore it on wake. */ + reg::Write(PMC + APBDEV_PMC_SCRATCH43, MemoryRegionPhysicalDramSecureDataStoreSecurityEngineState.GetAddress()); + } + } + + /* Clear keyslot 3, and then derive the save key. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); + se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, se::AesBlockSize); + + /* Declare a temporary block to be used as both iv and mac. */ + u32 temp_block[se::AesBlockSize / sizeof(u32)] = {}; + + /* Ensure that the SE sees consistent data for tzram. */ + const void * const tzram_save_src = MemoryRegionVirtualTzramReadOnlyAlias.GetPointer() + MemoryRegionVirtualTzramVolatileData.GetSize() + MemoryRegionVirtualTzramVolatileStack.GetSize(); + void * const tzram_save_dst = MemoryRegionVirtualIramSc7Work.GetPointer(); + constexpr size_t TzramSaveSize = MemoryRegionVirtualDramSecureDataStoreTzram.GetSize(); + + hw::FlushDataCache(tzram_save_src, TzramSaveSize); + hw::FlushDataCache(tzram_save_dst, TzramSaveSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Encrypt tzram using our random key. */ + se::EncryptAes256Cbc(tzram_save_dst, TzramSaveSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize, temp_block, se::AesBlockSize); + hw::FlushDataCache(tzram_save_dst, TzramSaveSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Copy the data from work space to the secure storage destination. */ + void * const tzram_store_dst = MemoryRegionVirtualDramSecureDataStoreTzram.GetPointer(); + std::memcpy(tzram_store_dst, tzram_save_dst, TzramSaveSize); + hw::FlushDataCache(tzram_store_dst, TzramSaveSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Compute cmac of tzram into our temporary block. */ + se::ComputeAes256Cmac(temp_block, se::AesBlockSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize); + + /* Ensure that the cmac registers are not locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) != pmc::LockState::Locked); + + /* Write the cmac, lock writes to the cmac, and verify that the cmac is write-locked. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH112, temp_block[0]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH113, temp_block[1]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH114, temp_block[2]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH115, temp_block[3]); + pmc::LockSecureRegister(pmc::SecureRegister_CmacWrite); + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacWrite) == pmc::LockState::Locked); + + /* Verify the key source is correct in registers, and read-lock the key source registers. */ + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH112) == temp_block[0]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH113) == temp_block[1]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH114) == temp_block[2]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH115) == temp_block[3]); + pmc::LockSecureRegister(pmc::SecureRegister_CmacRead); + + /* Ensure that the key source registers are locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) == pmc::LockState::Locked); + } + + void SaveSecureContextForMariko() { + /* TODO */ + } + + void SaveSecureContext() { + const auto soc_type = GetSocType(); + if (soc_type == fuse::SocType_Erista) { + SaveSecureContextForErista(); + } else /* if (soc_type == fuse::SocType_Mariko) */ { + SaveSecureContextForMariko(); + } + } + + void LoadAndStartSc7BpmpFirmware() { + /* Set the PMC as insecure, so that the BPMP firmware can access it. */ + reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, DISABLE)); + + /* Set the exception vectors for the bpmp. RESET should point to RESET, all others should point to generic exception/panic. */ + constexpr const u32 Sc7FirmwareResetVector = static_cast(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x0); + constexpr const u32 Sc7FirmwarePanicVector = static_cast(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x4); + + reg::Write(EVP + EVP_COP_RESET_VECTOR, Sc7FirmwareResetVector); + reg::Write(EVP + EVP_COP_UNDEF_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_SWI_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_PREFETCH_ABORT_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_DATA_ABORT_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_RSVD_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_IRQ_VECTOR, Sc7FirmwarePanicVector); + reg::Write(EVP + EVP_COP_FIQ_VECTOR, Sc7FirmwarePanicVector); + + /* Disable activity monitor bpmp monitoring, so that we don't panic upon bpmp wake. */ + actmon::StopMonitoringBpmp(); + + /* Set BPMP reset. */ + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SET_SET_COP_RST, ENABLE)); + + /* Load the bpmp firmware. */ + void * const sc7fw_load_address = MemoryRegionVirtualIramSc7Firmware.GetPointer(); + std::memcpy(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size); + hw::FlushDataCache(sc7fw_load_address, sc7fw_bin_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Ensure that the bpmp firmware was loaded. */ + AMS_ABORT_UNLESS(crypto::IsSameBytes(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size)); + + /* Clear BPMP reset. */ + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_COP_RST, ENABLE)); + + /* Start the bpmp. */ + reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_NONE)); + } + + void SaveSecureContextAndSuspend() { + /* Ensure there are no pending memory transactions before we continue */ + FlushEntireDataCache(); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Save all secure context (security engine state + tzram). */ + SaveSecureContext(); + + /* Load and start the sc7 firmware on the bpmp. */ + LoadAndStartSc7BpmpFirmware(); + + /* Log our suspension. */ + /* NOTE: Nintendo only does this on dev, but we will always do it. */ + if (true /* !pkg1::IsProduction() */) { + log::SendText("OYASUMI\n", 8); + } + + /* If we're on erista, configure the bootrom to allow our custom warmboot firmware. */ + if (GetSocType() == fuse::SocType_Erista) { + reg::Write(PMC + APBDEV_PMC_SCRATCH31, 0x2202E012); + reg::Write(PMC + APBDEV_PMC_SCRATCH32, 0x6001DC28); + } + + /* Finalize our powerdown and wait for an interrupt. */ + FinalizePowerOff(); + } + + SmcResult SuspendCpuImpl(SmcArguments &args) { + /* Decode arguments. */ + const util::BitPack32 power_state = { static_cast(args.r[1]) }; + const uintptr_t entry_point = args.r[2]; + const uintptr_t context_id = args.r[3]; + + const auto state_type = power_state.Get(); + const auto state_id = power_state.Get(); + + const auto core_id = hw::GetCurrentCoreId(); + + /* Validate arguments. */ + SMC_R_UNLESS(state_type == PowerStateType_PowerDown, PsciDenied); + SMC_R_UNLESS(state_id == PowerStateId_Sc7, PsciDenied); + + /* Orchestrate charger transition to Hi-Z mode if needed. */ + if (IsChargerHiZModeEnabled()) { + /* Ensure we can do comms over i2c-1. */ + clkrst::EnableI2c1Clock(); + + /* If the charger isn't in hi-z mode, perform a transition. */ + if (!charger::IsHiZMode()) { + charger::EnterHiZMode(); + + /* Wait up to 50ms for the transition to complete. */ + const auto start_time = util::GetMicroSeconds(); + auto current_time = start_time; + while ((current_time - start_time) <= 50'000) { + if (auto intr_status = reg::Read(GPIO + 0x634); (intr_status & 1) == 0) { + /* Wait 256 us to ensure the transition completes. */ + util::WaitMicroSeconds(256); + break; + } + current_time = util::GetMicroSeconds(); + } + } + + /* Disable i2c-1, since we're done communicating over it. */ + clkrst::DisableI2c1Clock(); + } + + /* Enable wake event detection. */ + pmc::EnableWakeEventDetection(); + + /* Ensure that i2c-5 is usable for communicating with the pmic. */ + clkrst::EnableI2c5Clock(); + i2c::Initialize(i2c::Port_5); + + /* Orchestrate sleep entry with the pmic. */ + pmic::EnableSleep(); + + /* Ensure that the soc is in a state valid for us to suspend. */ + ValidateSocStateForSuspend(); + + /* Configure the pmc for sc7 entry. */ + pmc::ConfigureForSc7Entry(); + + /* Configure the flow controller for sc7 entry. */ + flow::SetCc4Ctrl(core_id, 0); + flow::SetHaltCpuEvents(core_id, false); + flow::ClearL2FlushControl(); + flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_POWERGATE_CPU_TURNOFF_CPURAIL); + + /* Save the entry context. */ + SetEntryContext(core_id, entry_point, context_id); + + /* Configure the cpu context for reset. */ + SaveDebugRegisters(); + SetCoreOff(); + SetResetExpected(true); + + /* Switch to use the common smc stack (all other cores are off), and perform suspension. */ + PivotStackAndInvoke(reinterpret_cast(CommonSmcStackTop), SaveSecureContextAndSuspend); + + /* This code will never be reached. */ + __builtin_unreachable(); + } + + } + + SmcResult SmcPowerOffCpu(SmcArguments &args) { + /* Get the current core id. */ + const auto core_id = hw::GetCurrentCoreId(); + + /* Note that we're expecting a reset for the current core. */ + SetResetExpected(true); + + /* If we're on the final core, shut down directly. Otherwise, invoke with special stack. */ + if (core_id == NumCores - 1) { + PowerOffCpu(); + } else { + PivotStackAndInvoke(GetCoreExceptionStackVirtual(), PowerOffCpu); + } + + /* This code will never be reached. */ + __builtin_unreachable(); + } + + SmcResult SmcPowerOnCpu(SmcArguments &args) { + /* Get and validate the core to power on. */ + const int which_core = args.r[1]; + SMC_R_UNLESS(0 <= which_core && which_core < NumCores, PsciInvalidParameters); + + /* Ensure the core isn't already on. */ + SMC_R_UNLESS(!IsCoreOn(which_core), PsciAlreadyOn); + + /* Save the entry context. */ + SetEntryContext(which_core, args.r[2], args.r[3]); + + /* Reset the cpu. */ + ResetCpu(which_core); + + /* Turn on the core. */ + PowerOnCpu(CpuPowerGateStatusMasks[which_core], CpuPowerGateTogglePartitionIds[which_core]); + + /* Start the core. */ + StartCpu(which_core); + + return SmcResult::PsciSuccess; + } + + SmcResult SmcSuspendCpu(SmcArguments &args) { + return LockSecurityEngineAndInvoke(args, SuspendCpuImpl); + } + + bool IsChargerHiZModeEnabled() { + return g_charger_hi_z_mode_enabled; + } + + void SetChargerHiZModeEnabled(bool en) { + g_charger_hi_z_mode_enabled = en; + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_power_management.hpp b/exosphere/program/source/smc/secmon_smc_power_management.hpp new file mode 100644 index 0000000..65bd53d --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_power_management.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcPowerOffCpu(SmcArguments &args); + SmcResult SmcPowerOnCpu(SmcArguments &args); + + SmcResult SmcSuspendCpu(SmcArguments &args); + + bool IsChargerHiZModeEnabled(); + void SetChargerHiZModeEnabled(bool en); + +} diff --git a/exosphere/program/source/smc/secmon_smc_random.cpp b/exosphere/program/source/smc/secmon_smc_random.cpp new file mode 100644 index 0000000..6b3099a --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_random.cpp @@ -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 . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_random.hpp" +#include "secmon_random_cache.hpp" +#include "secmon_smc_se_lock.hpp" + +namespace ams::secmon::smc { + + namespace { + + SmcResult GenerateRandomBytesImpl(SmcArguments &args) { + /* Validate the input size. */ + const size_t size = args.r[1]; + SMC_R_UNLESS(size <= MaxRandomBytes, InvalidArgument); + + /* Create a buffer that the se can generate bytes into. */ + util::AlignedBuffer buffer; + hw::FlushDataCache(buffer, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes into the buffer. */ + se::GenerateRandomBytes(buffer, size); + + /* Ensure that the cpu sees consistent data. */ + hw::DataSynchronizationBarrierInnerShareable(); + hw::FlushDataCache(buffer, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Copy the bytes to output. */ + std::memcpy(std::addressof(args.r[1]), buffer, size); + return SmcResult::Success; + } + + } + + SmcResult SmcGenerateRandomBytes(SmcArguments &args) { + return LockSecurityEngineAndInvoke(args, GenerateRandomBytesImpl); + } + + SmcResult SmcGenerateRandomBytesNonBlocking(SmcArguments &args) { + /* Try to lock the security engine, so that we can call the standard impl. */ + if (TryLockSecurityEngine()) { + /* Ensure we unlock the security engine when done. */ + ON_SCOPE_EXIT { UnlockSecurityEngine(); }; + + /* Take advantage of our lock to refill lthe random cache. */ + ON_SCOPE_EXIT { RefillRandomCache(); }; + + /* If we lock it successfully, we can just call the blocking impl. */ + return GenerateRandomBytesImpl(args); + } else { + /* Otherwise, we'll retrieve some bytes from the cache. */ + const size_t size = args.r[1]; + SMC_R_UNLESS(size <= MaxRandomBytes, InvalidArgument); + + /* Get random bytes from the cache. */ + GetRandomFromCache(std::addressof(args.r[1]), size); + return SmcResult::Success; + } + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_random.hpp b/exosphere/program/source/smc/secmon_smc_random.hpp new file mode 100644 index 0000000..98c2990 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_random.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGenerateRandomBytes(SmcArguments &args); + SmcResult SmcGenerateRandomBytesNonBlocking(SmcArguments &args); + +} diff --git a/exosphere/program/source/smc/secmon_smc_register_access.cpp b/exosphere/program/source/smc/secmon_smc_register_access.cpp new file mode 100644 index 0000000..cdf8343 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_register_access.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_register_access.hpp" + +namespace ams::secmon::smc { + + namespace { + + template + constexpr void SetRegisterTableAllowed(std::array &arr, uintptr_t reg) { + /* All registers should be four byte aligned. */ + AMS_ASSUME(reg % sizeof(u32) == 0); + + /* Reduce the register to an index. */ + reg /= sizeof(u32); + + /* Get the index and mask. */ + const auto index = reg / BITSIZEOF(u8); + const auto mask = (1u << (reg % BITSIZEOF(u8))); + + /* Check that the permission bit isn't already set. */ + AMS_ASSUME((arr[index] & mask) == 0); + + /* Set the permission bit. */ + arr[index] |= mask; + + /* Ensure that indices are set in sorted order. */ + for (auto i = (reg % BITSIZEOF(u8)) + 1; i < 8; ++i) { + AMS_ASSUME((arr[index] & (1u << i)) == 0); + } + + for (auto i = index + 1; i < arr.size(); ++i) { + AMS_ASSUME(arr[i] == 0); + } + } + + template + consteval std::pair GetReducedAccessTableInfo(const std::array &arr) { + for (int last = arr.size() - 1; last >= 0; --last) { + if (arr[last] != 0) { + const int end = last + 1; + for (int start = 0; start < end; ++start) { + if (arr[start] != 0) { + return std::make_pair(static_cast(start), static_cast(end)); + } + } + return std::make_pair(static_cast(0), static_cast(end)); + } + } + + /* All empty perm table is disallowed. */ + AMS_ASSUME(false); + } + + + template + struct AccessTable { + static constexpr inline auto ReducedAccessTableInfo = GetReducedAccessTableInfo(RawTable); + static constexpr inline size_t ReducedAccessTableSize = ReducedAccessTableInfo.second - ReducedAccessTableInfo.first; + static constexpr inline auto ReducedAccessTable = []() -> std::array { + std::array reduced = {}; + + for (size_t i = ReducedAccessTableInfo.first; i < ReducedAccessTableInfo.second; ++i) { + reduced[i - ReducedAccessTableInfo.first] = RawTable[i]; + } + + return reduced; + }(); + + static constexpr u32 Address = _Address + (ReducedAccessTableInfo.first * sizeof(u32) * BITSIZEOF(u8)); + static constexpr u32 Size = static_cast(ReducedAccessTableSize * sizeof(u32) * BITSIZEOF(u8)); + + static_assert(Size <= 0x1000); + }; + + struct AccessTableEntry { + const u8 * const table; + uintptr_t virtual_address; + u32 address; + u32 size; + }; + + /* Include the access tables. */ + #include "secmon_define_pmc_access_table.inc" + #include "secmon_define_mc_access_table.inc" + #include "secmon_define_mc01_access_table.inc" + + constexpr const AccessTableEntry AccessTables[] = { + { PmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDevicePmc.GetAddress(), PmcAccessTable::Address, PmcAccessTable::Size, }, + { McAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController.GetAddress(), McAccessTable::Address, McAccessTable::Size, }, + { Mc01AccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController0.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController0.GetAddress(), Mc01AccessTable::Size, }, + { Mc01AccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController1.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController1.GetAddress(), Mc01AccessTable::Size, }, + }; + + constexpr bool IsAccessAllowed(const AccessTableEntry &entry, uintptr_t address) { + /* Check if the address is within range. */ + if (!(entry.address <= address && address < entry.address + entry.size)) { + return false; + } + + /* Get the offset. */ + const auto offset = address - entry.address; + + /* Convert it to an index. */ + const auto reg_index = offset / sizeof(u32); + + /* Get the bit fields. */ + const auto index = reg_index / BITSIZEOF(u8); + const auto mask = (1u << (reg_index % BITSIZEOF(u8))); + + /* Validate that we're not going out of bounds. */ + if (index >= entry.size / sizeof(u32)) { + return false; + } + + return (entry.table[index] & mask) != 0; + } + + constexpr const AccessTableEntry *GetAccessTableEntry(uintptr_t address) { + for (const auto &entry : AccessTables) { + if (IsAccessAllowed(entry, address)) { + return std::addressof(entry); + } + } + + return nullptr; + } + + } + + SmcResult SmcReadWriteRegister(SmcArguments &args) { + /* Get the arguments. */ + const uintptr_t address = args.r[1]; + const u32 mask = args.r[2]; + const u32 value = args.r[3]; + + /* Validate that the address is aligned. */ + SMC_R_UNLESS(util::IsAligned(address, alignof(u32)), InvalidArgument); + + /* Find the access table. */ + const AccessTableEntry * const entry = GetAccessTableEntry(address); + + /* If we have a table, perform the write. */ + if (entry != nullptr) { + /* Get the address to read or write. */ + const uintptr_t virtual_address = entry->virtual_address + (address - entry->address); + u32 out = 0; + + if (mask != ~static_cast(0)) { + out = reg::Read(virtual_address); + } + if (mask != static_cast(0)) { + reg::Write(virtual_address, (out & ~mask) | (value & mask)); + } + + args.r[1] = out; + } else { + /* For no clearly discernable reason, SmcReadWriteRegister returns success despite not doing the read/write */ + /* when accessing the SMMU controls for the BPMP and for APB-DMA. */ + /* This is "probably" to fuck with hackers who got access to the SMC and are trying to get control of the */ + /* BPMP to exploit jamais vu, deja vu, or other related DMA/wake-from-sleep vulnerabilities. */ + constexpr uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress(); + SMC_R_UNLESS((address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)), InvalidArgument); + } + + return SmcResult::Success; + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_register_access.hpp b/exosphere/program/source/smc/secmon_smc_register_access.hpp new file mode 100644 index 0000000..6a74cdc --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_register_access.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcReadWriteRegister(SmcArguments &args); + +} diff --git a/exosphere/program/source/smc/secmon_smc_result.cpp b/exosphere/program/source/smc/secmon_smc_result.cpp new file mode 100644 index 0000000..246f50c --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_result.cpp @@ -0,0 +1,106 @@ +/* + * 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_page_mapper.hpp" +#include "secmon_smc_result.hpp" + +namespace ams::secmon::smc { + + namespace { + + constinit u64 g_async_key = InvalidAsyncKey; + constinit GetResultHandler g_async_handler = nullptr; + + u64 GenerateRandomU64() { + /* NOTE: This is one of the only places where Nintendo does not do data flushing. */ + /* to ensure coherency when doing random byte generation. */ + /* It is not clear why it is necessary elsewhere but not here. */ + /* TODO: Figure out why. */ + u64 v; + se::GenerateRandomBytes(std::addressof(v), sizeof(v)); + return v; + } + + } + + u64 BeginAsyncOperation(GetResultHandler handler) { + /* Only allow one async operation at a time. */ + if (g_async_key != InvalidAsyncKey) { + return InvalidAsyncKey; + } + + /* Generate a random async key. */ + g_async_key = GenerateRandomU64(); + g_async_handler = handler; + + return g_async_key; + } + + void CancelAsyncOperation(u64 async_key) { + if (async_key == g_async_key) { + g_async_key = InvalidAsyncKey; + } + } + + void EndAsyncOperation() { + gic::SetPending(SecurityEngineUserInterruptId); + } + + SmcResult SmcGetResult(SmcArguments &args) { + /* Decode arguments. */ + const u64 async_key = args.r[1]; + + /* Validate arguments. */ + SMC_R_UNLESS(g_async_key != InvalidAsyncKey, NoAsyncOperation); + SMC_R_UNLESS(g_async_key == async_key, InvalidAsyncOperation); + + /* Call the handler. */ + args.r[1] = static_cast(g_async_handler(nullptr, 0)); + g_async_key = InvalidAsyncKey; + + return SmcResult::Success; + } + + SmcResult SmcGetResultData(SmcArguments &args) { + /* Decode arguments. */ + const u64 async_key = args.r[1]; + const uintptr_t user_phys_addr = args.r[2]; + const size_t user_size = args.r[3]; + + /* Allocate a work buffer on the stack. */ + alignas(8) u8 work_buffer[1_KB]; + + /* Validate arguments. */ + SMC_R_UNLESS(g_async_key != InvalidAsyncKey, NoAsyncOperation); + SMC_R_UNLESS(g_async_key == async_key, InvalidAsyncOperation); + SMC_R_UNLESS(user_size <= sizeof(work_buffer), InvalidArgument); + + /* Call the handler. */ + args.r[1] = static_cast(g_async_handler(work_buffer, user_size)); + g_async_key = InvalidAsyncKey; + + /* Map the user buffer. */ + { + UserPageMapper mapper(user_phys_addr); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyToUser(user_phys_addr, work_buffer, user_size), InvalidArgument); + } + + return SmcResult::Success; + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_result.hpp b/exosphere/program/source/smc/secmon_smc_result.hpp new file mode 100644 index 0000000..853772e --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_result.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + using GetResultHandler = SmcResult (*)(void *dst, size_t dst_size); + + u64 BeginAsyncOperation(GetResultHandler handler); + void CancelAsyncOperation(u64 async_key); + void EndAsyncOperation(); + + SmcResult SmcGetResult(SmcArguments &args); + SmcResult SmcGetResultData(SmcArguments &args); + +} diff --git a/exosphere/program/source/smc/secmon_smc_rsa.cpp b/exosphere/program/source/smc/secmon_smc_rsa.cpp new file mode 100644 index 0000000..d84ce9b --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_rsa.cpp @@ -0,0 +1,357 @@ +/* + * 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_page_mapper.hpp" +#include "secmon_smc_aes.hpp" +#include "secmon_smc_rsa.hpp" +#include "secmon_smc_se_lock.hpp" + +namespace ams::secmon::smc { + + namespace { + + struct ModularExponentiateByStorageKeyOption { + using Mode = util::BitPack32::Field<0, 2, u32>; + using Reserved = util::BitPack32::Field<2, 30, u32>; + }; + + struct PrepareEsDeviceUniqueKeyOption { + using KeyGeneration = util::BitPack32::Field<0, 6, int>; + using Type = util::BitPack32::Field<6, 1, EsCommonKeyType>; + using Reserved = util::BitPack32::Field<7, 25, u32>; + }; + + constexpr const u8 ModularExponentiateByStorageKeyTable[] = { + static_cast(ImportRsaKey_Lotus), + static_cast(ImportRsaKey_Ssl), + static_cast(ImportRsaKey_EsClientCert), + }; + constexpr size_t ModularExponentiateByStorageKeyTableSize = util::size(ModularExponentiateByStorageKeyTable); + + class PrepareEsDeviceUniqueKeyAsyncArguments { + private: + int generation; + EsCommonKeyType type; + u8 label_digest[crypto::Sha256Generator::HashSize]; + public: + void Set(int gen, EsCommonKeyType t, const u8 ld[crypto::Sha256Generator::HashSize]) { + this->generation = gen; + this->type = t; + std::memcpy(this->label_digest, ld, sizeof(this->label_digest)); + } + + int GetKeyGeneration() const { return this->generation; } + EsCommonKeyType GetCommonKeyType() const { return this->type; } + void GetLabelDigest(u8 dst[crypto::Sha256Generator::HashSize]) const { std::memcpy(dst, this->label_digest, sizeof(this->label_digest)); } + }; + + class ModularExponentiateByStorageKeyAsyncArguments { + private: + u8 msg[se::RsaSize]; + public: + void Set(const void *m, size_t m_size) { + std::memcpy(this->msg, m, sizeof(this->msg)); + } + + const u8 *GetMessage() const { return this->msg; } + }; + + constinit SmcResult g_exp_mod_result = SmcResult::Success; + + constinit bool g_test_exp_mod_public = false; + constinit int g_test_exp_mod_slot = pkg1::RsaKeySlot_Temporary; + constinit ImportRsaKey g_test_exp_mod_key = {}; + + constinit union { + ModularExponentiateByStorageKeyAsyncArguments modular_exponentiate_by_storage_key; + PrepareEsDeviceUniqueKeyAsyncArguments prepare_es_device_unique_key; + } g_async_arguments; + + ALWAYS_INLINE ModularExponentiateByStorageKeyAsyncArguments &GetModularExponentiateByStorageKeyAsyncArguments() { + return g_async_arguments.modular_exponentiate_by_storage_key; + } + + ALWAYS_INLINE PrepareEsDeviceUniqueKeyAsyncArguments &GetPrepareEsDeviceUniqueKeyAsyncArguments() { + return g_async_arguments.prepare_es_device_unique_key; + } + + void SecurityEngineDoneHandler() { + /* End the asynchronous operation. */ + g_exp_mod_result = SmcResult::Success; + EndAsyncOperation(); + } + + void TestRsaPublicKey(ImportRsaKey which, int slot, const void *mod, size_t mod_size, se::DoneHandler handler) { + /* Declare a buffer for our test message. */ + u8 msg[se::RsaSize]; + std::memset(msg, 'D', sizeof(msg)); + + /* Provisionally import the modulus. */ + ImportRsaKeyModulusProvisionally(which, mod, mod_size); + + /* Load the provisional public key into the slot. */ + LoadProvisionalRsaPublicKey(slot, which); + + /* Perform the test exponentiation. */ + se::ModularExponentiateAsync(slot, msg, sizeof(msg), handler); + } + + void TestRsaPrivateKey(ImportRsaKey which, int slot, se::DoneHandler handler) { + /* Get the result of the public key test. */ + u8 msg[se::RsaSize]; + se::GetRsaResult(msg, sizeof(msg)); + + /* Load the provisional private key into the slot. */ + LoadProvisionalRsaKey(slot, which); + + /* Perform the test exponentiation. */ + se::ModularExponentiateAsync(slot, msg, sizeof(msg), handler); + } + + void VerifyTestRsaKeyResult(ImportRsaKey which) { + /* Get the result of the test. */ + u8 msg[se::RsaSize]; + se::GetRsaResult(msg, sizeof(msg)); + + /* Validate the result. */ + const bool is_valid = (msg[0] == 'D') & (crypto::IsSameBytes(msg, msg + 1, sizeof(msg) - 1)); + + /* If the test passes, the key is no longer provisional. */ + if (is_valid) { + CommitRsaKeyModulus(which); + } + } + + void TestRsaKeyDoneHandler() { + if (g_test_exp_mod_public) { + /* If we're testing the public key, we still have another exponentiation to do to test the private key. */ + g_test_exp_mod_public = false; + + /* Test the private key. */ + TestRsaPrivateKey(g_test_exp_mod_key, g_test_exp_mod_slot, TestRsaKeyDoneHandler); + } else { + /* We're testing the private key, so validate the result. */ + VerifyTestRsaKeyResult(g_test_exp_mod_key); + + /* If the test passed, we can proceed to perform the intended exponentiation. */ + if (LoadRsaKey(g_test_exp_mod_slot, g_test_exp_mod_key)) { + se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, GetModularExponentiateByStorageKeyAsyncArguments().GetMessage(), se::RsaSize, SecurityEngineDoneHandler); + } else { + /* The test failed, so end the asynchronous operation. */ + g_exp_mod_result = SmcResult::InvalidArgument; + EndAsyncOperation(); + } + } + } + + SmcResult ModularExponentiateImpl(SmcArguments &args) { + /* Decode arguments. */ + const uintptr_t msg_address = args.r[1]; + const uintptr_t exp_address = args.r[2]; + const uintptr_t mod_address = args.r[3]; + const size_t exp_size = args.r[4]; + + /* Validate arguments. */ + SMC_R_UNLESS(util::IsAligned(exp_size, sizeof(u32)), InvalidArgument); + SMC_R_UNLESS(exp_size <= se::RsaSize, InvalidArgument); + + /* Copy the message and modulus from the user. */ + alignas(8) u8 msg[se::RsaSize]; + alignas(8) u8 exp[se::RsaSize]; + alignas(8) u8 mod[se::RsaSize]; + { + UserPageMapper mapper(msg_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(exp, exp_address, exp_size), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); + } + + /* We're performing an operation, so set the result to busy. */ + g_exp_mod_result = SmcResult::Busy; + + /* Load the key into the temporary keyslot. */ + se::SetRsaKey(pkg1::RsaKeySlot_Temporary, mod, sizeof(mod), exp, exp_size); + + /* Begin the asynchronous exponentiation. */ + se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler); + + return SmcResult::Success; + } + + SmcResult ModularExponentiateByStorageKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + const uintptr_t msg_address = args.r[1]; + const uintptr_t mod_address = args.r[2]; + const util::BitPack32 option = { static_cast(args.r[3]) }; + + const auto mode = option.Get(); + const auto reserved = option.Get(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + SMC_R_UNLESS(mode < ModularExponentiateByStorageKeyTableSize, InvalidArgument); + + /* Convert the mode to an import key. */ + const auto import_key = static_cast(ModularExponentiateByStorageKeyTable[mode]); + + /* Copy the message and modulus from the user. */ + alignas(8) u8 msg[se::RsaSize]; + alignas(8) u8 mod[se::RsaSize]; + { + UserPageMapper mapper(msg_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); + } + + /* We're performing an operation, so set the result to busy. */ + g_exp_mod_result = SmcResult::Busy; + + /* In the ideal case, the key pair is already verified. If it is, we can use it directly. */ + if (LoadRsaKey(pkg1::RsaKeySlot_Temporary, import_key)) { + se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler); + } else { + /* Set the async arguments. */ + GetModularExponentiateByStorageKeyAsyncArguments().Set(msg, sizeof(msg)); + + /* Test the rsa key. */ + g_test_exp_mod_slot = pkg1::RsaKeySlot_Temporary; + g_test_exp_mod_key = import_key; + g_test_exp_mod_public = true; + + TestRsaPublicKey(import_key, pkg1::RsaKeySlot_Temporary, mod, sizeof(mod), TestRsaKeyDoneHandler); + } + + return SmcResult::Success; + } + + SmcResult PrepareEsDeviceUniqueKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 label_digest[crypto::Sha256Generator::HashSize]; + + const uintptr_t msg_address = args.r[1]; + const uintptr_t mod_address = args.r[2]; + std::memcpy(label_digest, std::addressof(args.r[3]), sizeof(label_digest)); + const util::BitPack32 option = { static_cast(args.r[7]) }; + + const auto generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, option.Get() - 1) : 0; + const auto type = option.Get(); + const auto reserved = option.Get(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); + SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument); + SMC_R_UNLESS(type < EsCommonKeyType_Count, InvalidArgument); + + /* Copy the message and modulus from the user. */ + alignas(8) u8 msg[se::RsaSize]; + alignas(8) u8 mod[se::RsaSize]; + { + UserPageMapper mapper(msg_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); + } + + /* We're performing an operation, so set the result to busy. */ + g_exp_mod_result = SmcResult::Busy; + + /* Set the async arguments. */ + GetPrepareEsDeviceUniqueKeyAsyncArguments().Set(generation, type, label_digest); + + /* Load the es drm key into the security engine. */ + SMC_R_UNLESS(LoadRsaKey(pkg1::RsaKeySlot_Temporary, ImportRsaKey_EsDrmCert), NotInitialized); + + /* Trigger the asynchronous modular exponentiation. */ + se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler); + + return SmcResult::Success; + } + + SmcResult GetModularExponentiateResult(void *dst, size_t dst_size) { + /* Validate state. */ + SMC_R_TRY(g_exp_mod_result); + SMC_R_UNLESS(dst_size == se::RsaSize, InvalidArgument); + + /* We want to relinquish our security engine lock at the end of scope. */ + ON_SCOPE_EXIT { UnlockSecurityEngine(); }; + + /* Get the result of the exponentiation. */ + se::GetRsaResult(dst, se::RsaSize); + + return SmcResult::Success; + } + + SmcResult GetPrepareEsDeviceUniqueKeyResult(void *dst, size_t dst_size) { + /* Declare variables. */ + u8 key_source[se::AesBlockSize]; + u8 key[se::AesBlockSize]; + u8 access_key[se::AesBlockSize]; + + /* Validate state. */ + SMC_R_TRY(g_exp_mod_result); + SMC_R_UNLESS(dst_size == sizeof(access_key), InvalidArgument); + + /* We want to relinquish our security engine lock at the end of scope. */ + ON_SCOPE_EXIT { UnlockSecurityEngine(); }; + + /* Get the async args. */ + const auto &async_args = GetPrepareEsDeviceUniqueKeyAsyncArguments(); + + /* Get the exponentiation output. */ + alignas(8) u8 msg[se::RsaSize]; + se::GetRsaResult(msg, sizeof(msg)); + + /* Decode the key. */ + { + /* Get the label digest. */ + u8 label_digest[crypto::Sha256Generator::HashSize]; + async_args.GetLabelDigest(label_digest); + + /* Decode the key source. */ + const size_t key_source_size = se::DecodeRsaOaepSha256(key_source, sizeof(key_source), msg, sizeof(msg), label_digest, sizeof(label_digest)); + SMC_R_UNLESS(key_source_size == sizeof(key_source), InvalidArgument); + } + + /* Decrypt the key. */ + DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), async_args.GetCommonKeyType(), async_args.GetKeyGeneration()); + PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key)); + + /* Copy the access key to output. */ + std::memcpy(dst, access_key, sizeof(access_key)); + + return SmcResult::Success; + } + + } + + SmcResult SmcModularExponentiate(SmcArguments &args) { + return LockSecurityEngineAndInvokeAsync(args, ModularExponentiateImpl, GetModularExponentiateResult); + } + + SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args) { + return LockSecurityEngineAndInvokeAsync(args, ModularExponentiateByStorageKeyImpl, GetModularExponentiateResult); + } + + SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) { + return LockSecurityEngineAndInvokeAsync(args, PrepareEsDeviceUniqueKeyImpl, GetPrepareEsDeviceUniqueKeyResult); + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_rsa.hpp b/exosphere/program/source/smc/secmon_smc_rsa.hpp new file mode 100644 index 0000000..63859a9 --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_rsa.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcModularExponentiate(SmcArguments &args); + SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args); + + SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args); + +} diff --git a/exosphere/program/source/smc/secmon_smc_se_lock.cpp b/exosphere/program/source/smc/secmon_smc_se_lock.cpp new file mode 100644 index 0000000..b8ce15a --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_se_lock.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_se_lock.hpp" + +namespace ams::secmon::smc { + + namespace { + + constinit std::atomic_bool g_is_locked = false; + + } + + bool TryLockSecurityEngine() { + bool value = false; + return g_is_locked.compare_exchange_strong(value, true); + } + + void UnlockSecurityEngine() { + g_is_locked = false; + } + + bool IsSecurityEngineLocked() { + return g_is_locked; + } + + SmcResult LockSecurityEngineAndInvoke(SmcArguments &args, SmcHandler impl) { + /* Try to lock the security engine. */ + SMC_R_UNLESS(TryLockSecurityEngine(), Busy); + ON_SCOPE_EXIT { UnlockSecurityEngine(); }; + + return impl(args); + } + + SmcResult LockSecurityEngineAndInvokeAsync(SmcArguments &args, SmcHandler impl, GetResultHandler result_handler) { + /* Try to lock the security engine. */ + SMC_R_UNLESS(TryLockSecurityEngine(), Busy); + auto se_guard = SCOPE_GUARD { UnlockSecurityEngine(); }; + + /* Try to start an async operation. */ + const u64 async_key = BeginAsyncOperation(result_handler); + SMC_R_UNLESS(async_key != InvalidAsyncKey, Busy); + auto async_guard = SCOPE_GUARD { CancelAsyncOperation(async_key); }; + + /* Try to invoke the operation. */ + SMC_R_TRY(impl(args)); + + /* We succeeded! Cancel our guards, and return the async key to our caller. */ + async_guard.Cancel(); + se_guard.Cancel(); + + args.r[1] = async_key; + return SmcResult::Success; + } + +} diff --git a/exosphere/program/source/smc/secmon_smc_se_lock.hpp b/exosphere/program/source/smc/secmon_smc_se_lock.hpp new file mode 100644 index 0000000..d7fc33d --- /dev/null +++ b/exosphere/program/source/smc/secmon_smc_se_lock.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" +#include "secmon_smc_handler.hpp" +#include "secmon_smc_result.hpp" + +namespace ams::secmon::smc { + + bool TryLockSecurityEngine(); + void UnlockSecurityEngine(); + bool IsSecurityEngineLocked(); + + SmcResult LockSecurityEngineAndInvoke(SmcArguments &args, SmcHandler impl); + SmcResult LockSecurityEngineAndInvokeAsync(SmcArguments &args, SmcHandler impl, GetResultHandler result_handler); + +} diff --git a/exosphere/program/split_program.py b/exosphere/program/split_program.py new file mode 100644 index 0000000..b52406e --- /dev/null +++ b/exosphere/program/split_program.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +import sys, lz4 +from struct import unpack as up + +def lz4_compress(data): + return lz4.block.compress(data, 'high_compression', store_size=False) + +def split_binary(data): + A, B, START, BOOT_CODE_START, BOOT_CODE_END, PROGRAM_START, C, D = up('= 0x40 + for (fn, fdata) in split_binary(data): + with open('%s/%s' % (argv[2], fn), 'wb') as f: + f.write(fdata) + return 0 + +if __name__ == '__main__': + sys.exit(main(len(sys.argv), sys.argv)) \ No newline at end of file diff --git a/exosphere/warmboot/Makefile b/exosphere/warmboot/Makefile new file mode 100644 index 0000000..2288e27 --- /dev/null +++ b/exosphere/warmboot/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/warmboot/source/warmboot_bootrom_workaround.cpp b/exosphere/warmboot/source/warmboot_bootrom_workaround.cpp new file mode 100644 index 0000000..f49bf24 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_bootrom_workaround.cpp @@ -0,0 +1,99 @@ +/* + * 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 "warmboot_clkrst.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t EMC = EMC_ADDRESS(0); + + } + + void ApplyMbistWorkaround() { + /* Clear all LVL2 clock gate overrides to zero. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE, 0); + + /* Clear clock enable for all but a select few devices. */ + auto devices_to_clear_l = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_L); + reg::ClearBits(static_cast(devices_to_clear_l), CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_RTC ), + CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_TMR ), + CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_GPIO ), + CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_CACHE2)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, devices_to_clear_l); + + auto devices_to_clear_h = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_H); + reg::ClearBits(static_cast(devices_to_clear_h), CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_MEM ), + CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_PMC ), + CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_FUSE), + CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_EMC )); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, devices_to_clear_h); + + auto devices_to_clear_u = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_U); + reg::ClearBits(static_cast(devices_to_clear_u), CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_CSITE), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMA), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMB), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMC), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMD), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_CRAM2)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, devices_to_clear_u); + + auto devices_to_clear_v = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_V); + reg::ClearBits(static_cast(devices_to_clear_v), CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_MSELECT ), + CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_SPDIF_DOUBLER), + CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_TZRAM ), + CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_SE )); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_CLR, devices_to_clear_v); + + auto devices_to_clear_w = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_W); + reg::ClearBits(static_cast(devices_to_clear_w), CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX0), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX1), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX2), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX3), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX4), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX5), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_ENTROPY)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_CLR, devices_to_clear_w); + + auto devices_to_clear_x = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_X); + reg::ClearBits(static_cast(devices_to_clear_x), CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CAPA ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CBPA ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CPU ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_BBC ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_EMC_DLL ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_GPU ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_DBGAPB ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_PLLG_REF)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_X_CLR, devices_to_clear_x); + + auto devices_to_clear_y = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_Y); + reg::ClearBits(static_cast(devices_to_clear_y), CLK_RST_REG_BITS_MASK(CLK_ENB_Y_CLK_ENB_MC_CCPA), + CLK_RST_REG_BITS_MASK(CLK_ENB_Y_CLK_ENB_MC_CDPA)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_Y_CLR, devices_to_clear_y); + + /* If CH1 is enabled, enable clock to MC1. */ + if (reg::HasValue(EMC + EMC_FBIO_CFG7, EMC_REG_BITS_ENUM(FBIO_CFG7_CH1_ENABLE, ENABLE))) { + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_CLK_ENB_MC1, ENABLE)); + } + } + +} diff --git a/exosphere/warmboot/source/warmboot_bootrom_workaround.hpp b/exosphere/warmboot/source/warmboot_bootrom_workaround.hpp new file mode 100644 index 0000000..039c3df --- /dev/null +++ b/exosphere/warmboot/source/warmboot_bootrom_workaround.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::warmboot { + + void ApplyMbistWorkaround(); + +} \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_clkrst.cpp b/exosphere/warmboot/source/warmboot_clkrst.cpp new file mode 100644 index 0000000..258b3b3 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_clkrst.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 "warmboot_clkrst.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t TIMER = secmon::MemoryRegionPhysicalDeviceTimer.GetAddress(); + + } + + void ConfigureOscillators() { + /* Enable the crystal oscillator, and copy the drive strength from pmc. */ + const auto xofs = reg::GetValue(PMC + APBDEV_PMC_OSC_EDPD_OVER, PMC_REG_BITS_MASK(OSC_EDPD_OVER_XOFS)); + + reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_OSC_CTRL, CLK_RST_REG_BITS_ENUM (OSC_CTRL_XOE, ENABLE), + CLK_RST_REG_BITS_VALUE(OSC_CTRL_XOFS, xofs)); + + /* Configure CLK_M_DIVISOR to 2. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_SPARE_REG0, CLK_RST_REG_BITS_ENUM(SPARE_REG0_CLK_M_DIVISOR, CLK_M_DIVISOR2)); + reg::Read(CLKRST + CLK_RST_CONTROLLER_SPARE_REG0); + + /* Restore TIMERUS config to 19.2 MHz. */ + reg::Write(TIMER + TIMERUS_USEC_CFG, TIMER_REG_BITS_VALUE(USEC_CFG_USEC_DIVIDEND, 5 - 1), + TIMER_REG_BITS_VALUE(USEC_CFG_USEC_DIVISOR, 96 - 1)); + + } + +} diff --git a/exosphere/warmboot/source/warmboot_clkrst.hpp b/exosphere/warmboot/source/warmboot_clkrst.hpp new file mode 100644 index 0000000..327f825 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_clkrst.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::warmboot { + + void ConfigureOscillators(); + +} \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_cpu_cluster.cpp b/exosphere/warmboot/source/warmboot_cpu_cluster.cpp new file mode 100644 index 0000000..34983e0 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_cpu_cluster.cpp @@ -0,0 +1,219 @@ +/* + * 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 "warmboot_clkrst.hpp" +#include "warmboot_util.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress(); + constexpr inline const uintptr_t GPIO = secmon::MemoryRegionPhysicalDeviceGpio.GetAddress(); + constexpr inline const uintptr_t MSELECT = MSELECT(0); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionPhysicalDeviceSystem.GetAddress(); + + ALWAYS_INLINE void EnableClusterPartition(const reg::BitsValue value, APBDEV_PMC_PWRGATE_TOGGLE_PARTID part_id) { + /* Toggle the partitions if necessary. */ + if (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, value)) { + reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM (PWRGATE_TOGGLE_START, ENABLE), + PMC_REG_BITS_VALUE(PWRGATE_TOGGLE_PARTID, part_id)); + } + + /* Wait for the toggle to complete. */ + while (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, value)) { /* ... */ } + + /* Remove clamping. */ + reg::Write(PMC + APBDEV_PMC_REMOVE_CLAMPING_CMD, value); + + /* Wait for clamping to be removed. */ + while (reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, value)) { /* ... */ } + } + + } + + void InitializeCpuCluster() { + /* Set CoreSight reset. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_U_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SET_SET_CSITE_RST, ENABLE)); + + /* Restore PROD setting to CPU_SOFTRST_CTRL2 by clearing CAR2PMC_CPU_ACK_WIDTH. */ + reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2, CLK_RST_REG_BITS_VALUE(CPU_SOFTRST_CTRL2_CAR2PMC_CPU_ACK_WIDTH, 0)); + + /* Restore the CPU reset vector from PMC scratch. */ + reg::Write(SYSTEM + SB_AA64_RESET_LOW, reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH34) | 1); + reg::Write(SYSTEM + SB_AA64_RESET_HIGH, reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH35)); + + /* Configure SUPER_CCLKG_DIVIDER. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_SUPER_CCLKG_DIVIDER, CLK_RST_REG_BITS_ENUM (SUPER_CCLKG_DIVIDER_SUPER_CDIV_ENB, ENABLE), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_FIQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_FIQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_IRQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_IRQ, NO_IMPACT), + CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVIDEND, 0), + CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVISOR, 0)); + + /* Configure SUPER_CCLKLP_DIVIDER. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_SUPER_CCLKLP_DIVIDER, CLK_RST_REG_BITS_ENUM (SUPER_CCLKLP_DIVIDER_SUPER_CDIV_ENB, ENABLE), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_FIQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_FIQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_IRQ, NO_IMPACT), + CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_IRQ, NO_IMPACT), + CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVIDEND, 0), + CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVISOR, 0)); + + /* Enable CoreSight clock. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_U_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_SET_SET_CLK_ENB_CSITE, ENABLE)); + + /* Clear CoreSight reset. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_U_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_U_CLR_CLR_CSITE_RST, ENABLE)); + + /* Select MSELECT clock source as PLLP_OUT0 with divider of 4. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_MSELECT_MSELECT_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_MSELECT_MSELECT_CLK_DIVISOR, 6)); + + /* Enable clock to MSELECT. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_V_SET_SET_CLK_ENB_MSELECT, ENABLE)); + + /* Wait two microseconds, then take MSELECT out of reset. */ + util::WaitMicroSeconds(2); + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_V_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_V_CLR_CLR_MSELECT_RST, ENABLE)); + + /* Workaround bug by disabling MSELECT error mechanism and enabling WRAP type conversion. */ + reg::ReadWrite(MSELECT + MSELECT_CONFIG, MSELECT_REG_BITS_ENUM(CONFIG_ERR_RESP_EN_SLAVE1, DISABLE), + MSELECT_REG_BITS_ENUM(CONFIG_ERR_RESP_EN_SLAVE2, DISABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE0, ENABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE1, ENABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE2, ENABLE)); + + /* Disable PLLX. */ + reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_PLLX_BASE, CLK_RST_REG_BITS_ENUM(PLLX_BASE_PLLX_ENABLE, DISABLE)); + + /* Clear bit 0 of PMC Scratch 190. */ + reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH190, REG_BITS_VALUE(0, 1, 0)); + + /* Clear PMC_DPD_SAMPLE, and wait 10 us for clear to take effect. */ + reg::Write(PMC + APBDEV_PMC_DPD_SAMPLE, 0); + util::WaitMicroSeconds(10); + + /* Configure UART2_RTS low (GPIO controller 2 G). */ + reg::ReadWrite(GPIO + 0x108, REG_BITS_VALUE(2, 1, 1)); /* GPIO_CNF */ + reg::ReadWrite(GPIO + 0x118, REG_BITS_VALUE(2, 1, 1)); /* GPIO_OE */ + reg::ReadWrite(GPIO + 0x128, REG_BITS_VALUE(2, 1, 0)); /* GPIO_OUT */ + + /* Tristate CLDVFS PWN. */ + reg::Write(APB_MISC + PINMUX_AUX_DVFS_PWM, PINMUX_REG_BITS_ENUM(AUX_TRISTATE, TRISTATE), + PINMUX_REG_BITS_ENUM(AUX_DVFS_PWM_PM, CLDVFS)); + reg::Read(APB_MISC + PINMUX_AUX_DVFS_PWM); + + /* Restore PWR_I2C E_INPUT. */ + reg::Write(APB_MISC + PINMUX_AUX_PWR_I2C_SCL, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); + reg::Write(APB_MISC + PINMUX_AUX_PWR_I2C_SDA, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); + + /* Enable CLDVFS clock. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_SET_SET_CLK_ENB_DVFS, ENABLE)); + + /* Set CLDVFS clock source/divider. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DVFS_REF_DVFS_REF_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DVFS_REF_DVFS_REF_DIVISOR, 14)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DVFS_SOC_DVFS_SOC_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DVFS_SOC_DVFS_SOC_DIVISOR, 14)); + + /* Enable PWR_I2C controller (I2C5). */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_SET_SET_CLK_ENB_I2C5, ENABLE)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_H_SET_SET_I2C5_RST, ENABLE)); + util::WaitMicroSeconds(5); + + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_I2C5, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_I2C5_I2C5_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_I2C5_I2C5_CLK_DIVISOR, 4)); + + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_H_CLR_CLR_I2C5_RST, ENABLE)); + + /* Set the EN bit in pmic regulator. */ + pmic::SetEnBit(fuse::GetRegulator()); + + /* Wait 2ms. */ + util::WaitMicroSeconds(2'000); + + /* Enable power to the CRAIL partition. */ + EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_CRAIL, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CRAIL); + + /* Remove software clamp to CRAIL. */ + reg::Write(PMC + APBDEV_PMC_SET_SW_CLAMP, 0); + reg::Write(PMC + APBDEV_PMC_REMOVE_CLAMPING_CMD, PMC_REG_BITS_ENUM(REMOVE_CLAMPING_COMMAND_CRAIL, ENABLE)); + while (reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, PMC_REG_BITS_ENUM(CLAMP_STATUS_CRAIL, ENABLE))) { /* ... */ } + + /* Spinloop 8 times, to add a little delay. */ + SpinLoop(8); + + /* Disable PWR_I2C controller (I2C5). */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_H_SET_SET_I2C5_RST, ENABLE)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_CLR_CLR_CLK_ENB_I2C5, ENABLE)); + + /* Disable CLDVFS clock. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_CLR_CLR_CLK_ENB_DVFS, ENABLE)); + + /* Perform fast cluster RAM repair if needed. */ + if (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_BPMP_CLUSTER_CONTROL, FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, SLOW))) { + reg::Write(FLOW_CTLR + FLOW_CTLR_RAM_REPAIR, FLOW_REG_BITS_ENUM(RAM_REPAIR_REQ, ENABLE)); + + while (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_RAM_REPAIR, FLOW_REG_BITS_ENUM(RAM_REPAIR_STS, DONE))) { + /* ... */ + } + } + + /* Enable power to the non-cpu partition. */ + EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_C0NC, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_C0NC); + + /* Enable clock to PLLP_OUT_CPU. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_Y_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_Y_SET_SET_CLK_ENB_PLLP_OUT_CPU, ENABLE)); + util::WaitMicroSeconds(2); + + /* Enable clock to the cpu complex. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_SET_SET_CLK_ENB_CPU, ENABLE)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_V_SET_SET_CLK_ENB_CPUG, ENABLE)); + util::WaitMicroSeconds(10); + + /* Select cpu complex clock source. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CCLKG_BURST_POLICY, CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CPU_STATE, RUN)); + + reg::Write(CLKRST + CLK_RST_CONTROLLER_CCLKLP_BURST_POLICY, CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CPU_STATE, RUN)); + util::WaitMicroSeconds(10); + + /* Wake non-cpu out of reset. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, ENABLE)); + } + + void PowerOnCpu() { + /* Enable power to the CE0 partition. */ + EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_CE0, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0); + + /* Clear CPU reset. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET0, ENABLE), + CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET0, ENABLE)); + } + +} diff --git a/exosphere/warmboot/source/warmboot_cpu_cluster.hpp b/exosphere/warmboot/source/warmboot_cpu_cluster.hpp new file mode 100644 index 0000000..6b2e3c8 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_cpu_cluster.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::warmboot { + + void InitializeCpuCluster(); + void PowerOnCpu(); + +} \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_dram.cpp b/exosphere/warmboot/source/warmboot_dram.cpp new file mode 100644 index 0000000..1099964 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_dram.cpp @@ -0,0 +1,130 @@ +/* + * 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 "warmboot_dram.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t EMC = EMC_ADDRESS(0); + constexpr inline const uintptr_t EMC0 = EMC0_ADDRESS(0); + constexpr inline const uintptr_t EMC1 = EMC1_ADDRESS(0); + constexpr inline const uintptr_t MC = secmon::MemoryRegionPhysicalDeviceMemoryController.GetAddress(); + constexpr inline const uintptr_t MC0 = secmon::MemoryRegionPhysicalDeviceMemoryController0.GetAddress(); + constexpr inline const uintptr_t MC1 = secmon::MemoryRegionPhysicalDeviceMemoryController1.GetAddress(); + + } + + void RestrictBpmpAccessToMainMemory() { + /* Bpmp memory access is restricted by forcing internal access to an invalid carveout. */ + constexpr u32 ForceInternalAccess0 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS0_AVPCARM7R, ENABLE)); + constexpr u32 ForceInternalAccess1 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS1_AVPCARM7W, ENABLE)); + + 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, 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, UNTRANSLATED_ONLY), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, TZ_SECURE)); + + /* Specify a 128KB carveout at NULL with no clients allowed access, and bpmp forced to access. */ + reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM_HI, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_SIZE_128KB, 1); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0, ForceInternalAccess0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1, ForceInternalAccess1); + 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, CarveoutConfig); + } + + void RestoreRamSvop() { + reg::ReadWrite(APB_MISC + APB_MISC_GP_ASDBGREG, APB_MISC_REG_BITS_VALUE(GP_ASDBGREG_CFG2TMC_RAM_SVOP_PDP, 2)); + } + + void ConfigureEmcPmacroTraining() { + /* Disable writes to BYTE0-7. */ + reg::Write(EMC + EMC_PMACRO_CFG_PM_GLOBAL_0, EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3, DISABLE)); + + /* Set E_WRPTR on Channel 0. */ + reg::Write(EMC + EMC_PMACRO_TRAINING_CTRL_0, EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_ENABLED, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_TRAIN_QPOP, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_RX_E_DIRECT_ZI, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR, ENABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_DRV_DQS, DISABLED)); + + /* Set E_WRPTR on Channel 1. */ + reg::Write(EMC + EMC_PMACRO_TRAINING_CTRL_1, EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_ENABLED, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_TRAIN_QPOP, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_RX_E_DIRECT_ZI, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR, ENABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_DRV_DQS, DISABLED)); + + /* Re-enable writes to BYTE0-7. */ + reg::Write(EMC + EMC_PMACRO_CFG_PM_GLOBAL_0, EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3, DISABLE)); + } + +} diff --git a/exosphere/warmboot/source/warmboot_dram.hpp b/exosphere/warmboot/source/warmboot_dram.hpp new file mode 100644 index 0000000..0e8cbaa --- /dev/null +++ b/exosphere/warmboot/source/warmboot_dram.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::warmboot { + + void RestrictBpmpAccessToMainMemory(); + void RestoreRamSvop(); + void ConfigureEmcPmacroTraining(); + +} \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_exception_vectors.s b/exosphere/warmboot/source/warmboot_exception_vectors.s new file mode 100644 index 0000000..7bac9ac --- /dev/null +++ b/exosphere/warmboot/source/warmboot_exception_vectors.s @@ -0,0 +1,63 @@ +/* + * 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 warmboot_header +warmboot_header: + /* TODO: If Mariko warmboothax ever happens, generate a mariko header? */ + /* Warmboot header. */ + .word __total_size__ + .rept 3 + .word 0x00000000 + .endr + + /* RSA modulus. */ + .rept 0x40 + .word 0xFFFFFFFF + .endr + + /* Padding */ + .rept 4 + .word 0x00000000 + .endr + + /* RSA signature */ + .rept 0x40 + .word 0xFFFFFFFF + .endr + + /* Padding */ + .rept 4 + .word 0x00000000 + .endr + + /* Firmware metadata. */ + .word __total_size__ + .word _reset + .word _reset + .word __executable_size__ + +.global _reset +_reset: + b _ZN3ams8warmboot5StartEv + +.global _metadata +_metadata: + .ascii "WBT0" /* Magic number */ + .word 0x00000000 /* Target firmware. */ + .word 0x00000000 /* Reserved */ + .word 0x00000000 /* Reserved */ \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_main.cpp b/exosphere/warmboot/source/warmboot_main.cpp new file mode 100644 index 0000000..933b17e --- /dev/null +++ b/exosphere/warmboot/source/warmboot_main.cpp @@ -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 . + */ +#include +#include "warmboot_bootrom_workaround.hpp" +#include "warmboot_clkrst.hpp" +#include "warmboot_cpu_cluster.hpp" +#include "warmboot_dram.hpp" +#include "warmboot_main.hpp" +#include "warmboot_misc.hpp" +#include "warmboot_secure_monitor.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress(); + + constexpr inline const uintptr_t ExpectedMetadataAddress = 0x40010244; + + } + + void Main(const Metadata *metadata) { + /* Ensure that we're running under vaguely sane conditions. */ + AMS_ABORT_UNLESS(metadata->magic == Metadata::Magic); + AMS_ABORT_UNLESS(metadata->target_firmware <= ams::TargetFirmware_Max); + + /* Restrict the bpmp's access to dram. */ + if (metadata->target_firmware >= TargetFirmware_4_0_0) { + RestrictBpmpAccessToMainMemory(); + } + + /* Configure rtck-daisychaining/jtag. */ + ConfigureMiscSystemDebug(); + + /* NOTE: Here, Nintendo checks that the number of burnt anti-downgrade fuses is valid. */ + + /* NOTE: Here, Nintendo validates that APBDEV_PMC_SECURE_SCRATCH32 contains the correct magic number for the current warmboot firmware revision. */ + + /* Validate that we're executing at the correct address. */ + AMS_ABORT_UNLESS(reinterpret_cast(metadata) == ExpectedMetadataAddress); + + /* Validate that we're executing on the bpmp. */ + AMS_ABORT_UNLESS(reg::Read(PG_UP(PG_UP_TAG)) == PG_UP_TAG_PID_COP); + + /* Configure fuse bypass. */ + fuse::ConfigureFuseBypass(); + + /* Configure system oscillators. */ + ConfigureOscillators(); + + /* Restore DRAM configuration. */ + RestoreRamSvop(); + ConfigureEmcPmacroTraining(); + + /* If on Erista, work around the bootrom mbist issue. */ + if (fuse::GetSocType() == fuse::SocType_Erista) { + ApplyMbistWorkaround(); + } + + /* Initialize the cpu cluster. */ + InitializeCpuCluster(); + + /* Restore the secure monitor to tzram. */ + RestoreSecureMonitorToTzram(metadata->target_firmware); + + /* Power on the cpu. */ + PowerOnCpu(); + + /* Halt ourselves. */ + const bool disable_jtag = metadata->target_firmware >= TargetFirmware_4_0_0; + 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_SEL(HALT_COP_EVENTS_JTAG, disable_jtag, DISABLED, ENABLED)); + } + } + + 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() { + warmboot::ExceptionHandler(); + } + +} diff --git a/exosphere/warmboot/source/warmboot_main.hpp b/exosphere/warmboot/source/warmboot_main.hpp new file mode 100644 index 0000000..f71a89b --- /dev/null +++ b/exosphere/warmboot/source/warmboot_main.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::warmboot { + + struct Metadata { + static constexpr u32 Magic = util::FourCC<'W','B','T','0'>::Code; + + u32 magic; + ams::TargetFirmware target_firmware; + u32 reserved[2]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(Metadata) == 0x10); + +} diff --git a/exosphere/warmboot/source/warmboot_misc.cpp b/exosphere/warmboot/source/warmboot_misc.cpp new file mode 100644 index 0000000..eeb5932 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_misc.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "warmboot_misc.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionPhysicalDeviceSystem.GetAddress(); + + } + + void ConfigureMiscSystemDebug() { + /* Enable RTCK daisy-chaining. */ + reg::Write(APB_MISC + APB_MISC_PP_CONFIG_CTL, APB_MISC_REG_BITS_ENUM(PP_CONFIG_CTL_TBE, ENABLE)); + + /* If we're in production mode, perform JTAG configuration. */ + /* NOTE: While this is what NVidia's code does, this is almost certainly a logic error. */ + /* They intend to configure JTAG only when *not* in production mode. */ + /* However, here we will do what they do, and not what they intend. */ + const bool production_mode = fuse::IsOdmProductionMode(); + if (production_mode) { + const bool jtag_sts = reg::HasValue(PMC + APBDEV_PMC_STICKY_BITS, PMC_REG_BITS_ENUM(STICKY_BITS_JTAG_STS, ENABLE)); + + reg::ReadWrite(SYSTEM + SB_PFCFG, SB_REG_BITS_ENUM_SEL(PFCFG_DBGEN, jtag_sts, ENABLE, DISABLE), + SB_REG_BITS_ENUM_SEL(PFCFG_NIDEN, jtag_sts, ENABLE, DISABLE), + SB_REG_BITS_ENUM_SEL(PFCFG_SPNIDEN, jtag_sts, ENABLE, DISABLE), + SB_REG_BITS_ENUM (PFCFG_SPIDEN, DISABLE)); + + reg::ReadWrite(APB_MISC + APB_MISC_PP_CONFIG_CTL, APB_MISC_REG_BITS_ENUM_SEL(PP_CONFIG_CTL_JTAG, jtag_sts, ENABLE, DISABLE)); + } + + /* Configure HDA codec disable. */ + reg::ReadWrite(PMC + APBDEV_PMC_STICKY_BITS, PMC_REG_BITS_ENUM_SEL(STICKY_BITS_HDA_LPBK_DIS, production_mode, ENABLE, DISABLE)); + + /* Set E_INPUT in PINMUX_AUX_GPIO_PA6 (needed by the XUSB and SATA controllers). */ + reg::ReadWrite(APB_MISC + PINMUX_AUX_GPIO_PA6, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); + } + +} diff --git a/exosphere/warmboot/source/warmboot_misc.hpp b/exosphere/warmboot/source/warmboot_misc.hpp new file mode 100644 index 0000000..4cfeeab --- /dev/null +++ b/exosphere/warmboot/source/warmboot_misc.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::warmboot { + + void ConfigureMiscSystemDebug(); + +} \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_secure_monitor.cpp b/exosphere/warmboot/source/warmboot_secure_monitor.cpp new file mode 100644 index 0000000..40b7b84 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_secure_monitor.cpp @@ -0,0 +1,130 @@ +/* + * 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 "warmboot_secure_monitor.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + + constexpr inline const pkg1::AesKeySlot SavedAesKeySlots[] = { + pkg1::AesKeySlot_TzramSaveKek, + pkg1::AesKeySlot_RandomForUserWrap, + pkg1::AesKeySlot_RandomForKeyStorageWrap, + pkg1::AesKeySlot_DeviceMaster, + pkg1::AesKeySlot_Master, + pkg1::AesKeySlot_Device, + }; + + constexpr ALWAYS_INLINE bool IsSavedAesKeySlot(int slot) { + for (const auto SavedSlot : SavedAesKeySlots) { + if (slot == SavedSlot) { + return true; + } + } + + return false; + } + + void ClearUnsavedSecurityEngineKeySlots() { + /* Clear unsaved aes keys and all ivs. */ + for (int slot = 0; slot < se::AesKeySlotCount; ++slot) { + if (!IsSavedAesKeySlot(slot)) { + se::ClearAesKeySlot(slot); + } + se::ClearAesKeyIv(slot); + } + + /* Clear all rsa keys. */ + for (int slot = 0; slot < se::RsaKeySlotCount; ++slot) { + se::ClearRsaKeySlot(slot); + } + } + + void RestoreEncryptedTzram(void * const tzram_dst, const void * const tzram_src, size_t tzram_size) { + /* Derive the save key from the save kek. */ + const u32 key_source[se::AesBlockSize / sizeof(u32)] = { reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH24), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH25), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH26), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH27)}; + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); + se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, sizeof(key_source)); + + /* Decrypt tzram. */ + const u8 tzram_iv[se::AesBlockSize] = {}; + se::DecryptAes256Cbc(tzram_dst, tzram_size, pkg1::AesKeySlot_TzramSaveKey, tzram_src, tzram_size, tzram_iv, sizeof(tzram_iv)); + + /* Calculate the cmac of decrypted tzram. */ + u8 tzram_mac[se::AesBlockSize] = {}; + se::ComputeAes256Cmac(tzram_mac, sizeof(tzram_mac), pkg1::AesKeySlot_TzramSaveKey, tzram_dst, tzram_size); + + /* Get the expected mac from pmc scratch. */ + const u32 expected_mac[sizeof(tzram_mac) / sizeof(u32)] = { reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH112), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH113), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH114), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH115)}; + + /* Validate that the calculated mac is correct. */ + AMS_ABORT_UNLESS(crypto::IsSameBytes(tzram_mac, expected_mac, sizeof(tzram_mac))); + } + + void RestoreSecureMonitorToTzramErista(const TargetFirmware target_fw) { + /* Clear all unsaved security engine keyslots. */ + ClearUnsavedSecurityEngineKeySlots(); + + /* Restore encrypted tzram contents. */ + void * const tzram_src = secmon::MemoryRegionPhysicalDramSecureDataStoreTzram.GetPointer(); + void * const tzram_dst = secmon::MemoryRegionPhysicalTzramNonVolatile.GetPointer(); + const size_t tzram_size = secmon::MemoryRegionPhysicalTzramNonVolatile.GetSize(); + RestoreEncryptedTzram(tzram_dst, tzram_src, tzram_size); + + /* Clear the tzram kek registers. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH24, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH25, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH26, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH27, 0); + + /* Clear the tzram cmac registers. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH112, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH113, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH114, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH115, 0); + + /* Clear the keydata used to protect tzram. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKek); + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); + + /* Clear the encrypted copy of tzram in dram. */ + /* NOTE: This does not actually clear the encrypted copy, because BPMP access to main memory has been restricted. */ + /* Nintendo seems to not realize this, though, so we'll do the same. */ + std::memset(tzram_src, 0, tzram_size); + + /* Set Tzram to secure-world only. */ + se::SetTzramSecure(); + } + + } + + void RestoreSecureMonitorToTzram(const TargetFirmware target_fw) { + /* If erista, perform restoration procedure. */ + if (fuse::GetSocType() == fuse::SocType_Erista) { + RestoreSecureMonitorToTzramErista(target_fw); + } + + /* Lock secure scratch. */ + pmc::LockSecureRegister(static_cast(pmc::SecureRegister_DramParameters | pmc::SecureRegister_Other)); + + /* Lockout fuses. */ + fuse::Lockout(); + } + +} diff --git a/exosphere/warmboot/source/warmboot_secure_monitor.hpp b/exosphere/warmboot/source/warmboot_secure_monitor.hpp new file mode 100644 index 0000000..499d125 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_secure_monitor.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::warmboot { + + void RestoreSecureMonitorToTzram(const TargetFirmware target_fw); + +} \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_start.s b/exosphere/warmboot/source/warmboot_start.s new file mode 100644 index 0000000..18070e0 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_start.s @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.section .text._ZN3ams8warmboot5StartEv, "ax", %progbits +.align 3 +.global _ZN3ams8warmboot5StartEv +_ZN3ams8warmboot5StartEv: + /* Set CPSR_cf and CPSR_cf. */ + msr cpsr_f, #0xC0 + msr cpsr_cf, #0xD3 + + /* Set the stack pointer. */ + ldr sp, =__stack_top__ + + /* Set our link register to the exception handler. */ + ldr lr, =_ZN3ams8warmboot16ExceptionHandlerEv + + /* Invoke main. */ + ldr r0, =_metadata + bl _ZN3ams8warmboot4MainEPKNS0_8MetadataE + + /* Infinite loop. */ + 1: b 1b \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_util.hpp b/exosphere/warmboot/source/warmboot_util.hpp new file mode 100644 index 0000000..5ca4402 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_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::warmboot { + + void SpinLoop(unsigned int num); + +} \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_util_asm.s b/exosphere/warmboot/source/warmboot_util_asm.s new file mode 100644 index 0000000..c23d7f3 --- /dev/null +++ b/exosphere/warmboot/source/warmboot_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._ZN3ams8warmboot8SpinLoopEj, "ax", %progbits +.global _ZN3ams8warmboot8SpinLoopEj +.thumb_func +.syntax unified +_ZN3ams8warmboot8SpinLoopEj: + 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/warmboot/warmboot.ld b/exosphere/warmboot/warmboot.ld new file mode 100644 index 0000000..0e3d448 --- /dev/null +++ b/exosphere/warmboot/warmboot.ld @@ -0,0 +1,189 @@ +OUTPUT_ARCH(arm) +ENTRY(_reset) + +MEMORY +{ + NULL : ORIGIN = 0, LENGTH = 4K + lp0fw : ORIGIN = 0x40010000, LENGTH = 16K +} + + +SECTIONS +{ + /* =========== CODE section =========== */ + PROVIDE(__start__ = ORIGIN(lp0fw)); + . = __start__; + __code_start = . ; + + .vectors : + { + KEEP (*(.vectors .vectors.*)) + . = ALIGN(8); + } >lp0fw + + .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); + } >lp0fw + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } >lp0fw + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } >lp0fw + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } >lp0fw + + + /* =========== RODATA section =========== */ + . = ALIGN(8); + __rodata_start = . ; + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } >lp0fw + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >lp0fw + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >lp0fw + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >lp0fw + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >lp0fw + + .hash : { *(.hash) } >lp0fw + + /* =========== DATA section =========== */ + . = ALIGN(8); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >lp0fw + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >lp0fw + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >lp0fw + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >lp0fw + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } >lp0fw + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } >lp0fw + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } >lp0fw + + .ctors ALIGN(8) : + { + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >lp0fw + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >lp0fw + + __got_start__ = .; + + .got : { *(.got) *(.igot) } >lp0fw + .got.plt : { *(.got.plt) *(.igot.plt) } >lp0fw + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >lp0fw + + __bss_start__ = .; + .bss ALIGN(8) : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + } >lp0fw + __bss_end__ = .; + + __end__ = ABSOLUTE(.) ; + + __total_size__ = (__end__ - __start__); + __executable_size__ = (__end__ - _reset); + + __stack_top__ = 0x40013000; + __stack_bottom__ = 0x40012000; + + /* ================== + ==== 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/warmboot/warmboot.specs b/exosphere/warmboot/warmboot.specs new file mode 100644 index 0000000..45d0056 --- /dev/null +++ b/exosphere/warmboot/warmboot.specs @@ -0,0 +1,7 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /warmboot.ld) --gc-sections --nmagic -nostdlib -nostartfiles + +*startfile: +crti%O%s crtbegin%O%s diff --git a/exosphere2/Makefile b/exosphere2/Makefile deleted file mode 100644 index 8017acd..0000000 --- a/exosphere2/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -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/exosphere2/loader_stub/Makefile b/exosphere2/loader_stub/Makefile deleted file mode 100644 index a5725e5..0000000 --- a/exosphere2/loader_stub/Makefile +++ /dev/null @@ -1,130 +0,0 @@ -#--------------------------------------------------------------------------------- -# Define the atmosphere board and cpu -#--------------------------------------------------------------------------------- -export ATMOSPHERE_BOARD := nx-hac-001 -export ATMOSPHERE_CPU := arm-cortex-a57 - -#--------------------------------------------------------------------------------- -# pull in common atmosphere configuration -#--------------------------------------------------------------------------------- -include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/exosphere.mk - -#--------------------------------------------------------------------------------- -# no real need to edit anything past this point unless you need to add additional -# rules for different file extensions -#--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) -#--------------------------------------------------------------------------------- - -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) -export DEPSDIR := $(CURDIR)/$(BUILD) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(TOPDIR)/../program - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) - -BINFILES := program.lz4 boot_code.lz4 - -#--------------------------------------------------------------------------------- -# use CXX for linking C++ projects, CC for standard C -#--------------------------------------------------------------------------------- -ifeq ($(strip $(CPPFILES)),) -#--------------------------------------------------------------------------------- - export LD := $(CC) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- - export LD := $(CXX) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -export OFILES_BIN := $(addsuffix .o,$(BINFILES)) -export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) -export OFILES := $(OFILES_BIN) $(OFILES_SRC) -export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES)))) - -export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ - $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ - -I. - -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) - -.PHONY: $(BUILD) clean all check_program - -#--------------------------------------------------------------------------------- -all: $(BUILD) check_program - -$(BUILD): check_program - @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -check_program: - @$(MAKE) -C ../program all - -#--------------------------------------------------------------------------------- -clean: - @echo clean ... - @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf - -#--------------------------------------------------------------------------------- -else -.PHONY: all - -DEPENDS := $(OFILES:.o=.d) - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -all : $(OUTPUT).bin - -$(OUTPUT).bin : $(OUTPUT).elf - $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@ - @echo built ... $(notdir $@) - -$(OUTPUT).elf : $(OFILES) ../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a - -%.elf: - @echo linking $(notdir $@) - $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ - @$(NM) -CSn $@ > $(notdir $*.lst) - -$(OFILES_SRC) : $(HFILES_BIN) - -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.bin.o %_bin.h: %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - -%.lz4.o %_lz4.h: %.lz4 -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - --include $(DEPENDS) - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/exosphere2/loader_stub/loader_stub.ld b/exosphere2/loader_stub/loader_stub.ld deleted file mode 100644 index bb8239d..0000000 --- a/exosphere2/loader_stub/loader_stub.ld +++ /dev/null @@ -1,182 +0,0 @@ -OUTPUT_ARCH(aarch64) -ENTRY(_start) - -MEMORY -{ - NULL : ORIGIN = 0, LENGTH = 4K - ldr_stub : ORIGIN = 0x040030000, LENGTH = 128K -} - -SECTIONS -{ - /* =========== CODE section =========== */ - PROVIDE(__start__ = 0x040030000); - . = __start__; - __code_start = . ; - - .crt0 : - { - KEEP (*(.crt0 .crt0.*)) - . = ALIGN(8); - } >ldr_stub - - .text : - { - *(.text.unlikely .text.*_unlikely .text.unlikely.*) - *(.text.exit .text.exit.*) - *(.text.startup .text.startup.*) - *(.text.hot .text.hot.*) - *(.text .stub .text.* .gnu.linkonce.t.*) - . = ALIGN(8); - } >ldr_stub - - .init : - { - KEEP( *(.init) ) - . = ALIGN(8); - } >ldr_stub - - .plt : - { - *(.plt) - *(.iplt) - . = ALIGN(8); - } >ldr_stub - - .fini : - { - KEEP( *(.fini) ) - . = ALIGN(8); - } >ldr_stub - - - /* =========== RODATA section =========== */ - . = ALIGN(8); - __rodata_start = . ; - - .rodata : - { - *(.rodata .rodata.* .gnu.linkonce.r.*) - . = ALIGN(8); - } >ldr_stub - - .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >ldr_stub - .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ldr_stub - .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >ldr_stub - .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >ldr_stub - - .hash : { *(.hash) } >ldr_stub - - /* =========== DATA section =========== */ - . = ALIGN(8); - __data_start = . ; - - .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ldr_stub - .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >ldr_stub - .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >ldr_stub - .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >ldr_stub - - .preinit_array ALIGN(8) : - { - PROVIDE (__preinit_array_start = .); - KEEP (*(.preinit_array)) - PROVIDE (__preinit_array_end = .); - } >ldr_stub - - .init_array ALIGN(8) : - { - PROVIDE (__init_array_start = .); - KEEP (*(SORT(.init_array.*))) - KEEP (*(.init_array)) - PROVIDE (__init_array_end = .); - } >ldr_stub - - .fini_array ALIGN(8) : - { - PROVIDE (__fini_array_start = .); - KEEP (*(.fini_array)) - KEEP (*(SORT(.fini_array.*))) - PROVIDE (__fini_array_end = .); - } >ldr_stub - - .ctors ALIGN(8) : - { - KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ - KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) - } >ldr_stub - - .dtors ALIGN(8) : - { - KEEP (*crtbegin.o(.dtors)) - KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) - } >ldr_stub - - __got_start__ = .; - - .got : { *(.got) *(.igot) } >ldr_stub - .got.plt : { *(.got.plt) *(.igot.plt) } >ldr_stub - - __got_end__ = .; - - .data ALIGN(8) : - { - *(.data .data.* .gnu.linkonce.d.*) - SORT(CONSTRUCTORS) - } >ldr_stub - - __bss_start__ = .; - .bss ALIGN(8) : - { - *(.dynbss) - *(.bss .bss.* .gnu.linkonce.b.*) - *(COMMON) - . = ALIGN(16); - } >ldr_stub - __bss_end__ = .; - - __end__ = ABSOLUTE(.) ; - - /* ================== - ==== Metadata ==== - ================== */ - - /* Discard sections that difficult post-processing */ - /DISCARD/ : { *(.group .comment .note .interp) } - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section so we begin them at 0. */ - - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } -} \ No newline at end of file diff --git a/exosphere2/loader_stub/loader_stub.specs b/exosphere2/loader_stub/loader_stub.specs deleted file mode 100644 index 0398476..0000000 --- a/exosphere2/loader_stub/loader_stub.specs +++ /dev/null @@ -1,7 +0,0 @@ -%rename link old_link - -*link: -%(old_link) -T %:getenv(TOPDIR /loader_stub.ld) --gc-sections --nmagic -nostdlib -nostartfiles - -*startfile: -crti%O%s crtbegin%O%s diff --git a/exosphere2/loader_stub/source/secmon_loader_error.cpp b/exosphere2/loader_stub/source/secmon_loader_error.cpp deleted file mode 100644 index 2f67b50..0000000 --- a/exosphere2/loader_stub/source/secmon_loader_error.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "secmon_loader_error.hpp" - -namespace ams::diag { - - NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) { - ams::secmon::loader::ErrorReboot(); - } - - NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) { - ams::secmon::loader::ErrorReboot(); - } - - NORETURN void AbortImpl() { - ams::secmon::loader::ErrorReboot(); - } - -} - -namespace ams::secmon::loader { - - NORETURN void ErrorReboot() { - /* Invalidate the security engine. */ - /* TODO */ - - /* Reboot. */ - while (true) { - wdt::Reboot(); - } - } - -} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_error.hpp b/exosphere2/loader_stub/source/secmon_loader_error.hpp deleted file mode 100644 index 6119907..0000000 --- a/exosphere2/loader_stub/source/secmon_loader_error.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#pragma once - -namespace ams::secmon::loader { - - NORETURN void ErrorReboot(); - -} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_main.cpp b/exosphere2/loader_stub/source/secmon_loader_main.cpp deleted file mode 100644 index 26b1944..0000000 --- a/exosphere2/loader_stub/source/secmon_loader_main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "secmon_loader_uncompress.hpp" -#include "program_lz4.h" -#include "boot_code_lz4.h" - -namespace ams::secmon::loader { - - NORETURN void UncompressAndExecute() { - /* Uncompress the program image. */ - Uncompress(secmon::MemoryRegionPhysicalTzramFullProgramImage.GetPointer(), secmon::MemoryRegionPhysicalTzramFullProgramImage.GetSize(), program_lz4, program_lz4_size); - - - /* Copy the boot image to the end of IRAM */ - u8 *relocated_boot_code = secmon::MemoryRegionPhysicalIramBootCodeImage.GetEndPointer() - boot_code_lz4_size; - std::memcpy(relocated_boot_code, boot_code_lz4, boot_code_lz4_size); - - /* Uncompress the boot image. */ - Uncompress(secmon::MemoryRegionPhysicalIramBootCodeImage.GetPointer(), secmon::MemoryRegionPhysicalIramBootCodeImage.GetSize(), relocated_boot_code, boot_code_lz4_size); - - - /* Jump to the boot image. */ - reinterpret_cast(secmon::MemoryRegionPhysicalIramBootCodeImage.GetAddress())(); - - /* We will never reach this point. */ - __builtin_unreachable(); - } - -} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp b/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp deleted file mode 100644 index a58e3c5..0000000 --- a/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "secmon_loader_uncompress.hpp" - -namespace ams::secmon::loader { - - namespace { - - class Lz4Uncompressor { - private: - const u8 *src; - size_t src_size; - size_t src_offset; - u8 *dst; - size_t dst_size; - size_t dst_offset; - public: - Lz4Uncompressor(void *dst, size_t dst_size, const void *src, size_t src_size) : src(static_cast(src)), src_size(src_size), src_offset(0), dst(static_cast(dst)), dst_size(dst_size), dst_offset(0) { - /* ... */ - } - - void Uncompress() { - while (true) { - /* Read a control byte. */ - u8 control = this->ReadByte(); - - /* Copy what it specifies we should copy. */ - this->Copy(this->GetCopySize(control >> 4)); - - /* If we've exceeded size, we're done. */ - if (this->src_offset >= this->src_size) { - break; - } - - /* Read the wide copy offset. */ - u16 wide_offset = this->ReadByte(); - AMS_ABORT_UNLESS(this->CanRead()); - wide_offset |= (this->ReadByte() << 8); - - /* Determine the copy size. */ - const size_t wide_copy_size = this->GetCopySize(control & 0xF); - - /* Copy bytes. */ - const size_t end_offset = this->dst_offset + wide_copy_size + 4; - for (size_t cur_offset = this->dst_offset; cur_offset < end_offset; this->dst_offset = (++cur_offset)) { - AMS_ABORT_UNLESS(wide_offset <= cur_offset); - - this->dst[cur_offset] = this->dst[cur_offset - wide_offset]; - } - } - } - private: - u8 ReadByte() { - return this->src[this->src_offset++]; - } - - bool CanRead() const { - return this->src_offset < this->src_size; - } - - size_t GetCopySize(u8 control) { - size_t size = control; - - if (control >= 0xF) { - do { - AMS_ABORT_UNLESS(this->CanRead()); - control = this->ReadByte(); - size += control; - } while (control == 0xFF); - } - - return size; - } - - void Copy(size_t size) { - __builtin_memcpy(this->dst + this->dst_offset, this->src + this->src_offset, size); - this->dst_offset += size; - this->src_offset += size; - } - }; - - } - - void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size) { - /* Create an execute a decompressor. */ - Lz4Uncompressor(dst, dst_size, src, src_size).Uncompress(); - } - -} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp b/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp deleted file mode 100644 index b820209..0000000 --- a/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#pragma once - -namespace ams::secmon::loader { - - void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size); - -} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/start.s b/exosphere2/loader_stub/source/start.s deleted file mode 100644 index 7476605..0000000 --- a/exosphere2/loader_stub/source/start.s +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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/exosphere2/program/Makefile b/exosphere2/program/Makefile deleted file mode 100644 index 23b18af..0000000 --- a/exosphere2/program/Makefile +++ /dev/null @@ -1,138 +0,0 @@ -#--------------------------------------------------------------------------------- -# 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/exosphere2/program/program.ld b/exosphere2/program/program.ld deleted file mode 100644 index 8ca3998..0000000 --- a/exosphere2/program/program.ld +++ /dev/null @@ -1,285 +0,0 @@ -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/exosphere2/program/program.specs b/exosphere2/program/program.specs deleted file mode 100644 index 22b7899..0000000 --- a/exosphere2/program/program.specs +++ /dev/null @@ -1,4 +0,0 @@ -%rename link old_link - -*link: -%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic -nostdlib -nostartfiles \ No newline at end of file diff --git a/exosphere2/program/rebootstub/Makefile b/exosphere2/program/rebootstub/Makefile deleted file mode 100644 index 31439e8..0000000 --- a/exosphere2/program/rebootstub/Makefile +++ /dev/null @@ -1,122 +0,0 @@ -#--------------------------------------------------------------------------------- -# Define the atmosphere board and cpu -#--------------------------------------------------------------------------------- -export ATMOSPHERE_BOARD := nx-hac-001 -export ATMOSPHERE_CPU := arm7tdmi - -#--------------------------------------------------------------------------------- -# pull in common atmosphere configuration -#--------------------------------------------------------------------------------- -include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../../libraries/config/templates/exosphere.mk - -#--------------------------------------------------------------------------------- -# no real need to edit anything past this point unless you need to add additional -# rules for different file extensions -#--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) -#--------------------------------------------------------------------------------- - -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) -export DEPSDIR := $(CURDIR)/$(BUILD) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) - -#--------------------------------------------------------------------------------- -# use CXX for linking C++ projects, CC for standard C -#--------------------------------------------------------------------------------- -ifeq ($(strip $(CPPFILES)),) -#--------------------------------------------------------------------------------- - export LD := $(CC) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- - export LD := $(CXX) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -export OFILES_BIN := $(addsuffix .o,$(BINFILES)) -export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) -export OFILES := $(OFILES_BIN) $(OFILES_SRC) -export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES)))) - -export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ - $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ - -I. - -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) - -.PHONY: $(BUILD) clean all - -#--------------------------------------------------------------------------------- -all: $(BUILD) check_libexo - -$(BUILD): check_libexo - @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -check_libexo: - @$(MAKE) --no-print-directory -C ../../../libraries/libexosphere arm - -#--------------------------------------------------------------------------------- -clean: - @echo clean ... - @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 - -#--------------------------------------------------------------------------------- -else -.PHONY: all - -DEPENDS := $(OFILES:.o=.d) - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -all : $(OUTPUT).bin - -$(OUTPUT).bin : $(OUTPUT).elf - $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@ - @echo built ... $(notdir $@) - -$(OUTPUT).elf : $(OFILES) ../../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a - -%.elf: - @echo linking $(notdir $@) - $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ - @$(NM) -CSn $@ > $(notdir $*.lst) - -$(OFILES_SRC) : $(HFILES_BIN) - -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.bin.o %_bin.h: %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - --include $(DEPENDS) - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/exosphere2/program/rebootstub/rebootstub.ld b/exosphere2/program/rebootstub/rebootstub.ld deleted file mode 100644 index c37c755..0000000 --- a/exosphere2/program/rebootstub/rebootstub.ld +++ /dev/null @@ -1,183 +0,0 @@ -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/exosphere2/program/rebootstub/rebootstub.specs b/exosphere2/program/rebootstub/rebootstub.specs deleted file mode 100644 index 4e41b16..0000000 --- a/exosphere2/program/rebootstub/rebootstub.specs +++ /dev/null @@ -1,7 +0,0 @@ -%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/exosphere2/program/rebootstub/source/rebootstub_exception_vectors.s b/exosphere2/program/rebootstub/source/rebootstub_exception_vectors.s deleted file mode 100644 index eaf62f1..0000000 --- a/exosphere2/program/rebootstub/source/rebootstub_exception_vectors.s +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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/exosphere2/program/rebootstub/source/rebootstub_main.s b/exosphere2/program/rebootstub/source/rebootstub_main.s deleted file mode 100644 index 03e3ec6..0000000 --- a/exosphere2/program/rebootstub/source/rebootstub_main.s +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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/exosphere2/program/rebootstub/source/rebootstub_power_off.cpp b/exosphere2/program/rebootstub/source/rebootstub_power_off.cpp deleted file mode 100644 index 97d0e21..0000000 --- a/exosphere2/program/rebootstub/source/rebootstub_power_off.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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/exosphere2/program/sc7fw/Makefile b/exosphere2/program/sc7fw/Makefile deleted file mode 100644 index 31439e8..0000000 --- a/exosphere2/program/sc7fw/Makefile +++ /dev/null @@ -1,122 +0,0 @@ -#--------------------------------------------------------------------------------- -# Define the atmosphere board and cpu -#--------------------------------------------------------------------------------- -export ATMOSPHERE_BOARD := nx-hac-001 -export ATMOSPHERE_CPU := arm7tdmi - -#--------------------------------------------------------------------------------- -# pull in common atmosphere configuration -#--------------------------------------------------------------------------------- -include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../../libraries/config/templates/exosphere.mk - -#--------------------------------------------------------------------------------- -# no real need to edit anything past this point unless you need to add additional -# rules for different file extensions -#--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) -#--------------------------------------------------------------------------------- - -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) -export DEPSDIR := $(CURDIR)/$(BUILD) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) - -#--------------------------------------------------------------------------------- -# use CXX for linking C++ projects, CC for standard C -#--------------------------------------------------------------------------------- -ifeq ($(strip $(CPPFILES)),) -#--------------------------------------------------------------------------------- - export LD := $(CC) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- - export LD := $(CXX) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -export OFILES_BIN := $(addsuffix .o,$(BINFILES)) -export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) -export OFILES := $(OFILES_BIN) $(OFILES_SRC) -export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES)))) - -export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ - $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ - -I. - -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) - -.PHONY: $(BUILD) clean all - -#--------------------------------------------------------------------------------- -all: $(BUILD) check_libexo - -$(BUILD): check_libexo - @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -check_libexo: - @$(MAKE) --no-print-directory -C ../../../libraries/libexosphere arm - -#--------------------------------------------------------------------------------- -clean: - @echo clean ... - @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 - -#--------------------------------------------------------------------------------- -else -.PHONY: all - -DEPENDS := $(OFILES:.o=.d) - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -all : $(OUTPUT).bin - -$(OUTPUT).bin : $(OUTPUT).elf - $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@ - @echo built ... $(notdir $@) - -$(OUTPUT).elf : $(OFILES) ../../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a - -%.elf: - @echo linking $(notdir $@) - $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ - @$(NM) -CSn $@ > $(notdir $*.lst) - -$(OFILES_SRC) : $(HFILES_BIN) - -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.bin.o %_bin.h: %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - --include $(DEPENDS) - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/exosphere2/program/sc7fw/sc7fw.ld b/exosphere2/program/sc7fw/sc7fw.ld deleted file mode 100644 index 794edc3..0000000 --- a/exosphere2/program/sc7fw/sc7fw.ld +++ /dev/null @@ -1,183 +0,0 @@ -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/exosphere2/program/sc7fw/sc7fw.specs b/exosphere2/program/sc7fw/sc7fw.specs deleted file mode 100644 index 13455dc..0000000 --- a/exosphere2/program/sc7fw/sc7fw.specs +++ /dev/null @@ -1,7 +0,0 @@ -%rename link old_link - -*link: -%(old_link) -T %:getenv(TOPDIR /sc7fw.ld) --gc-sections --nmagic -nostdlib -nostartfiles - -*startfile: -crti%O%s crtbegin%O%s diff --git a/exosphere2/program/sc7fw/source/sc7fw_dram.cpp b/exosphere2/program/sc7fw/source/sc7fw_dram.cpp deleted file mode 100644 index b477a7d..0000000 --- a/exosphere2/program/sc7fw/source/sc7fw_dram.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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/exosphere2/program/sc7fw/source/sc7fw_dram.hpp b/exosphere2/program/sc7fw/source/sc7fw_dram.hpp deleted file mode 100644 index dca0a47..0000000 --- a/exosphere2/program/sc7fw/source/sc7fw_dram.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include - -namespace ams::sc7fw { - - void SaveEmcFsp(); - void EnableSdramSelfRefresh(); - void EnableEmcAllSegmentsRefresh(); - void EnableDdrDeepPowerDown(); - -} \ No newline at end of file diff --git a/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s b/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s deleted file mode 100644 index ca8c3b7..0000000 --- a/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -.section .vectors, "ax", %progbits -.align 3 -.global reset -reset: - b _start - b _ZN3ams5sc7fw16ExceptionHandlerEv diff --git a/exosphere2/program/sc7fw/source/sc7fw_main.cpp b/exosphere2/program/sc7fw/source/sc7fw_main.cpp deleted file mode 100644 index 0a08258..0000000 --- a/exosphere2/program/sc7fw/source/sc7fw_main.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "sc7fw_util.hpp" -#include "sc7fw_dram.hpp" - -namespace ams::sc7fw { - - namespace { - - constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); - - void DisableCrail() { - /* Wait for CRAIL to be off. */ - while (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_ENUM(PWRGATE_STATUS_CRAIL, OFF))) { /* ... */ } - - /* Set CRAIL to be clamped. */ - reg::ReadWrite(PMC + APBDEV_PMC_SET_SW_CLAMP, PMC_REG_BITS_VALUE(SET_SW_CLAMP_CRAIL, 1)); - - /* Wait for CRAIL to be clamped. */ - while (!reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, PMC_REG_BITS_ENUM(CLAMP_STATUS_CRAIL, ENABLE))) { /* ... */ } - - /* Spin loop for a short while, to allow time for the clamp to take effect. */ - sc7fw::SpinLoop(10); - - /* Initialize i2c-5. */ - i2c::Initialize(i2c::Port_5); - - /* Disable the voltage to CPU. */ - pmic::DisableVddCpu(fuse::GetRegulator()); - - /* Wait 700 microseconds to ensure voltage is disabled. */ - util::WaitMicroSeconds(700); - } - - void DisableAllInterrupts() { - /* Disable all interrupts for bpmp in all interrupt controllers. */ - reg::Write(PRI_ICTLR(ICTLR_COP_IER_CLR), ~0u); - reg::Write(SEC_ICTLR(ICTLR_COP_IER_CLR), ~0u); - reg::Write(TRI_ICTLR(ICTLR_COP_IER_CLR), ~0u); - reg::Write(QUAD_ICTLR(ICTLR_COP_IER_CLR), ~0u); - reg::Write(PENTA_ICTLR(ICTLR_COP_IER_CLR), ~0u); - reg::Write(HEXA_ICTLR(ICTLR_COP_IER_CLR), ~0u); - } - - void EnterSc7() { - /* Disable read buffering and write buffering in the BPMP cache. */ - reg::ReadWrite(AVP_CACHE_ADDRESS(AVP_CACHE_CONFIG), AVP_CACHE_REG_BITS_ENUM(DISABLE_WB, TRUE), - AVP_CACHE_REG_BITS_ENUM(DISABLE_RB, TRUE)); - - /* Ensure the CPU Rail is turned off. */ - DisableCrail(); - - /* Disable all interrupts. */ - DisableAllInterrupts(); - - /* Save the EMC FSP */ - SaveEmcFsp(); - - /* Enable self-refresh for DRAM */ - EnableSdramSelfRefresh(); - - /* Enable refresh for all EMC devices. */ - EnableEmcAllSegmentsRefresh(); - - /* Enable deep power-down for ddr. */ - EnableDdrDeepPowerDown(); - - /* Enable pad sampling during deep sleep. */ - reg::ReadWrite(PMC + APBDEV_PMC_DPD_SAMPLE, PMC_REG_BITS_ENUM(DPD_SAMPLE_ON, ENABLE)); - reg::Read(PMC + APBDEV_PMC_DPD_SAMPLE); - - /* Wait a while for pad sampling to be enabled. */ - sc7fw::SpinLoop(0x128); - - /* Enter deep sleep. */ - reg::ReadWrite(PMC + APBDEV_PMC_DPD_ENABLE, PMC_REG_BITS_ENUM(DPD_ENABLE_ON, ENABLE)); - - /* Wait forever until we're asleep. */ - while (true) { /* ... */ } - } - - } - - void Main() { - EnterSc7(); - } - - NORETURN void ExceptionHandler() { - /* Write enable to MAIN_RESET. */ - reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE)); - while (true) { /* ... */ } - } - -} - -namespace ams::diag { - - void AbortImpl() { - sc7fw::ExceptionHandler(); - } - -} diff --git a/exosphere2/program/sc7fw/source/sc7fw_start.s b/exosphere2/program/sc7fw/source/sc7fw_start.s deleted file mode 100644 index 02761c1..0000000 --- a/exosphere2/program/sc7fw/source/sc7fw_start.s +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -.section .text.start, "ax", %progbits -.align 3 -.global _start -_start: - /* Set CPSR_cf and CPSR_cf. */ - msr cpsr_f, #0xC0 - msr cpsr_cf, #0xD3 - - /* Set the stack pointer. */ - ldr sp, =0x40005000 - - /* Set our link register to the exception handler. */ - ldr lr, =_ZN3ams5sc7fw16ExceptionHandlerEv - - /* Invoke main. */ - bl _ZN3ams5sc7fw4MainEv - - /* Infinite loop. */ - 1: b 1b \ No newline at end of file diff --git a/exosphere2/program/sc7fw/source/sc7fw_util.hpp b/exosphere2/program/sc7fw/source/sc7fw_util.hpp deleted file mode 100644 index 2202deb..0000000 --- a/exosphere2/program/sc7fw/source/sc7fw_util.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include - -namespace ams::sc7fw { - - void SpinLoop(unsigned int num); - -} \ No newline at end of file diff --git a/exosphere2/program/sc7fw/source/sc7fw_util_asm.s b/exosphere2/program/sc7fw/source/sc7fw_util_asm.s deleted file mode 100644 index 249c5b0..0000000 --- a/exosphere2/program/sc7fw/source/sc7fw_util_asm.s +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -.section .text._ZN3ams5sc7fw8SpinLoopEj, "ax", %progbits -.global _ZN3ams5sc7fw8SpinLoopEj -.thumb_func -.syntax unified -_ZN3ams5sc7fw8SpinLoopEj: - 1: - /* Subtract one from the count. */ - subs r0, r0, #1 - - /* If we aren't at zero, continue looping. */ - bgt 1b - - /* Return. */ - bx lr \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot.hpp b/exosphere2/program/source/boot/secmon_boot.hpp deleted file mode 100644 index 469aa88..0000000 --- a/exosphere2/program/source/boot/secmon_boot.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_boot_cache.cpp b/exosphere2/program/source/boot/secmon_boot_cache.cpp deleted file mode 100644 index f2d1f8e..0000000 --- a/exosphere2/program/source/boot/secmon_boot_cache.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_boot_cache.hpp b/exosphere2/program/source/boot/secmon_boot_cache.hpp deleted file mode 100644 index 60d5b42..0000000 --- a/exosphere2/program/source/boot/secmon_boot_cache.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_boot_config.cpp b/exosphere2/program/source/boot/secmon_boot_config.cpp deleted file mode 100644 index c444862..0000000 --- a/exosphere2/program/source/boot/secmon_boot_config.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp deleted file mode 100644 index 08e42c8..0000000 --- a/exosphere2/program/source/boot/secmon_boot_functions.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_boot_functions.hpp b/exosphere2/program/source/boot/secmon_boot_functions.hpp deleted file mode 100644 index c03a04b..0000000 --- a/exosphere2/program/source/boot/secmon_boot_functions.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_boot_key_data.s b/exosphere2/program/source/boot/secmon_boot_key_data.s deleted file mode 100644 index 2990ea3..0000000 --- a/exosphere2/program/source/boot/secmon_boot_key_data.s +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_boot_rsa.cpp b/exosphere2/program/source/boot/secmon_boot_rsa.cpp deleted file mode 100644 index 344196c..0000000 --- a/exosphere2/program/source/boot/secmon_boot_rsa.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_boot_setup.cpp b/exosphere2/program/source/boot/secmon_boot_setup.cpp deleted file mode 100644 index 1f56214..0000000 --- a/exosphere2/program/source/boot/secmon_boot_setup.cpp +++ /dev/null @@ -1,386 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_crt0.s b/exosphere2/program/source/boot/secmon_crt0.s deleted file mode 100644 index 44d24eb..0000000 --- a/exosphere2/program/source/boot/secmon_crt0.s +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -.section .crt0.text.start, "ax", %progbits -.align 6 -.global _start -_start: - /* mask all interrupts */ - msr daifset, #0xF - - /* Set the stack pointer to a temporary location. */ - ldr x20, =0x7C010800 - mov sp, x20 - - /* Initialize all memory to expected state. */ - ldr x0, =__bss_start__ - ldr x1, =__bss_end__ - ldr x2, =__boot_bss_start__ - ldr x3, =__boot_bss_end__ - bl _ZN3ams6secmon4boot10InitializeEmmmm - - /* Jump to the first bit of virtual code. */ - ldr x16, =_ZN3ams6secmon5StartEv - br x16 diff --git a/exosphere2/program/source/boot/secmon_crt0_cpp.cpp b/exosphere2/program/source/boot/secmon_crt0_cpp.cpp deleted file mode 100644 index 35b993e..0000000 --- a/exosphere2/program/source/boot/secmon_crt0_cpp.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "secmon_boot.hpp" -#include "../secmon_setup.hpp" - -extern "C" void __libc_init_array(); - -namespace ams::secmon::boot { - - void Initialize(uintptr_t bss_start, size_t bss_end, uintptr_t boot_bss_start, uintptr_t boot_bss_end) { - /* Set our start time. */ - auto &secmon_params = *MemoryRegionPhysicalDeviceBootloaderParams.GetPointer(); - secmon_params.secmon_start_time = *reinterpret_cast(MemoryRegionPhysicalDeviceTimer.GetAddress() + 0x10); - - /* Setup DMA controllers. */ - SetupSocDmaControllers(); - - /* Make the page table. */ - MakePageTable(); - - /* Setup memory controllers the MMU. */ - SetupCpuMemoryControllersEnableMmu(); - - /* Clear bss. */ - std::memset(reinterpret_cast(bss_start), 0, bss_end - bss_start); - std::memset(reinterpret_cast(boot_bss_start), 0, boot_bss_end - boot_bss_start); - - /* Call init array. */ - __libc_init_array(); - } - -} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp deleted file mode 100644 index 6769cd9..0000000 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_make_page_table.cpp b/exosphere2/program/source/boot/secmon_make_page_table.cpp deleted file mode 100644 index 4309cf9..0000000 --- a/exosphere2/program/source/boot/secmon_make_page_table.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_package2.cpp b/exosphere2/program/source/boot/secmon_package2.cpp deleted file mode 100644 index 32e8e8a..0000000 --- a/exosphere2/program/source/boot/secmon_package2.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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/exosphere2/program/source/boot/secmon_size_data.s b/exosphere2/program/source/boot/secmon_size_data.s deleted file mode 100644 index 0943ef9..0000000 --- a/exosphere2/program/source/boot/secmon_size_data.s +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -.section .metadata.sizes, "ax", %progbits -.align 6 -.global __metadata__sizes -__metadata__sizes: - .quad 0xAAAAAAAAAAAAAAAA, 0xBBBBBBBBBBBBBBBB - .quad __glob_start__ - .quad __bootcode_start__ - .quad __bootcode_end__ - .quad __program_start__ - .quad 0xCCCCCCCCCCCCCCCC, 0xDDDDDDDDDDDDDDDD diff --git a/exosphere2/program/source/secmon_cache.cpp b/exosphere2/program/source/secmon_cache.cpp deleted file mode 100644 index 02fd043..0000000 --- a/exosphere2/program/source/secmon_cache.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_cache.hpp b/exosphere2/program/source/secmon_cache.hpp deleted file mode 100644 index 3050c38..0000000 --- a/exosphere2/program/source/secmon_cache.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_cache.inc b/exosphere2/program/source/secmon_cache.inc deleted file mode 100644 index 8ae241a..0000000 --- a/exosphere2/program/source/secmon_cache.inc +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_cache_impl.inc b/exosphere2/program/source/secmon_cache_impl.inc deleted file mode 100644 index a305ce6..0000000 --- a/exosphere2/program/source/secmon_cache_impl.inc +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_cpu_context.cpp b/exosphere2/program/source/secmon_cpu_context.cpp deleted file mode 100644 index d2185a6..0000000 --- a/exosphere2/program/source/secmon_cpu_context.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "secmon_cpu_context.hpp" -#include "secmon_error.hpp" - -namespace ams::secmon { - - namespace { - - struct DebugRegisters { - u32 osdttrx_el1; - u32 osdtrtx_el1; - u32 mdscr_el1; - u32 oseccr_el1; - u32 mdccint_el1; - u32 dbgclaimclr_el1; - u32 dbgvcr32_el2; - u32 sder32_el3; - u32 mdcr_el2; - u32 mdcr_el3; - u32 spsr_el3; - u64 dbgbvcr_el1[12]; - u64 dbgwvcr_el1[ 8]; - }; - - struct CoreContext { - EntryContext entry_context; - bool is_on; - bool is_reset_expected; - bool is_debug_registers_saved; - DebugRegisters debug_registers; - }; - - void SaveDebugRegisters(DebugRegisters &dr) { - /* Set the OS lock; this will be unlocked by entry code. */ - HW_CPU_SET_OSLAR_EL1(1); - - /* Save general debug registers. */ - HW_CPU_GET_OSDTRRX_EL1 (dr.osdttrx_el1); - HW_CPU_GET_OSDTRTX_EL1 (dr.osdtrtx_el1); - HW_CPU_GET_MDSCR_EL1 (dr.mdscr_el1); - HW_CPU_GET_OSECCR_EL1 (dr.oseccr_el1); - HW_CPU_GET_MDCCINT_EL1 (dr.mdccint_el1); - HW_CPU_GET_DBGCLAIMCLR_EL1(dr.dbgclaimclr_el1); - HW_CPU_GET_DBGVCR32_EL2 (dr.dbgvcr32_el2); - HW_CPU_GET_SDER32_EL3 (dr.sder32_el3); - HW_CPU_GET_MDCR_EL2 (dr.mdcr_el2); - HW_CPU_GET_MDCR_EL3 (dr.mdcr_el3); - HW_CPU_GET_SPSR_EL3 (dr.spsr_el3); - - /* Save debug breakpoints. */ - HW_CPU_GET_DBGBVR0_EL1(dr.dbgbvcr_el1[ 0]); - HW_CPU_GET_DBGBCR0_EL1(dr.dbgbvcr_el1[ 1]); - HW_CPU_GET_DBGBVR1_EL1(dr.dbgbvcr_el1[ 2]); - HW_CPU_GET_DBGBCR1_EL1(dr.dbgbvcr_el1[ 3]); - HW_CPU_GET_DBGBVR2_EL1(dr.dbgbvcr_el1[ 4]); - HW_CPU_GET_DBGBCR2_EL1(dr.dbgbvcr_el1[ 5]); - HW_CPU_GET_DBGBVR3_EL1(dr.dbgbvcr_el1[ 6]); - HW_CPU_GET_DBGBCR3_EL1(dr.dbgbvcr_el1[ 7]); - HW_CPU_GET_DBGBVR4_EL1(dr.dbgbvcr_el1[ 8]); - HW_CPU_GET_DBGBCR4_EL1(dr.dbgbvcr_el1[ 9]); - HW_CPU_GET_DBGBVR5_EL1(dr.dbgbvcr_el1[10]); - HW_CPU_GET_DBGBCR5_EL1(dr.dbgbvcr_el1[11]); - - /* Save debug watchpoints. */ - HW_CPU_GET_DBGWVR0_EL1(dr.dbgwvcr_el1[0]); - HW_CPU_GET_DBGWCR0_EL1(dr.dbgwvcr_el1[1]); - HW_CPU_GET_DBGWVR1_EL1(dr.dbgwvcr_el1[2]); - HW_CPU_GET_DBGWCR1_EL1(dr.dbgwvcr_el1[3]); - HW_CPU_GET_DBGWVR2_EL1(dr.dbgwvcr_el1[4]); - HW_CPU_GET_DBGWCR2_EL1(dr.dbgwvcr_el1[5]); - HW_CPU_GET_DBGWVR3_EL1(dr.dbgwvcr_el1[6]); - HW_CPU_GET_DBGWCR3_EL1(dr.dbgwvcr_el1[7]); - } - - void RestoreDebugRegisters(const DebugRegisters &dr) { - /* Restore general debug registers. */ - HW_CPU_SET_OSDTRRX_EL1 (dr.osdttrx_el1); - HW_CPU_SET_OSDTRTX_EL1 (dr.osdtrtx_el1); - HW_CPU_SET_MDSCR_EL1 (dr.mdscr_el1); - HW_CPU_SET_OSECCR_EL1 (dr.oseccr_el1); - HW_CPU_SET_MDCCINT_EL1 (dr.mdccint_el1); - HW_CPU_SET_DBGCLAIMCLR_EL1(dr.dbgclaimclr_el1); - HW_CPU_SET_DBGVCR32_EL2 (dr.dbgvcr32_el2); - HW_CPU_SET_SDER32_EL3 (dr.sder32_el3); - HW_CPU_SET_MDCR_EL2 (dr.mdcr_el2); - HW_CPU_SET_MDCR_EL3 (dr.mdcr_el3); - HW_CPU_SET_SPSR_EL3 (dr.spsr_el3); - - /* Restore debug breakpoints. */ - HW_CPU_SET_DBGBVR0_EL1(dr.dbgbvcr_el1[ 0]); - HW_CPU_SET_DBGBCR0_EL1(dr.dbgbvcr_el1[ 1]); - HW_CPU_SET_DBGBVR1_EL1(dr.dbgbvcr_el1[ 2]); - HW_CPU_SET_DBGBCR1_EL1(dr.dbgbvcr_el1[ 3]); - HW_CPU_SET_DBGBVR2_EL1(dr.dbgbvcr_el1[ 4]); - HW_CPU_SET_DBGBCR2_EL1(dr.dbgbvcr_el1[ 5]); - HW_CPU_SET_DBGBVR3_EL1(dr.dbgbvcr_el1[ 6]); - HW_CPU_SET_DBGBCR3_EL1(dr.dbgbvcr_el1[ 7]); - HW_CPU_SET_DBGBVR4_EL1(dr.dbgbvcr_el1[ 8]); - HW_CPU_SET_DBGBCR4_EL1(dr.dbgbvcr_el1[ 9]); - HW_CPU_SET_DBGBVR5_EL1(dr.dbgbvcr_el1[10]); - HW_CPU_SET_DBGBCR5_EL1(dr.dbgbvcr_el1[11]); - - /* Restore debug watchpoints. */ - HW_CPU_SET_DBGWVR0_EL1(dr.dbgwvcr_el1[0]); - HW_CPU_SET_DBGWCR0_EL1(dr.dbgwvcr_el1[1]); - HW_CPU_SET_DBGWVR1_EL1(dr.dbgwvcr_el1[2]); - HW_CPU_SET_DBGWCR1_EL1(dr.dbgwvcr_el1[3]); - HW_CPU_SET_DBGWVR2_EL1(dr.dbgwvcr_el1[4]); - HW_CPU_SET_DBGWCR2_EL1(dr.dbgwvcr_el1[5]); - HW_CPU_SET_DBGWVR3_EL1(dr.dbgwvcr_el1[6]); - HW_CPU_SET_DBGWCR3_EL1(dr.dbgwvcr_el1[7]); - } - - constinit CoreContext g_core_contexts[NumCores] = {}; - - } - - bool IsCoreOn(int core) { - return g_core_contexts[core].is_on; - } - - void SetCoreOff() { - g_core_contexts[hw::GetCurrentCoreId()].is_on = false; - } - - bool IsResetExpected() { - return g_core_contexts[hw::GetCurrentCoreId()].is_reset_expected; - } - - void SetResetExpected(int core, bool expected) { - g_core_contexts[core].is_reset_expected = expected; - } - - void SetResetExpected(bool expected) { - SetResetExpected(hw::GetCurrentCoreId(), expected); - } - - void SetEntryContext(int core, uintptr_t address, uintptr_t arg) { - g_core_contexts[core].entry_context.pc = address; - g_core_contexts[core].entry_context.x0 = arg; - } - - void GetEntryContext(EntryContext *out) { - auto &ctx = g_core_contexts[hw::GetCurrentCoreId()]; - - const auto pc = ctx.entry_context.pc; - const auto x0 = ctx.entry_context.x0; - - if (pc == 0 || ctx.is_on) { - SetError(pkg1::ErrorInfo_InvalidCoreContext); - AMS_ABORT("Invalid core context"); - } - - ctx.entry_context = {}; - ctx.is_on = true; - - out->pc = pc; - out->x0 = x0; - } - - void SaveDebugRegisters() { - auto &ctx = g_core_contexts[hw::GetCurrentCoreId()]; - - SaveDebugRegisters(ctx.debug_registers); - ctx.is_debug_registers_saved = true; - } - - void RestoreDebugRegisters() { - auto &ctx = g_core_contexts[hw::GetCurrentCoreId()]; - - if (ctx.is_debug_registers_saved) { - RestoreDebugRegisters(ctx.debug_registers); - ctx.is_debug_registers_saved = false; - } - } - -} diff --git a/exosphere2/program/source/secmon_cpu_context.hpp b/exosphere2/program/source/secmon_cpu_context.hpp deleted file mode 100644 index a19bd8a..0000000 --- a/exosphere2/program/source/secmon_cpu_context.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include - -namespace ams::secmon { - - constexpr inline int NumCores = 4; - - struct EntryContext { - u64 x0; - u64 pc; - }; - - bool IsCoreOn(int core); - void SetCoreOff(); - - bool IsResetExpected(); - void SetResetExpected(int core, bool expected); - void SetResetExpected(bool expected); - - void SetEntryContext(int core, uintptr_t address, uintptr_t arg); - void GetEntryContext(EntryContext *out); - - void SaveDebugRegisters(); - void RestoreDebugRegisters(); - -} diff --git a/exosphere2/program/source/secmon_error.cpp b/exosphere2/program/source/secmon_error.cpp deleted file mode 100644 index c05bf4c..0000000 --- a/exosphere2/program/source/secmon_error.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_error.hpp b/exosphere2/program/source/secmon_error.hpp deleted file mode 100644 index abbfad2..0000000 --- a/exosphere2/program/source/secmon_error.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include - -namespace ams::secmon { - - void SetError(pkg1::ErrorInfo info); - NORETURN void ErrorReboot(); - -} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_exception_vectors.s b/exosphere2/program/source/secmon_exception_vectors.s deleted file mode 100644 index 2749f9f..0000000 --- a/exosphere2/program/source/secmon_exception_vectors.s +++ /dev/null @@ -1,326 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_interrupt_handler.cpp b/exosphere2/program/source/secmon_interrupt_handler.cpp deleted file mode 100644 index 41f4666..0000000 --- a/exosphere2/program/source/secmon_interrupt_handler.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "secmon_interrupt_handler.hpp" -#include "secmon_error.hpp" - -namespace ams::secmon { - - namespace { - - constexpr inline int InterruptHandlersMax = 4; - - constinit InterruptHandler g_handlers[InterruptHandlersMax] = {}; - constinit int g_interrupt_ids[InterruptHandlersMax] = {}; - - } - - void SetInterruptHandler(int interrupt_id, InterruptHandler handler) { - for (int i = 0; i < InterruptHandlersMax; ++i) { - if (g_interrupt_ids[i] == 0) { - g_interrupt_ids[i] = interrupt_id; - g_handlers[i] = handler; - return; - } - } - - AMS_ABORT("Failed to register interrupt handler"); - } - - void HandleInterrupt() { - /* Get the interrupt id. */ - const int interrupt_id = gic::GetInterruptRequestId(); - if (interrupt_id >= gic::InterruptCount) { - /* Invalid interrupt number, just return. */ - return; - } - - /* Check each handler. */ - for (int i = 0; i < InterruptHandlersMax; ++i) { - if (g_interrupt_ids[i] == interrupt_id) { - /* Invoke the handler. */ - g_handlers[i](); - gic::SetEndOfInterrupt(interrupt_id); - return; - } - } - - AMS_ABORT("Failed to find interrupt handler."); - } - -} diff --git a/exosphere2/program/source/secmon_interrupt_handler.hpp b/exosphere2/program/source/secmon_interrupt_handler.hpp deleted file mode 100644 index 136da02..0000000 --- a/exosphere2/program/source/secmon_interrupt_handler.hpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include - -namespace ams::secmon { - - using InterruptHandler = void (*)(); - - void SetInterruptHandler(int interrupt_id, InterruptHandler handler); - -} diff --git a/exosphere2/program/source/secmon_key_storage.cpp b/exosphere2/program/source/secmon_key_storage.cpp deleted file mode 100644 index 207f102..0000000 --- a/exosphere2/program/source/secmon_key_storage.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_key_storage.hpp b/exosphere2/program/source/secmon_key_storage.hpp deleted file mode 100644 index 076756d..0000000 --- a/exosphere2/program/source/secmon_key_storage.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp deleted file mode 100644 index 96ccf84..0000000 --- a/exosphere2/program/source/secmon_map.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_map.hpp b/exosphere2/program/source/secmon_map.hpp deleted file mode 100644 index a6bdbc4..0000000 --- a/exosphere2/program/source/secmon_map.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_misc.cpp b/exosphere2/program/source/secmon_misc.cpp deleted file mode 100644 index eed73b5..0000000 --- a/exosphere2/program/source/secmon_misc.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_misc.hpp b/exosphere2/program/source/secmon_misc.hpp deleted file mode 100644 index f637d0a..0000000 --- a/exosphere2/program/source/secmon_misc.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_page_mapper.cpp b/exosphere2/program/source/secmon_page_mapper.cpp deleted file mode 100644 index 93452d4..0000000 --- a/exosphere2/program/source/secmon_page_mapper.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_page_mapper.hpp b/exosphere2/program/source/secmon_page_mapper.hpp deleted file mode 100644 index 1da95eb..0000000 --- a/exosphere2/program/source/secmon_page_mapper.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp deleted file mode 100644 index 254b8e5..0000000 --- a/exosphere2/program/source/secmon_setup.cpp +++ /dev/null @@ -1,1220 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_setup.hpp b/exosphere2/program/source/secmon_setup.hpp deleted file mode 100644 index f6e1f37..0000000 --- a/exosphere2/program/source/secmon_setup.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_setup_warm.cpp b/exosphere2/program/source/secmon_setup_warm.cpp deleted file mode 100644 index 5603593..0000000 --- a/exosphere2/program/source/secmon_setup_warm.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_spinlock.hpp b/exosphere2/program/source/secmon_spinlock.hpp deleted file mode 100644 index 3c095da..0000000 --- a/exosphere2/program/source/secmon_spinlock.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_spinlock.s b/exosphere2/program/source/secmon_spinlock.s deleted file mode 100644 index 0e4ef7f..0000000 --- a/exosphere2/program/source/secmon_spinlock.s +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_stack_warm.s b/exosphere2/program/source/secmon_stack_warm.s deleted file mode 100644 index 85dad85..0000000 --- a/exosphere2/program/source/secmon_stack_warm.s +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s deleted file mode 100644 index d8b1356..0000000 --- a/exosphere2/program/source/secmon_start_virtual.s +++ /dev/null @@ -1,212 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_start_warm.s b/exosphere2/program/source/secmon_start_warm.s deleted file mode 100644 index 74eaa53..0000000 --- a/exosphere2/program/source/secmon_start_warm.s +++ /dev/null @@ -1,212 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_user_power_management.cpp b/exosphere2/program/source/secmon_user_power_management.cpp deleted file mode 100644 index 4fb063b..0000000 --- a/exosphere2/program/source/secmon_user_power_management.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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/exosphere2/program/source/secmon_user_power_management.hpp b/exosphere2/program/source/secmon_user_power_management.hpp deleted file mode 100644 index 9a50e82..0000000 --- a/exosphere2/program/source/secmon_user_power_management.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_define_access_table.inc b/exosphere2/program/source/smc/secmon_define_access_table.inc deleted file mode 100644 index d887523..0000000 --- a/exosphere2/program/source/smc/secmon_define_access_table.inc +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_define_mc01_access_table.inc b/exosphere2/program/source/smc/secmon_define_mc01_access_table.inc deleted file mode 100644 index ecb142b..0000000 --- a/exosphere2/program/source/smc/secmon_define_mc01_access_table.inc +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_define_mc_access_table.inc b/exosphere2/program/source/smc/secmon_define_mc_access_table.inc deleted file mode 100644 index bfc3da5..0000000 --- a/exosphere2/program/source/smc/secmon_define_mc_access_table.inc +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_define_pmc_access_table.inc b/exosphere2/program/source/smc/secmon_define_pmc_access_table.inc deleted file mode 100644 index 9ce0f52..0000000 --- a/exosphere2/program/source/smc/secmon_define_pmc_access_table.inc +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_mc01_access_table_data.inc b/exosphere2/program/source/smc/secmon_mc01_access_table_data.inc deleted file mode 100644 index 4e4f68d..0000000 --- a/exosphere2/program/source/smc/secmon_mc01_access_table_data.inc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_mc_access_table_data.inc b/exosphere2/program/source/smc/secmon_mc_access_table_data.inc deleted file mode 100644 index 5b97b8f..0000000 --- a/exosphere2/program/source/smc/secmon_mc_access_table_data.inc +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_pmc_access_table_data.inc b/exosphere2/program/source/smc/secmon_pmc_access_table_data.inc deleted file mode 100644 index e94be6d..0000000 --- a/exosphere2/program/source/smc/secmon_pmc_access_table_data.inc +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_random_cache.cpp b/exosphere2/program/source/smc/secmon_random_cache.cpp deleted file mode 100644 index f14330b..0000000 --- a/exosphere2/program/source/smc/secmon_random_cache.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_random_cache.hpp b/exosphere2/program/source/smc/secmon_random_cache.hpp deleted file mode 100644 index e1e0082..0000000 --- a/exosphere2/program/source/smc/secmon_random_cache.hpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp deleted file mode 100644 index cfb992e..0000000 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ /dev/null @@ -1,768 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp deleted file mode 100644 index b502ee6..0000000 --- a/exosphere2/program/source/smc/secmon_smc_aes.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_smc_carveout.cpp b/exosphere2/program/source/smc/secmon_smc_carveout.cpp deleted file mode 100644 index 120ae90..0000000 --- a/exosphere2/program/source/smc/secmon_smc_carveout.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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/exosphere2/program/source/smc/secmon_smc_carveout.hpp b/exosphere2/program/source/smc/secmon_smc_carveout.hpp deleted file mode 100644 index bc09fcd..0000000 --- a/exosphere2/program/source/smc/secmon_smc_carveout.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "secmon_smc_common.hpp" - -namespace ams::secmon::smc { - - SmcResult SmcSetKernelCarveoutRegion(SmcArguments &args); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_common.hpp b/exosphere2/program/source/smc/secmon_smc_common.hpp deleted file mode 100644 index a03658e..0000000 --- a/exosphere2/program/source/smc/secmon_smc_common.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include - -namespace ams::secmon::smc { - - enum class SmcResult : u32 { - Success = 0, - NotImplemented = 1, - InvalidArgument = 2, - Busy = 3, - NoAsyncOperation = 4, - InvalidAsyncOperation = 5, - NotPermitted = 6, - NotInitialized = 7, - - PsciSuccess = 0, - PsciNotSupported = static_cast(-1), - PsciInvalidParameters = static_cast(-2), - PsciDenied = static_cast(-3), - PsciAlreadyOn = static_cast(-4), - }; - - #define SMC_R_SUCCEEEDED(res) (res == SmcResult::Success) - #define SMC_R_FAILED(res) (res != SmcResult::Success) - - #define SMC_R_TRY(res_expr) ({ const auto _tmp_r_try_rc = (res_expr); if (SMC_R_FAILED(_tmp_r_try_rc)) { return _tmp_r_try_rc; } }) - #define SMC_R_UNLESS(cond, RES) ({ if (!(cond)) { return SmcResult::RES; }}) - - struct SmcArguments { - u64 r[8]; - }; - - constexpr inline int SecurityEngineUserInterruptId = 44; - constexpr inline u64 InvalidAsyncKey = 0; - -} diff --git a/exosphere2/program/source/smc/secmon_smc_cpu_asm.s b/exosphere2/program/source/smc/secmon_smc_cpu_asm.s deleted file mode 100644 index dd9f4df..0000000 --- a/exosphere2/program/source/smc/secmon_smc_cpu_asm.s +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 - -.section .text._ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE, "ax", %progbits -.align 4 -.global _ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE -_ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE: - /* 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 .text._ZN3ams6secmon3smc16FinalizePowerOffEv, "ax", %progbits -.align 4 -.global _ZN3ams6secmon3smc16FinalizePowerOffEv -_ZN3ams6secmon3smc16FinalizePowerOffEv: - /* Disable all caches by clearing sctlr_el1.C. */ - mrs x0, sctlr_el1 - and x0, x0, #~(1 << 2) - msr sctlr_el1, x0 - isb - - /* Disable all caches by clearing sctlr_el3.C. */ - mrs x0, sctlr_el3 - and x0, x0, #~(1 << 2) - msr sctlr_el3, x0 - isb - - /* Disable prefetching of page table walking descriptors. */ - mrs x0, cpuectlr_el1 - orr x0, x0, #(1 << 38) - - /* Disable prefetching of instructions. */ - and x0, x0, #~(3 << 35) - - /* Disable prefetching of data. */ - and x0, x0, #~(3 << 32) - msr cpuectlr_el1, x0 - isb - - /* Ensure that all data prefetching prior to our configuration change completes. */ - dsb sy - - /* Flush the entire data cache (local). */ - bl _ZN3ams6secmon25FlushEntireDataCacheLocalEv - - /* Disable receiving instruction cache/TLB maintenance operations. */ - mrs x0, cpuectlr_el1 - and x0, x0, #~(1 << 6) - msr cpuectlr_el1, x0 - - /* Configure the gic to not send interrupts to the current core. */ - ldr x1, =0x1F0043000 - mov w0, #0x1E0 /* Set FIQBypDisGrp1, IRQBypDisGrp1, reserved bits 7/8. */ - str w0, [x1] - - /* Lock the OS Double Lock. */ - mrs x0, osdlr_el1 - orr x0, x0, #(1 << 0) - msr osdlr_el1, x0 - - /* Ensure that our configuration takes. */ - isb - dsb sy - - /* Wait for interrupts, infinitely. */ -0: - wfi - b 0b - - - diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp deleted file mode 100644 index 6505daa..0000000 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "../secmon_error.hpp" -#include "secmon_smc_device_unique_data.hpp" - -namespace ams::secmon::smc { - - namespace { - - void GenerateIv(void *dst, size_t dst_size) { - /* Flush the region we're about to fill to ensure consistency with the SE. */ - hw::FlushDataCache(dst, dst_size); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Generate random bytes. */ - se::GenerateRandomBytes(dst, dst_size); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Flush to ensure the CPU sees consistent data for the region. */ - hw::FlushDataCache(dst, dst_size); - hw::DataSynchronizationBarrierInnerShareable(); - } - - void PrepareDeviceUniqueDataKey(const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size) { - /* Derive the seal key. */ - se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, seal_key_source, seal_key_source_size); - - /* Derive the device unique data kek. */ - se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, access_key, access_key_size); - - /* Derive the actual device unique data key. */ - se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, key_source, key_source_size); - } - - void ComputeAes128Ctr(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { - /* Ensure that the SE sees consistent data. */ - hw::FlushDataCache(src, src_size); - hw::FlushDataCache(dst, dst_size); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Use the security engine to transform the data. */ - se::ComputeAes128Ctr(dst, dst_size, slot, src, src_size, iv, iv_size); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Ensure the CPU sees consistent data. */ - hw::FlushDataCache(dst, dst_size); - hw::DataSynchronizationBarrierInnerShareable(); - } - - void ComputeGmac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *iv, size_t iv_size) { - /* Declare keyslot (as encryptor will need to take it by pointer/reference). */ - constexpr int Slot = pkg1::AesKeySlot_Smc; - - /* Calculate the mac. */ - crypto::Aes128GcmEncryptor gcm; - gcm.Initialize(std::addressof(Slot), sizeof(Slot), iv, iv_size); - gcm.UpdateAad(data, data_size); - gcm.GetMac(dst, dst_size); - } - - constexpr u64 GetDeviceIdLow(u64 device_id) { - /* Mask out the top byte. */ - constexpr u64 ByteMask = (static_cast(1) << BITSIZEOF(u8)) - 1; - constexpr u64 LowMask = ~(ByteMask << (BITSIZEOF(u64) - BITSIZEOF(u8))); - return device_id & LowMask; - } - - constexpr u8 GetDeviceIdHigh(u64 device_id) { - /* Get the top byte. */ - return static_cast(device_id >> (BITSIZEOF(u64) - BITSIZEOF(u8))); - } - - constexpr u64 EncodeDeviceId(u8 device_id_high, u64 device_id_low) { - return (static_cast(device_id_high) << (BITSIZEOF(u64) - BITSIZEOF(u8))) | device_id_low; - } - - } - - bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size) { - /* Determine how much decrypted data there will be. */ - const size_t enc_size = src_size - DeviceUniqueDataOuterMetaSize; - const size_t dec_size = enc_size - DeviceUniqueDataInnerMetaSize; - - /* Ensure that our sizes are allowed. */ - AMS_ABORT_UNLESS(src_size > DeviceUniqueDataTotalMetaSize); - AMS_ABORT_UNLESS(dst_size >= enc_size); - - /* Determine the extents of the data. */ - const u8 * const iv = static_cast(src); - const u8 * const enc = iv + DeviceUniqueDataIvSize; - const u8 * const mac = enc + enc_size; - - /* Decrypt the data. */ - { - /* Declare temporaries. */ - u8 temp_iv[DeviceUniqueDataIvSize]; - u8 calc_mac[DeviceUniqueDataMacSize]; - ON_SCOPE_EXIT { crypto::ClearMemory(temp_iv, sizeof(temp_iv)); crypto::ClearMemory(calc_mac, sizeof(calc_mac)); }; - - /* Prepare the key used to decrypt the data. */ - PrepareDeviceUniqueDataKey(seal_key_source, seal_key_source_size, access_key, access_key_size, key_source, key_source_size); - - /* Copy the iv to stack. */ - std::memcpy(temp_iv, iv, sizeof(temp_iv)); - - /* Decrypt the data. */ - ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Smc, enc, enc_size, temp_iv, DeviceUniqueDataIvSize); - - /* Compute the gmac. */ - ComputeGmac(calc_mac, DeviceUniqueDataMacSize, dst, enc_size, temp_iv, DeviceUniqueDataIvSize); - - /* Validate the gmac. */ - if (!crypto::IsSameBytes(mac, calc_mac, sizeof(calc_mac))) { - return false; - } - } - - /* Validate device id, output device id if needed. */ - { - /* Locate the device id in the decryption output. */ - const u8 * const padding = static_cast(dst) + dec_size; - const u8 * const device_id = padding + DeviceUniqueDataPaddingSize; - - /* Load the big endian device id. */ - const u64 device_id_val = util::LoadBigEndian(static_cast(static_cast(device_id))); - - /* Validate that the device id low matches the value in fuses. */ - if (GetDeviceIdLow(device_id_val) != fuse::GetDeviceId()) { - return false; - } - - /* Set the output device id high, if needed. */ - if (out_device_id_high != nullptr) { - *out_device_id_high = GetDeviceIdHigh(device_id_val); - } - } - - return true; - } - - void EncryptDeviceUniqueData(void *dst, size_t dst_size, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, u8 device_id_high) { - /* Determine metadata locations. */ - u8 * const dst_iv = static_cast(dst); - u8 * const dst_data = dst_iv + DeviceUniqueDataIvSize; - u8 * const dst_pad = dst_data + src_size; - u8 * const dst_did = dst_pad + DeviceUniqueDataPaddingSize; - u8 * const dst_mac = dst_did + DeviceUniqueDataDeviceIdSize; - - /* Verify that our sizes are okay. */ - const size_t enc_size = src_size + DeviceUniqueDataInnerMetaSize; - const size_t res_size = src_size + DeviceUniqueDataTotalMetaSize; - AMS_ABORT_UNLESS(res_size <= dst_size); - - /* Layout the image as expected. */ - { - /* Generate a random iv. */ - util::AlignedBuffer iv; - GenerateIv(iv, DeviceUniqueDataIvSize); - - /* Move the data to the output image. */ - std::memmove(dst_data, src, src_size); - - /* Copy the iv. */ - std::memcpy(dst_iv, iv, DeviceUniqueDataIvSize); - - /* Clear the padding. */ - std::memset(dst_pad, 0, DeviceUniqueDataPaddingSize); - - /* Store the device id. */ - util::StoreBigEndian(reinterpret_cast(dst_did), EncodeDeviceId(device_id_high, fuse::GetDeviceId())); - } - - /* Encrypt and mac. */ - { - - /* Prepare the key used to encrypt the data. */ - PrepareDeviceUniqueDataKey(seal_key_source, seal_key_source_size, access_key, access_key_size, key_source, key_source_size); - - /* Compute the gmac. */ - ComputeGmac(dst_mac, DeviceUniqueDataMacSize, dst_data, enc_size, dst_iv, DeviceUniqueDataIvSize); - - /* Encrypt the data. */ - ComputeAes128Ctr(dst_data, enc_size, pkg1::AesKeySlot_Smc, dst_data, enc_size, dst_iv, DeviceUniqueDataIvSize); - } - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp deleted file mode 100644 index a8e7901..0000000 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 DeviceUniqueDataIvSize = se::AesBlockSize; - constexpr inline size_t DeviceUniqueDataMacSize = se::AesBlockSize; - constexpr inline size_t DeviceUniqueDataDeviceIdSize = sizeof(u64); - constexpr inline size_t DeviceUniqueDataPaddingSize = se::AesBlockSize - DeviceUniqueDataDeviceIdSize; - - constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize + DeviceUniqueDataMacSize; - constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize; - constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize; - - bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size); - void EncryptDeviceUniqueData(void *dst, size_t dst_size, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, u8 device_id_high); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_error.cpp b/exosphere2/program/source/smc/secmon_smc_error.cpp deleted file mode 100644 index 70ea8f1..0000000 --- a/exosphere2/program/source/smc/secmon_smc_error.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "../secmon_error.hpp" -#include "secmon_smc_error.hpp" - -namespace ams::secmon::smc { - - SmcResult SmcShowError(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_error.hpp b/exosphere2/program/source/smc/secmon_smc_error.hpp deleted file mode 100644 index 25b74f1..0000000 --- a/exosphere2/program/source/smc/secmon_smc_error.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "secmon_smc_common.hpp" - -namespace ams::secmon::smc { - - SmcResult SmcShowError(SmcArguments &args); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp deleted file mode 100644 index 644139b..0000000 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "../secmon_error.hpp" -#include "../secmon_misc.hpp" -#include "secmon_smc_common.hpp" -#include "secmon_smc_handler.hpp" -#include "secmon_smc_aes.hpp" -#include "secmon_smc_carveout.hpp" -#include "secmon_smc_device_unique_data.hpp" -#include "secmon_smc_error.hpp" -#include "secmon_smc_info.hpp" -#include "secmon_smc_memory_access.hpp" -#include "secmon_smc_power_management.hpp" -#include "secmon_smc_random.hpp" -#include "secmon_smc_register_access.hpp" -#include "secmon_smc_result.hpp" -#include "secmon_smc_rsa.hpp" - -namespace ams::secmon::smc { - - namespace { - - struct HandlerInfo { - u32 function_id; - u32 restriction_mask; - SmcHandler handler; - }; - - struct HandlerTable { - const HandlerInfo *entries; - size_t count; - }; - - enum HandlerType : int { - HandlerType_User = 0, - HandlerType_Kern = 1, - HandlerType_Count = 2, - }; - - enum Restriction { - Restriction_None = (0 << 0), - Restriction_Normal = (1 << 0), - Restriction_DeviceUniqueDataNotAllowed = (1 << 1), - Restriction_SafeModeNotAllowed = (1 << 2), - }; - - enum SmcCallRange { - SmcCallRange_ArmArch = 0, - SmcCallRange_Cpu = 1, - SmcCallRange_Sip = 2, - SmcCallRange_Oem = 3, - SmcCallRange_Standard = 4, - - SmcCallRange_TrustedApp = 0x30, - }; - - enum SmcArgumentType { - ArgumentType_Integer = 0, - ArgumentType_Pointer = 1, - }; - - enum SmcConvention { - Convention_Smc32 = 0, - Convention_Smc64 = 1, - }; - - enum SmcCallType { - SmcCallType_YieldingCall = 0, - SmcCallType_FastCall = 1, - }; - - struct SmcFunctionId { - using FunctionId = util::BitPack64::Field< 0, 8, u32>; - using ArgumentType0 = util::BitPack64::Field< 8, 1, SmcArgumentType>; - using ArgumentType1 = util::BitPack64::Field< 9, 1, SmcArgumentType>; - using ArgumentType2 = util::BitPack64::Field<10, 1, SmcArgumentType>; - using ArgumentType3 = util::BitPack64::Field<11, 1, SmcArgumentType>; - using ArgumentType4 = util::BitPack64::Field<12, 1, SmcArgumentType>; - using ArgumentType5 = util::BitPack64::Field<13, 1, SmcArgumentType>; - using ArgumentType6 = util::BitPack64::Field<14, 1, SmcArgumentType>; - using ArgumentType7 = util::BitPack64::Field<15, 1, SmcArgumentType>; - using Reserved = util::BitPack64::Field<16, 8, u32>; - using CallRange = util::BitPack64::Field<24, 6, SmcCallRange>; - using Convention = util::BitPack64::Field<30, 1, SmcConvention>; - using CallType = util::BitPack64::Field<31, 1, SmcCallType>; - using Reserved2 = util::BitPack64::Field<32, 32, u32>; - }; - - constinit HandlerInfo g_user_handlers[] = { - { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, - { 0xC3000401, Restriction_SafeModeNotAllowed, SmcSetConfig }, - { 0xC3000002, Restriction_Normal, SmcGetConfigUser }, - { 0xC3000003, Restriction_Normal, SmcGetResult }, - { 0xC3000404, Restriction_Normal, SmcGetResultData }, - { 0xC3000E05, Restriction_SafeModeNotAllowed, SmcModularExponentiate }, - { 0xC3000006, Restriction_Normal, SmcGenerateRandomBytes }, - { 0xC3000007, Restriction_Normal, SmcGenerateAesKek }, - { 0xC3000008, Restriction_Normal, SmcLoadAesKey }, - { 0xC3000009, Restriction_Normal, SmcComputeAes }, - { 0xC300000A, Restriction_Normal, SmcGenerateSpecificAesKey }, - { 0xC300040B, Restriction_Normal, SmcComputeCmac }, - { 0xC300D60C, Restriction_Normal, SmcReencryptDeviceUniqueData }, - { 0xC300100D, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptDeviceUniqueData }, - { 0xC300000E, Restriction_SafeModeNotAllowed, nullptr }, - { 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey }, - { 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey }, - { 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey }, - { 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey } - }; - - constinit HandlerInfo g_kern_handlers[] = { - { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, - { 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu }, - { 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu }, - { 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu }, - { 0xC3000004, Restriction_Normal, SmcGetConfigKern }, - { 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking }, - { 0xC3000006, Restriction_Normal, SmcShowError }, - { 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion }, - { 0xC3000008, Restriction_Normal, SmcReadWriteRegister }, - }; - - constinit HandlerInfo g_ams_handlers[] = { - { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, - { 0xF0000201, Restriction_None, SmcIramCopy }, - { 0xF0000002, Restriction_None, SmcReadWriteRegister }, - { 0xF0000003, Restriction_None, SmcWriteAddress }, - { 0xF0000404, Restriction_None, SmcGetEmummcConfig }, - }; - - constexpr const HandlerInfo GetSecureDataHandlerInfo = { - 0x67891234, Restriction_None, SmcGetSecureData - }; - - constinit HandlerTable g_handler_tables[] = { - { g_user_handlers, util::size(g_user_handlers) }, - { g_kern_handlers, util::size(g_kern_handlers) }, - }; - - constinit HandlerTable g_ams_handler_table = { - g_ams_handlers, util::size(g_ams_handlers) - }; - - NORETURN void InvalidSmcError(u64 id) { - SetError(pkg1::ErrorInfo_UnknownSmc); - AMS_ABORT("Invalid SMC: %lx", id); - } - - const HandlerTable &GetHandlerTable(HandlerType type, u64 id) { - /* Ensure we have a valid handler type. */ - if (AMS_UNLIKELY(!(0 <= type && type < HandlerType_Count))) { - InvalidSmcError(id); - } - - /* Provide support for legacy SmcGetSecureData. */ - if (id == GetSecureDataHandlerInfo.function_id) { - return g_handler_tables[HandlerType_User]; - } - - /* Check if we're a user SMC. */ - if (type == HandlerType_User) { - /* Nintendo uses OEM SMCs. */ - /* We will assign Atmosphere extension SMCs the TrustedApplication range. */ - if (util::BitPack64{id}.Get() == SmcCallRange_TrustedApp) { - return g_ams_handler_table; - } - - /* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */ - if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) { - InvalidSmcError(id); - } - } - - return g_handler_tables[type]; - } - - const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) { - /* Provide support for legacy SmcGetSecureData. */ - if (id == GetSecureDataHandlerInfo.function_id) { - return GetSecureDataHandlerInfo; - } - - /* Get and check the index. */ - const auto index = util::BitPack64{id}.Get(); - if (AMS_UNLIKELY(index >= table.count)) { - InvalidSmcError(id); - } - - /* Get and check the handler info. */ - const auto &handler_info = table.entries[index]; - - /* Check that the handler isn't null. */ - if (AMS_UNLIKELY(handler_info.handler == nullptr)) { - InvalidSmcError(id); - } - - /* Check that the handler's id matches. */ - if (AMS_UNLIKELY(handler_info.function_id != id)) { - InvalidSmcError(id); - } - - return handler_info; - } - - bool IsHandlerRestricted(const HandlerInfo &info) { - return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0; - } - - SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) { - /* Check if the smc is restricted. */ - if (AMS_UNLIKELY(IsHandlerRestricted(info))) { - return SmcResult::NotPermitted; - } - - /* Invoke the smc. */ - return info.handler(args); - } - - constinit std::atomic g_logged = 0; - - constexpr int LogMin = 0x1000000; - constexpr int LogMax = 0x1000000; - - constexpr size_t LogBufSize = 0x5000; - - void DebugLog(SmcArguments &args) { - const int current = g_logged.fetch_add(1); - - if (current == 0) { - std::memset(MemoryRegionVirtualDebug.GetPointer(), 0xCC, LogBufSize); - } - - if (current < LogMin) { - return; - } - - const int ind = current - LogMin; - const int ofs = (ind * sizeof(args)) % LogBufSize; - - for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { - ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + ofs))[i] = reinterpret_cast(std::addressof(args))[i]; - } - - if (current >= LogMax) { - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; - - util::WaitMicroSeconds(1000); - } - - } - - } - - void HandleSmc(int type, SmcArguments &args) { - /* Get the table. */ - const auto &table = GetHandlerTable(static_cast(type), args.r[0]); - - if (std::addressof(table) == std::addressof(g_handler_tables[HandlerType_User])) { - DebugLog(args); - } - - /* Get the handler info. */ - const auto &info = GetHandlerInfo(table, args.r[0]); - - /* Set the invocation result. */ - args.r[0] = static_cast(InvokeSmcHandler(info, args)); - - if (std::addressof(table) == std::addressof(g_handler_tables[HandlerType_User])) { - DebugLog(args); - } - -/* TODO: For debugging. Remove this when exo2 is complete. */ -#if 1 - if (args.r[0] == static_cast(SmcResult::NotImplemented)) { - *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xBBBBBBBB; - *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast(info.function_id); - for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { - ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast(std::addressof(args))[i]; - } - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; - - util::WaitMicroSeconds(1000); - } - if (args.r[0] != static_cast(SmcResult::Success) && info.function_id != 0xC3000007 /* generate aes key fails during SetupKekAccessKeys */) { - *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xCCCCCCCC; - *(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast(info.function_id); - for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) { - ((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast(std::addressof(args))[i]; - } - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02; - *(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10; - - util::WaitMicroSeconds(1000); - } -#endif - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_handler.hpp b/exosphere2/program/source/smc/secmon_smc_handler.hpp deleted file mode 100644 index f051209..0000000 --- a/exosphere2/program/source/smc/secmon_smc_handler.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "secmon_smc_common.hpp" - -namespace ams::secmon::smc { - - using SmcHandler = SmcResult (*)(SmcArguments &args); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp deleted file mode 100644 index 7cf5026..0000000 --- a/exosphere2/program/source/smc/secmon_smc_info.cpp +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "../secmon_error.hpp" -#include "../secmon_misc.hpp" -#include "../secmon_page_mapper.hpp" -#include "../secmon_user_power_management.hpp" -#include "secmon_smc_info.hpp" -#include "secmon_smc_power_management.hpp" - -namespace ams::secmon::smc { - - namespace { - - struct KernelConfiguration { - /* Secure Monitor view. */ - using Flags1 = util::BitPack32::Field< 0, 8>; - using Flags0 = util::BitPack32::Field< 8, 8>; - using PhysicalMemorySize = util::BitPack32::Field<16, 2>; - - /* Kernel view, from libmesosphere. */ - using DebugFillMemory = util::BitPack32::Field<0, 1, bool>; - using EnableUserExceptionHandlers = util::BitPack32::Field; - using EnableUserPmuAccess = util::BitPack32::Field; - using IncreaseThreadResourceLimit = util::BitPack32::Field; - using Reserved4 = util::BitPack32::Field; - using UseSecureMonitorPanicCall = util::BitPack32::Field; - using Reserved9 = util::BitPack32::Field; - using MemorySize = util::BitPack32::Field; /* smc::MemorySize = pkg1::MemorySize */ - }; - - constexpr const pkg1::MemorySize DramIdToMemorySize[fuse::DramId_Count] = { - [fuse::DramId_IcosaSamsung4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_IcosaHynix4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_IcosaMicron4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_CopperSamsung4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_IcosaSamsung6GB] = pkg1::MemorySize_6GB, - [fuse::DramId_CopperHynix4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_CopperMicron4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_IowaX1X2Samsung4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_IowaSansung4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_IowaSamsung8GB] = pkg1::MemorySize_8GB, - [fuse::DramId_IowaHynix4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_IowaMicron4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_HoagSamsung4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_HoagSamsung8GB] = pkg1::MemorySize_8GB, - [fuse::DramId_HoagHynix4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_HoagMicron4GB] = pkg1::MemorySize_4GB, - [fuse::DramId_IowaSamsung4GBY] = pkg1::MemorySize_4GB, - [fuse::DramId_IowaSamsung1y4GBX] = pkg1::MemorySize_4GB, - [fuse::DramId_IowaSamsung1y8GBX] = pkg1::MemorySize_8GB, - [fuse::DramId_HoagSamsung1y4GBX] = pkg1::MemorySize_4GB, - [fuse::DramId_IowaSamsung1y4GBY] = pkg1::MemorySize_4GB, - [fuse::DramId_IowaSamsung1y8GBY] = pkg1::MemorySize_8GB, - [fuse::DramId_IowaSamsung1y4GBA] = pkg1::MemorySize_4GB, - [fuse::DramId_FiveSamsung1y8GBX] = pkg1::MemorySize_8GB, - [fuse::DramId_FiveSamsung1y4GBX] = pkg1::MemorySize_4GB, - }; - - constexpr const pkg1::MemoryMode MemoryModes[] = { - pkg1::MemoryMode_Auto, - - pkg1::MemoryMode_4GB, - pkg1::MemoryMode_4GBAppletDev, - pkg1::MemoryMode_4GBSystemDev, - - pkg1::MemoryMode_6GB, - pkg1::MemoryMode_6GBAppletDev, - - pkg1::MemoryMode_8GB, - }; - - constexpr bool IsValidMemoryMode(pkg1::MemoryMode mode) { - for (const auto known_mode : MemoryModes) { - if (mode == known_mode) { - return true; - } - } - return false; - } - - pkg1::MemoryMode SanitizeMemoryMode(pkg1::MemoryMode mode) { - if (IsValidMemoryMode(mode)) { - return mode; - } - return pkg1::MemoryMode_Auto; - } - - pkg1::MemorySize GetAvailableMemorySize(pkg1::MemorySize size) { - return std::min(GetPhysicalMemorySize(), size); - } - - pkg1::MemoryMode GetMemoryMode(pkg1::MemoryMode mode) { - /* Sanitize the mode. */ - mode = SanitizeMemoryMode(mode); - - /* If the mode is auto, construct the memory mode. */ - if (mode == pkg1::MemoryMode_Auto) { - return pkg1::MakeMemoryMode(GetPhysicalMemorySize(), pkg1::MemoryArrange_Normal); - } else { - const auto mode_size = GetMemorySize(mode); - const auto mode_arrange = GetMemoryArrange(mode); - const auto size = GetAvailableMemorySize(mode_size); - const auto arrange = (size == mode_size) ? mode_arrange : pkg1::MemoryArrange_Normal; - return pkg1::MakeMemoryMode(size, arrange); - } - } - - u32 GetMemoryMode() { - /* Unless development function is enabled, we're 4 GB. */ - u32 memory_mode = pkg1::MemoryMode_4GB; - - if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) { - memory_mode = GetMemoryMode(bcd.GetMemoryMode()); - } - - return memory_mode; - } - - u32 GetKernelConfiguration() { - pkg1::MemorySize memory_size = pkg1::MemorySize_4GB; - util::BitPack32 value = {}; - - if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) { - memory_size = GetMemorySize(GetMemoryMode(bcd.GetMemoryMode())); - - value.Set(bcd.GetKernelFlags1()); - value.Set(bcd.GetKernelFlags0()); - } - - value.Set(memory_size); - - /* Exosphere extensions. */ - const auto &sc = GetSecmonConfiguration(); - - if (!sc.DisableUserModeExceptionHandlers()) { - value.Set(true); - } - - if (sc.EnableUserModePerformanceCounterAccess()) { - value.Set(true); - } - - return value.value; - } - - SmcResult GetConfig(SmcArguments &args, bool kern) { - switch (static_cast(args.r[1])) { - case ConfigItem::DisableProgramVerification: - args.r[1] = GetBootConfig().signed_data.IsProgramVerificationDisabled(); - break; - case ConfigItem::DramId: - args.r[1] = fuse::GetDramId(); - break; - case ConfigItem::SecurityEngineInterruptNumber: - args.r[1] = SecurityEngineUserInterruptId; - break; - case ConfigItem::FuseVersion: - args.r[1] = fuse::GetExpectedFuseVersion(GetTargetFirmware()); - break; - case ConfigItem::HardwareType: - args.r[1] = fuse::GetHardwareType(); - break; - case ConfigItem::HardwareState: - args.r[1] = fuse::GetHardwareState(); - break; - case ConfigItem::IsRecoveryBoot: - args.r[1] = IsRecoveryBoot(); - break; - case ConfigItem::DeviceId: - args.r[1] = fuse::GetDeviceId(); - break; - case ConfigItem::BootReason: - { - /* This was removed in firmware 4.0.0. */ - if (GetTargetFirmware() >= TargetFirmware_4_0_0) { - return SmcResult::InvalidArgument; - } - - args.r[1] = GetDeprecatedBootReason(); - } - break; - case ConfigItem::MemoryMode: - args.r[1] = GetMemoryMode(); - break; - case ConfigItem::IsDevelopmentFunctionEnabled: - args.r[1] = GetSecmonConfiguration().IsDevelopmentFunctionEnabled(kern) || GetBootConfig().data.IsDevelopmentFunctionEnabled(); - break; - case ConfigItem::KernelConfiguration: - args.r[1] = GetKernelConfiguration(); - break; - case ConfigItem::IsChargerHiZModeEnabled: - args.r[1] = IsChargerHiZModeEnabled(); - break; - case ConfigItem::QuestState: - args.r[1] = fuse::GetQuestState(); - break; - case ConfigItem::RegulatorType: - args.r[1] = fuse::GetRegulator(); - break; - case ConfigItem::DeviceUniqueKeyGeneration: - args.r[1] = fuse::GetDeviceUniqueKeyGeneration(); - break; - case ConfigItem::Package2Hash: - { - /* Only allow getting the package2 hash in recovery boot. */ - if (!IsRecoveryBoot()) { - return SmcResult::InvalidArgument; - } - - /* Get the hash. */ - se::Sha256Hash tmp_hash; - GetPackage2Hash(std::addressof(tmp_hash)); - - /* Copy it out. */ - static_assert(sizeof(args) - sizeof(args.r[0]) >= sizeof(tmp_hash)); - std::memcpy(std::addressof(args.r[1]), std::addressof(tmp_hash), sizeof(tmp_hash)); - } - break; - case ConfigItem::ExosphereApiVersion: - /* Get information about the current exosphere version. */ - args.r[1] = (static_cast(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) | - (static_cast(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) | - (static_cast(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) | - (static_cast(GetKeyGeneration()) << 32) | - (static_cast(GetTargetFirmware()) << 00); - break; - case ConfigItem::ExosphereNeedsReboot: - /* We are executing, so we aren't in the process of rebooting. */ - args.r[1] = 0; - break; - case ConfigItem::ExosphereNeedsShutdown: - /* We are executing, so we aren't in the process of shutting down. */ - args.r[1] = 0; - break; - case ConfigItem::ExosphereGitCommitHash: - /* Get information about the current exosphere git commit hash. */ - args.r[1] = ATMOSPHERE_GIT_HASH; - break; - case ConfigItem::ExosphereHasRcmBugPatch: - /* Get information about whether this unit has the RCM bug patched. */ - args.r[1] = fuse::HasRcmVulnerabilityPatch(); - break; - case ConfigItem::ExosphereBlankProdInfo: - /* Get whether this unit should simulate a "blanked" PRODINFO. */ - args.r[1] = GetSecmonConfiguration().ShouldUseBlankCalibrationBinary(); - break; - case ConfigItem::ExosphereAllowCalWrites: - /* Get whether this unit should allow writing to the calibration partition. */ - args.r[1] = (GetEmummcConfiguration().IsEmummcActive() || GetSecmonConfiguration().AllowWritingToCalibrationBinarySysmmc()); - break; - default: - return SmcResult::InvalidArgument; - } - - return SmcResult::Success; - } - - SmcResult SetConfig(SmcArguments &args) { - const auto soc_type = GetSocType(); - - switch (static_cast(args.r[1])) { - case ConfigItem::IsChargerHiZModeEnabled: - /* Configure the HiZ mode. */ - SetChargerHiZModeEnabled(static_cast(args.r[3])); - break; - case ConfigItem::ExosphereNeedsReboot: - if (soc_type == fuse::SocType_Erista) { - switch (static_cast(args.r[3])) { - case UserRebootType_None: - break; - case UserRebootType_ToRcm: - PerformUserRebootToRcm(); - break; - case UserRebootType_ToPayload: - PerformUserRebootToPayload(); - break; - default: - return SmcResult::InvalidArgument; - } - } else /* if (soc_type == fuse::SocType_Mariko) */ { - return SmcResult::NotImplemented; - } - break; - case ConfigItem::ExosphereNeedsShutdown: - if (soc_type == fuse::SocType_Erista) { - if (args.r[3] != 0) { - PerformUserShutDown(); - } - } else /* if (soc_type == fuse::SocType_Mariko) */ { - return SmcResult::NotImplemented; - } - break; - default: - return SmcResult::InvalidArgument; - } - - return SmcResult::Success; - } - - } - - SmcResult SmcGetConfigUser(SmcArguments &args) { - return GetConfig(args, false); - } - - SmcResult SmcGetConfigKern(SmcArguments &args) { - return GetConfig(args, true); - } - - SmcResult SmcSetConfig(SmcArguments &args) { - return SetConfig(args); - } - - /* This is an atmosphere extension smc. */ - SmcResult SmcGetEmummcConfig(SmcArguments &args) { - /* Decode arguments. */ - const auto mmc = static_cast(args.r[1]); - const uintptr_t user_address = args.r[2]; - const uintptr_t user_offset = user_address % 4_KB; - - /* Validate arguments. */ - /* NOTE: In the future, configuration for non-NAND storage may be implemented. */ - SMC_R_UNLESS(mmc == EmummcMmc_Nand, NotImplemented); - SMC_R_UNLESS(user_offset + 2 * sizeof(EmummcFilePath) <= 4_KB, InvalidArgument); - - /* Get the emummc config. */ - const auto &cfg = GetEmummcConfiguration(); - static_assert(sizeof(cfg.file_cfg) == sizeof(EmummcFilePath)); - static_assert(sizeof(cfg.emu_dir_path) == sizeof(EmummcFilePath)); - - /* Clear the output. */ - constexpr size_t InlineOutputSize = sizeof(args) - sizeof(args.r[0]); - u8 * const inline_output = static_cast(static_cast(std::addressof(args.r[1]))); - std::memset(inline_output, 0, InlineOutputSize); - - /* Copy out the configuration. */ - { - /* Map the user output page. */ - AtmosphereUserPageMapper mapper(user_address); - SMC_R_UNLESS(mapper.Map(), InvalidArgument); - - /* Copy the base configuration. */ - static_assert(sizeof(cfg.base_cfg) <= InlineOutputSize); - std::memcpy(inline_output, std::addressof(cfg.base_cfg), sizeof(cfg.base_cfg)); - - /* Copy out type-specific data. */ - switch (cfg.base_cfg.type) { - case EmummcType_None: - /* No additional configuration needs to be copied. */ - break; - case EmummcType_Partition: - /* Copy the partition config. */ - static_assert(sizeof(cfg.base_cfg) + sizeof(cfg.partition_cfg) <= InlineOutputSize); - std::memcpy(inline_output + sizeof(cfg.base_cfg), std::addressof(cfg.partition_cfg), sizeof(cfg.partition_cfg)); - break; - case EmummcType_File: - /* Copy the file config. */ - SMC_R_UNLESS(mapper.CopyToUser(user_address, std::addressof(cfg.file_cfg), sizeof(cfg.file_cfg)), InvalidArgument); - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Copy the redirection directory path to the user page. */ - SMC_R_UNLESS(mapper.CopyToUser(user_address + sizeof(EmummcFilePath), std::addressof(cfg.emu_dir_path), sizeof(cfg.emu_dir_path)), InvalidArgument); - } - - return SmcResult::Success; - } - - /* For exosphere's usage. */ - pkg1::MemorySize GetPhysicalMemorySize() { - const auto dram_id = fuse::GetDramId(); - AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count); - return DramIdToMemorySize[dram_id]; - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_info.hpp b/exosphere2/program/source/smc/secmon_smc_info.hpp deleted file mode 100644 index d45bf57..0000000 --- a/exosphere2/program/source/smc/secmon_smc_info.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "secmon_smc_common.hpp" - -namespace ams::secmon::smc { - - enum class ConfigItem : u32 { - /* Standard config items. */ - DisableProgramVerification = 1, - DramId = 2, - SecurityEngineInterruptNumber = 3, - FuseVersion = 4, - HardwareType = 5, - HardwareState = 6, - IsRecoveryBoot = 7, - DeviceId = 8, - BootReason = 9, - MemoryMode = 10, - IsDevelopmentFunctionEnabled = 11, - KernelConfiguration = 12, - IsChargerHiZModeEnabled = 13, - QuestState = 14, - RegulatorType = 15, - DeviceUniqueKeyGeneration = 16, - Package2Hash = 17, - - /* Extension config items for exosphere. */ - ExosphereApiVersion = 65000, - ExosphereNeedsReboot = 65001, - ExosphereNeedsShutdown = 65002, - ExosphereGitCommitHash = 65003, - ExosphereHasRcmBugPatch = 65004, - ExosphereBlankProdInfo = 65005, - ExosphereAllowCalWrites = 65006, - }; - - SmcResult SmcGetConfigUser(SmcArguments &args); - SmcResult SmcGetConfigKern(SmcArguments &args); - SmcResult SmcSetConfig(SmcArguments &args); - - /* This is an atmosphere extension smc. */ - SmcResult SmcGetEmummcConfig(SmcArguments &args); - - /* For other parts of exosphere. */ - pkg1::MemorySize GetPhysicalMemorySize(); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp deleted file mode 100644 index dba249c..0000000 --- a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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_page_mapper.hpp" -#include "secmon_smc_memory_access.hpp" - -namespace ams::secmon::smc { - - namespace { - - enum IramCopyType { - IramCopyType_FromIramToDram = 0, - IramCopyType_FromDramToIram = 1, - IramCopyType_Count, - }; - - struct IramCopyOption { - using CopyType = util::BitPack32::Field<0, 1, IramCopyType>; - }; - - } - - /* This is an atmosphere extension smc. */ - SmcResult SmcIramCopy(SmcArguments &args) { - /* Decode arguments. */ - const uintptr_t dram_address = args.r[1]; - const uintptr_t iram_address = args.r[2]; - const size_t size = args.r[3]; - const util::BitPack32 option = { static_cast(args.r[4]) }; - - const auto copy_type = option.Get(); - - /* Validate arguments. */ - SMC_R_UNLESS(copy_type < IramCopyType_Count, InvalidArgument); - - { - /* Map the pages. */ - AtmosphereUserPageMapper dram_mapper(dram_address); - AtmosphereIramPageMapper iram_mapper(iram_address); - SMC_R_UNLESS(dram_mapper.Map(), InvalidArgument); - SMC_R_UNLESS(iram_mapper.Map(), InvalidArgument); - - /* Get the ranges we're copying. */ - const void * const src = (copy_type == IramCopyType_FromIramToDram) ? iram_mapper.GetPointerTo(iram_address, size) : dram_mapper.GetPointerTo(dram_address, size); - void * const dst = (copy_type == IramCopyType_FromIramToDram) ? dram_mapper.GetPointerTo(dram_address, size) : iram_mapper.GetPointerTo(iram_address, size); - SMC_R_UNLESS(src != nullptr, InvalidArgument); - SMC_R_UNLESS(dst != nullptr, InvalidArgument); - - /* Copy the data. */ - std::memcpy(dst, src, size); - } - - return SmcResult::Success; - } - - SmcResult SmcWriteAddress(SmcArguments &args) { - /* NOTE: This smc was deprecated in Atmosphère 0.13.0. */ - return SmcResult::NotImplemented; - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.hpp b/exosphere2/program/source/smc/secmon_smc_memory_access.hpp deleted file mode 100644 index 3106576..0000000 --- a/exosphere2/program/source/smc/secmon_smc_memory_access.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "secmon_smc_common.hpp" - -namespace ams::secmon::smc { - - /* This is an atmosphere extension smc. */ - SmcResult SmcIramCopy(SmcArguments &args); - SmcResult SmcWriteAddress(SmcArguments &args); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp deleted file mode 100644 index 93f0538..0000000 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ /dev/null @@ -1,501 +0,0 @@ -/* - * 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_cpu_context.hpp" -#include "../secmon_error.hpp" -#include "../secmon_misc.hpp" -#include "secmon_smc_power_management.hpp" -#include "secmon_smc_se_lock.hpp" - -#include "sc7fw_bin.h" - -namespace ams::secmon { - - /* Declare assembly functionality. */ - void *GetCoreExceptionStackVirtual(); - -} - -namespace ams::secmon::smc { - - /* Declare assembly power-management functionality. */ - void PivotStackAndInvoke(void *stack, void (*function)()); - void FinalizePowerOff(); - - namespace { - - constexpr inline const uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); - constexpr inline const uintptr_t APB_MISC = MemoryRegionVirtualDeviceApbMisc.GetAddress(); - constexpr inline const uintptr_t GPIO = MemoryRegionVirtualDeviceGpio.GetAddress(); - constexpr inline const uintptr_t CLK_RST = MemoryRegionVirtualDeviceClkRst.GetAddress(); - constexpr inline const uintptr_t EVP = secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress(); - constexpr inline const uintptr_t FLOW_CTLR = MemoryRegionVirtualDeviceFlowController.GetAddress(); - - constexpr inline uintptr_t CommonSmcStackTop = MemoryRegionVirtualTzramVolatileData.GetEndAddress() - (0x80 * (NumCores - 1)); - - enum PowerStateType { - PowerStateType_StandBy = 0, - PowerStateType_PowerDown = 1, - }; - - enum PowerStateId { - PowerStateId_Sc7 = 27, - }; - - /* http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_Coordination_Interface_PDD_v1_1_DEN0022D.pdf Page 46 */ - struct SuspendCpuPowerState { - using StateId = util::BitPack32::Field< 0, 16, PowerStateId>; - using StateType = util::BitPack32::Field<16, 1, PowerStateType>; - using PowerLevel = util::BitPack32::Field<24, 2, u32>; - }; - - constinit bool g_charger_hi_z_mode_enabled = false; - - constinit const reg::BitsMask CpuPowerGateStatusMasks[NumCores] = { - PMC_REG_BITS_MASK(PWRGATE_STATUS_CE0), - PMC_REG_BITS_MASK(PWRGATE_STATUS_CE1), - PMC_REG_BITS_MASK(PWRGATE_STATUS_CE2), - PMC_REG_BITS_MASK(PWRGATE_STATUS_CE3), - }; - - constinit const APBDEV_PMC_PWRGATE_TOGGLE_PARTID CpuPowerGateTogglePartitionIds[NumCores] = { - APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0, - APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE1, - APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE2, - APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE3, - }; - - bool IsCpuPoweredOn(const reg::BitsMask mask) { - return reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, REG_BITS_VALUE_FROM_MASK(mask, APBDEV_PMC_PWRGATE_STATUS_STATUS_ON)); - } - - void PowerOnCpu(const reg::BitsMask mask, u32 toggle_partid) { - /* If the cpu is already on, we have nothing to do. */ - if (IsCpuPoweredOn(mask)) { - return; - } - - /* Wait until nothing is being powergated. */ - int timeout = 5000; - while (true) { - if (reg::HasValue(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_START, DISABLE))) { - break; - } - - util::WaitMicroSeconds(1); - if ((--timeout) < 0) { - /* NOTE: Nintendo doesn't do any error handling here... */ - return; - } - } - - /* Toggle on the cpu partition. */ - reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM (PWRGATE_TOGGLE_START, ENABLE), - PMC_REG_BITS_VALUE(PWRGATE_TOGGLE_PARTID, toggle_partid)); - - /* Wait up to 5000 us for the powergate to complete. */ - timeout = 5000; - while (true) { - if (IsCpuPoweredOn(mask)) { - break; - } - - util::WaitMicroSeconds(1); - if ((--timeout) < 0) { - /* NOTE: Nintendo doesn't do any error handling here... */ - return; - } - } - } - - void ResetCpu(int which_core) { - reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */ - REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */ - } - - void StartCpu(int which_core) { - reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */ - REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */ - } - - void PowerOffCpu() { - /* Get the current core id. */ - const auto core_id = hw::GetCurrentCoreId(); - - /* Configure the flow controller to prepare for shutting down the current core. */ - flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_POWERGATE_CPU_ONLY); - flow::SetHaltCpuEvents(core_id, false); - flow::SetCc4Ctrl(core_id, 0); - - /* Save the core's context for restoration on next power-on. */ - SaveDebugRegisters(); - SetCoreOff(); - - /* Ensure there are no pending memory transactions prior to our power-down. */ - FlushEntireDataCache(); - - /* Finalize our powerdown and wait for an interrupt. */ - FinalizePowerOff(); - } - - void ValidateSocStateForSuspend() { - /* Validate that all other cores are off. */ - AMS_ABORT_UNLESS(reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_VALUE(PWRGATE_STATUS_CE123, 0))); - - /* Validate that the bpmp is appropriately halted. */ - AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) == reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), - FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, IsJtagEnabled(), ENABLED, DISABLED))); - - /* TODO */ - } - - void GenerateCryptographicallyRandomBytes(void * const dst, int size) { - /* Flush the region we're about to fill to ensure consistency with the SE. */ - hw::FlushDataCache(dst, size); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Generate random bytes. */ - se::GenerateRandomBytes(dst, size); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Flush to ensure the CPU sees consistent data for the region. */ - hw::FlushDataCache(dst, size); - hw::DataSynchronizationBarrierInnerShareable(); - } - - void SaveSecureContextForErista() { - /* Generate a random key source. */ - util::AlignedBuffer key_source; - GenerateCryptographicallyRandomBytes(key_source, se::AesBlockSize); - - const u32 * const key_source_32 = reinterpret_cast(static_cast(key_source)); - - /* Ensure that the key source registers are not locked. */ - AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) != pmc::LockState::Locked); - - /* Write the key source, lock writes to the key source, and verify that the key source is write-locked. */ - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH24, key_source_32[0]); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH25, key_source_32[1]); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH26, key_source_32[2]); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH27, key_source_32[3]); - pmc::LockSecureRegister(pmc::SecureRegister_KeySourceWrite); - AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceWrite) == pmc::LockState::Locked); - - /* Verify the key source is correct in registers, and read-lock the key source registers. */ - AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH24) == key_source_32[0]); - AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH25) == key_source_32[1]); - AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH26) == key_source_32[2]); - AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH27) == key_source_32[3]); - pmc::LockSecureRegister(pmc::SecureRegister_KeySourceRead); - - /* Ensure that the key source registers are locked. */ - AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) == pmc::LockState::Locked); - - /* Generate a random kek into keyslot 2. */ - se::SetRandomKey(pkg1::AesKeySlot_TzramSaveKek); - - /* Verify that the se is in a validate state, context save, and validate again. */ - { - se::ValidateErrStatus(); - ON_SCOPE_EXIT { se::ValidateErrStatus(); }; - - { - /* Transition to non-secure mode for the duration of the context save operation. */ - se::SetSecure(false); - ON_SCOPE_EXIT { se::SetSecure(true); }; - - /* Get a pointer to the context storage. */ - se::Context * const context = MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetPointer(); - static_assert(MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetSize() == sizeof(*context)); - - /* Save the context. */ - se::SaveContext(context); - - /* Ensure that the cpu sees consistent data. */ - hw::FlushDataCache(context, sizeof(*context)); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Write the context pointer to pmc scratch, so that the bootrom will restore it on wake. */ - reg::Write(PMC + APBDEV_PMC_SCRATCH43, MemoryRegionPhysicalDramSecureDataStoreSecurityEngineState.GetAddress()); - } - } - - /* Clear keyslot 3, and then derive the save key. */ - se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); - se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, se::AesBlockSize); - - /* Declare a temporary block to be used as both iv and mac. */ - u32 temp_block[se::AesBlockSize / sizeof(u32)] = {}; - - /* Ensure that the SE sees consistent data for tzram. */ - const void * const tzram_save_src = MemoryRegionVirtualTzramReadOnlyAlias.GetPointer() + MemoryRegionVirtualTzramVolatileData.GetSize() + MemoryRegionVirtualTzramVolatileStack.GetSize(); - void * const tzram_save_dst = MemoryRegionVirtualIramSc7Work.GetPointer(); - constexpr size_t TzramSaveSize = MemoryRegionVirtualDramSecureDataStoreTzram.GetSize(); - - hw::FlushDataCache(tzram_save_src, TzramSaveSize); - hw::FlushDataCache(tzram_save_dst, TzramSaveSize); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Encrypt tzram using our random key. */ - se::EncryptAes256Cbc(tzram_save_dst, TzramSaveSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize, temp_block, se::AesBlockSize); - hw::FlushDataCache(tzram_save_dst, TzramSaveSize); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Copy the data from work space to the secure storage destination. */ - void * const tzram_store_dst = MemoryRegionVirtualDramSecureDataStoreTzram.GetPointer(); - std::memcpy(tzram_store_dst, tzram_save_dst, TzramSaveSize); - hw::FlushDataCache(tzram_store_dst, TzramSaveSize); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Compute cmac of tzram into our temporary block. */ - se::ComputeAes256Cmac(temp_block, se::AesBlockSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize); - - /* Ensure that the cmac registers are not locked. */ - AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) != pmc::LockState::Locked); - - /* Write the cmac, lock writes to the cmac, and verify that the cmac is write-locked. */ - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH112, temp_block[0]); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH113, temp_block[1]); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH114, temp_block[2]); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH115, temp_block[3]); - pmc::LockSecureRegister(pmc::SecureRegister_CmacWrite); - AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacWrite) == pmc::LockState::Locked); - - /* Verify the key source is correct in registers, and read-lock the key source registers. */ - AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH112) == temp_block[0]); - AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH113) == temp_block[1]); - AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH114) == temp_block[2]); - AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH115) == temp_block[3]); - pmc::LockSecureRegister(pmc::SecureRegister_CmacRead); - - /* Ensure that the key source registers are locked. */ - AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) == pmc::LockState::Locked); - } - - void SaveSecureContextForMariko() { - /* TODO */ - } - - void SaveSecureContext() { - const auto soc_type = GetSocType(); - if (soc_type == fuse::SocType_Erista) { - SaveSecureContextForErista(); - } else /* if (soc_type == fuse::SocType_Mariko) */ { - SaveSecureContextForMariko(); - } - } - - void LoadAndStartSc7BpmpFirmware() { - /* Set the PMC as insecure, so that the BPMP firmware can access it. */ - reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, DISABLE)); - - /* Set the exception vectors for the bpmp. RESET should point to RESET, all others should point to generic exception/panic. */ - constexpr const u32 Sc7FirmwareResetVector = static_cast(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x0); - constexpr const u32 Sc7FirmwarePanicVector = static_cast(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x4); - - reg::Write(EVP + EVP_COP_RESET_VECTOR, Sc7FirmwareResetVector); - reg::Write(EVP + EVP_COP_UNDEF_VECTOR, Sc7FirmwarePanicVector); - reg::Write(EVP + EVP_COP_SWI_VECTOR, Sc7FirmwarePanicVector); - reg::Write(EVP + EVP_COP_PREFETCH_ABORT_VECTOR, Sc7FirmwarePanicVector); - reg::Write(EVP + EVP_COP_DATA_ABORT_VECTOR, Sc7FirmwarePanicVector); - reg::Write(EVP + EVP_COP_RSVD_VECTOR, Sc7FirmwarePanicVector); - reg::Write(EVP + EVP_COP_IRQ_VECTOR, Sc7FirmwarePanicVector); - reg::Write(EVP + EVP_COP_FIQ_VECTOR, Sc7FirmwarePanicVector); - - /* Disable activity monitor bpmp monitoring, so that we don't panic upon bpmp wake. */ - actmon::StopMonitoringBpmp(); - - /* Set BPMP reset. */ - reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SET_SET_COP_RST, ENABLE)); - - /* Load the bpmp firmware. */ - void * const sc7fw_load_address = MemoryRegionVirtualIramSc7Firmware.GetPointer(); - std::memcpy(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size); - hw::FlushDataCache(sc7fw_load_address, sc7fw_bin_size); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Ensure that the bpmp firmware was loaded. */ - AMS_ABORT_UNLESS(crypto::IsSameBytes(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size)); - - /* Clear BPMP reset. */ - reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_COP_RST, ENABLE)); - - /* Start the bpmp. */ - reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_NONE)); - } - - void SaveSecureContextAndSuspend() { - /* Ensure there are no pending memory transactions before we continue */ - FlushEntireDataCache(); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Save all secure context (security engine state + tzram). */ - SaveSecureContext(); - - /* Load and start the sc7 firmware on the bpmp. */ - LoadAndStartSc7BpmpFirmware(); - - /* Log our suspension. */ - /* NOTE: Nintendo only does this on dev, but we will always do it. */ - if (true /* !pkg1::IsProduction() */) { - log::SendText("OYASUMI\n", 8); - } - - /* If we're on erista, configure the bootrom to allow our custom warmboot firmware. */ - if (GetSocType() == fuse::SocType_Erista) { - reg::Write(PMC + APBDEV_PMC_SCRATCH31, 0x2202E012); - reg::Write(PMC + APBDEV_PMC_SCRATCH32, 0x6001DC28); - } - - /* Finalize our powerdown and wait for an interrupt. */ - FinalizePowerOff(); - } - - SmcResult SuspendCpuImpl(SmcArguments &args) { - /* Decode arguments. */ - const util::BitPack32 power_state = { static_cast(args.r[1]) }; - const uintptr_t entry_point = args.r[2]; - const uintptr_t context_id = args.r[3]; - - const auto state_type = power_state.Get(); - const auto state_id = power_state.Get(); - - const auto core_id = hw::GetCurrentCoreId(); - - /* Validate arguments. */ - SMC_R_UNLESS(state_type == PowerStateType_PowerDown, PsciDenied); - SMC_R_UNLESS(state_id == PowerStateId_Sc7, PsciDenied); - - /* Orchestrate charger transition to Hi-Z mode if needed. */ - if (IsChargerHiZModeEnabled()) { - /* Ensure we can do comms over i2c-1. */ - clkrst::EnableI2c1Clock(); - - /* If the charger isn't in hi-z mode, perform a transition. */ - if (!charger::IsHiZMode()) { - charger::EnterHiZMode(); - - /* Wait up to 50ms for the transition to complete. */ - const auto start_time = util::GetMicroSeconds(); - auto current_time = start_time; - while ((current_time - start_time) <= 50'000) { - if (auto intr_status = reg::Read(GPIO + 0x634); (intr_status & 1) == 0) { - /* Wait 256 us to ensure the transition completes. */ - util::WaitMicroSeconds(256); - break; - } - current_time = util::GetMicroSeconds(); - } - } - - /* Disable i2c-1, since we're done communicating over it. */ - clkrst::DisableI2c1Clock(); - } - - /* Enable wake event detection. */ - pmc::EnableWakeEventDetection(); - - /* Ensure that i2c-5 is usable for communicating with the pmic. */ - clkrst::EnableI2c5Clock(); - i2c::Initialize(i2c::Port_5); - - /* Orchestrate sleep entry with the pmic. */ - pmic::EnableSleep(); - - /* Ensure that the soc is in a state valid for us to suspend. */ - ValidateSocStateForSuspend(); - - /* Configure the pmc for sc7 entry. */ - pmc::ConfigureForSc7Entry(); - - /* Configure the flow controller for sc7 entry. */ - flow::SetCc4Ctrl(core_id, 0); - flow::SetHaltCpuEvents(core_id, false); - flow::ClearL2FlushControl(); - flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_POWERGATE_CPU_TURNOFF_CPURAIL); - - /* Save the entry context. */ - SetEntryContext(core_id, entry_point, context_id); - - /* Configure the cpu context for reset. */ - SaveDebugRegisters(); - SetCoreOff(); - SetResetExpected(true); - - /* Switch to use the common smc stack (all other cores are off), and perform suspension. */ - PivotStackAndInvoke(reinterpret_cast(CommonSmcStackTop), SaveSecureContextAndSuspend); - - /* This code will never be reached. */ - __builtin_unreachable(); - } - - } - - SmcResult SmcPowerOffCpu(SmcArguments &args) { - /* Get the current core id. */ - const auto core_id = hw::GetCurrentCoreId(); - - /* Note that we're expecting a reset for the current core. */ - SetResetExpected(true); - - /* If we're on the final core, shut down directly. Otherwise, invoke with special stack. */ - if (core_id == NumCores - 1) { - PowerOffCpu(); - } else { - PivotStackAndInvoke(GetCoreExceptionStackVirtual(), PowerOffCpu); - } - - /* This code will never be reached. */ - __builtin_unreachable(); - } - - SmcResult SmcPowerOnCpu(SmcArguments &args) { - /* Get and validate the core to power on. */ - const int which_core = args.r[1]; - SMC_R_UNLESS(0 <= which_core && which_core < NumCores, PsciInvalidParameters); - - /* Ensure the core isn't already on. */ - SMC_R_UNLESS(!IsCoreOn(which_core), PsciAlreadyOn); - - /* Save the entry context. */ - SetEntryContext(which_core, args.r[2], args.r[3]); - - /* Reset the cpu. */ - ResetCpu(which_core); - - /* Turn on the core. */ - PowerOnCpu(CpuPowerGateStatusMasks[which_core], CpuPowerGateTogglePartitionIds[which_core]); - - /* Start the core. */ - StartCpu(which_core); - - return SmcResult::PsciSuccess; - } - - SmcResult SmcSuspendCpu(SmcArguments &args) { - return LockSecurityEngineAndInvoke(args, SuspendCpuImpl); - } - - bool IsChargerHiZModeEnabled() { - return g_charger_hi_z_mode_enabled; - } - - void SetChargerHiZModeEnabled(bool en) { - g_charger_hi_z_mode_enabled = en; - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.hpp b/exosphere2/program/source/smc/secmon_smc_power_management.hpp deleted file mode 100644 index 65bd53d..0000000 --- a/exosphere2/program/source/smc/secmon_smc_power_management.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "secmon_smc_common.hpp" - -namespace ams::secmon::smc { - - SmcResult SmcPowerOffCpu(SmcArguments &args); - SmcResult SmcPowerOnCpu(SmcArguments &args); - - SmcResult SmcSuspendCpu(SmcArguments &args); - - bool IsChargerHiZModeEnabled(); - void SetChargerHiZModeEnabled(bool en); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_random.cpp b/exosphere2/program/source/smc/secmon_smc_random.cpp deleted file mode 100644 index 6b3099a..0000000 --- a/exosphere2/program/source/smc/secmon_smc_random.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "../secmon_error.hpp" -#include "secmon_smc_random.hpp" -#include "secmon_random_cache.hpp" -#include "secmon_smc_se_lock.hpp" - -namespace ams::secmon::smc { - - namespace { - - SmcResult GenerateRandomBytesImpl(SmcArguments &args) { - /* Validate the input size. */ - const size_t size = args.r[1]; - SMC_R_UNLESS(size <= MaxRandomBytes, InvalidArgument); - - /* Create a buffer that the se can generate bytes into. */ - util::AlignedBuffer buffer; - hw::FlushDataCache(buffer, size); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Generate random bytes into the buffer. */ - se::GenerateRandomBytes(buffer, size); - - /* Ensure that the cpu sees consistent data. */ - hw::DataSynchronizationBarrierInnerShareable(); - hw::FlushDataCache(buffer, size); - hw::DataSynchronizationBarrierInnerShareable(); - - /* Copy the bytes to output. */ - std::memcpy(std::addressof(args.r[1]), buffer, size); - return SmcResult::Success; - } - - } - - SmcResult SmcGenerateRandomBytes(SmcArguments &args) { - return LockSecurityEngineAndInvoke(args, GenerateRandomBytesImpl); - } - - SmcResult SmcGenerateRandomBytesNonBlocking(SmcArguments &args) { - /* Try to lock the security engine, so that we can call the standard impl. */ - if (TryLockSecurityEngine()) { - /* Ensure we unlock the security engine when done. */ - ON_SCOPE_EXIT { UnlockSecurityEngine(); }; - - /* Take advantage of our lock to refill lthe random cache. */ - ON_SCOPE_EXIT { RefillRandomCache(); }; - - /* If we lock it successfully, we can just call the blocking impl. */ - return GenerateRandomBytesImpl(args); - } else { - /* Otherwise, we'll retrieve some bytes from the cache. */ - const size_t size = args.r[1]; - SMC_R_UNLESS(size <= MaxRandomBytes, InvalidArgument); - - /* Get random bytes from the cache. */ - GetRandomFromCache(std::addressof(args.r[1]), size); - return SmcResult::Success; - } - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_random.hpp b/exosphere2/program/source/smc/secmon_smc_random.hpp deleted file mode 100644 index 98c2990..0000000 --- a/exosphere2/program/source/smc/secmon_smc_random.hpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "secmon_smc_common.hpp" - -namespace ams::secmon::smc { - - SmcResult SmcGenerateRandomBytes(SmcArguments &args); - SmcResult SmcGenerateRandomBytesNonBlocking(SmcArguments &args); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.cpp b/exosphere2/program/source/smc/secmon_smc_register_access.cpp deleted file mode 100644 index cdf8343..0000000 --- a/exosphere2/program/source/smc/secmon_smc_register_access.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "../secmon_error.hpp" -#include "secmon_smc_register_access.hpp" - -namespace ams::secmon::smc { - - namespace { - - template - constexpr void SetRegisterTableAllowed(std::array &arr, uintptr_t reg) { - /* All registers should be four byte aligned. */ - AMS_ASSUME(reg % sizeof(u32) == 0); - - /* Reduce the register to an index. */ - reg /= sizeof(u32); - - /* Get the index and mask. */ - const auto index = reg / BITSIZEOF(u8); - const auto mask = (1u << (reg % BITSIZEOF(u8))); - - /* Check that the permission bit isn't already set. */ - AMS_ASSUME((arr[index] & mask) == 0); - - /* Set the permission bit. */ - arr[index] |= mask; - - /* Ensure that indices are set in sorted order. */ - for (auto i = (reg % BITSIZEOF(u8)) + 1; i < 8; ++i) { - AMS_ASSUME((arr[index] & (1u << i)) == 0); - } - - for (auto i = index + 1; i < arr.size(); ++i) { - AMS_ASSUME(arr[i] == 0); - } - } - - template - consteval std::pair GetReducedAccessTableInfo(const std::array &arr) { - for (int last = arr.size() - 1; last >= 0; --last) { - if (arr[last] != 0) { - const int end = last + 1; - for (int start = 0; start < end; ++start) { - if (arr[start] != 0) { - return std::make_pair(static_cast(start), static_cast(end)); - } - } - return std::make_pair(static_cast(0), static_cast(end)); - } - } - - /* All empty perm table is disallowed. */ - AMS_ASSUME(false); - } - - - template - struct AccessTable { - static constexpr inline auto ReducedAccessTableInfo = GetReducedAccessTableInfo(RawTable); - static constexpr inline size_t ReducedAccessTableSize = ReducedAccessTableInfo.second - ReducedAccessTableInfo.first; - static constexpr inline auto ReducedAccessTable = []() -> std::array { - std::array reduced = {}; - - for (size_t i = ReducedAccessTableInfo.first; i < ReducedAccessTableInfo.second; ++i) { - reduced[i - ReducedAccessTableInfo.first] = RawTable[i]; - } - - return reduced; - }(); - - static constexpr u32 Address = _Address + (ReducedAccessTableInfo.first * sizeof(u32) * BITSIZEOF(u8)); - static constexpr u32 Size = static_cast(ReducedAccessTableSize * sizeof(u32) * BITSIZEOF(u8)); - - static_assert(Size <= 0x1000); - }; - - struct AccessTableEntry { - const u8 * const table; - uintptr_t virtual_address; - u32 address; - u32 size; - }; - - /* Include the access tables. */ - #include "secmon_define_pmc_access_table.inc" - #include "secmon_define_mc_access_table.inc" - #include "secmon_define_mc01_access_table.inc" - - constexpr const AccessTableEntry AccessTables[] = { - { PmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDevicePmc.GetAddress(), PmcAccessTable::Address, PmcAccessTable::Size, }, - { McAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController.GetAddress(), McAccessTable::Address, McAccessTable::Size, }, - { Mc01AccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController0.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController0.GetAddress(), Mc01AccessTable::Size, }, - { Mc01AccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController1.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController1.GetAddress(), Mc01AccessTable::Size, }, - }; - - constexpr bool IsAccessAllowed(const AccessTableEntry &entry, uintptr_t address) { - /* Check if the address is within range. */ - if (!(entry.address <= address && address < entry.address + entry.size)) { - return false; - } - - /* Get the offset. */ - const auto offset = address - entry.address; - - /* Convert it to an index. */ - const auto reg_index = offset / sizeof(u32); - - /* Get the bit fields. */ - const auto index = reg_index / BITSIZEOF(u8); - const auto mask = (1u << (reg_index % BITSIZEOF(u8))); - - /* Validate that we're not going out of bounds. */ - if (index >= entry.size / sizeof(u32)) { - return false; - } - - return (entry.table[index] & mask) != 0; - } - - constexpr const AccessTableEntry *GetAccessTableEntry(uintptr_t address) { - for (const auto &entry : AccessTables) { - if (IsAccessAllowed(entry, address)) { - return std::addressof(entry); - } - } - - return nullptr; - } - - } - - SmcResult SmcReadWriteRegister(SmcArguments &args) { - /* Get the arguments. */ - const uintptr_t address = args.r[1]; - const u32 mask = args.r[2]; - const u32 value = args.r[3]; - - /* Validate that the address is aligned. */ - SMC_R_UNLESS(util::IsAligned(address, alignof(u32)), InvalidArgument); - - /* Find the access table. */ - const AccessTableEntry * const entry = GetAccessTableEntry(address); - - /* If we have a table, perform the write. */ - if (entry != nullptr) { - /* Get the address to read or write. */ - const uintptr_t virtual_address = entry->virtual_address + (address - entry->address); - u32 out = 0; - - if (mask != ~static_cast(0)) { - out = reg::Read(virtual_address); - } - if (mask != static_cast(0)) { - reg::Write(virtual_address, (out & ~mask) | (value & mask)); - } - - args.r[1] = out; - } else { - /* For no clearly discernable reason, SmcReadWriteRegister returns success despite not doing the read/write */ - /* when accessing the SMMU controls for the BPMP and for APB-DMA. */ - /* This is "probably" to fuck with hackers who got access to the SMC and are trying to get control of the */ - /* BPMP to exploit jamais vu, deja vu, or other related DMA/wake-from-sleep vulnerabilities. */ - constexpr uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress(); - SMC_R_UNLESS((address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)), InvalidArgument); - } - - return SmcResult::Success; - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.hpp b/exosphere2/program/source/smc/secmon_smc_register_access.hpp deleted file mode 100644 index 6a74cdc..0000000 --- a/exosphere2/program/source/smc/secmon_smc_register_access.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "secmon_smc_common.hpp" - -namespace ams::secmon::smc { - - SmcResult SmcReadWriteRegister(SmcArguments &args); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_result.cpp b/exosphere2/program/source/smc/secmon_smc_result.cpp deleted file mode 100644 index 246f50c..0000000 --- a/exosphere2/program/source/smc/secmon_smc_result.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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_page_mapper.hpp" -#include "secmon_smc_result.hpp" - -namespace ams::secmon::smc { - - namespace { - - constinit u64 g_async_key = InvalidAsyncKey; - constinit GetResultHandler g_async_handler = nullptr; - - u64 GenerateRandomU64() { - /* NOTE: This is one of the only places where Nintendo does not do data flushing. */ - /* to ensure coherency when doing random byte generation. */ - /* It is not clear why it is necessary elsewhere but not here. */ - /* TODO: Figure out why. */ - u64 v; - se::GenerateRandomBytes(std::addressof(v), sizeof(v)); - return v; - } - - } - - u64 BeginAsyncOperation(GetResultHandler handler) { - /* Only allow one async operation at a time. */ - if (g_async_key != InvalidAsyncKey) { - return InvalidAsyncKey; - } - - /* Generate a random async key. */ - g_async_key = GenerateRandomU64(); - g_async_handler = handler; - - return g_async_key; - } - - void CancelAsyncOperation(u64 async_key) { - if (async_key == g_async_key) { - g_async_key = InvalidAsyncKey; - } - } - - void EndAsyncOperation() { - gic::SetPending(SecurityEngineUserInterruptId); - } - - SmcResult SmcGetResult(SmcArguments &args) { - /* Decode arguments. */ - const u64 async_key = args.r[1]; - - /* Validate arguments. */ - SMC_R_UNLESS(g_async_key != InvalidAsyncKey, NoAsyncOperation); - SMC_R_UNLESS(g_async_key == async_key, InvalidAsyncOperation); - - /* Call the handler. */ - args.r[1] = static_cast(g_async_handler(nullptr, 0)); - g_async_key = InvalidAsyncKey; - - return SmcResult::Success; - } - - SmcResult SmcGetResultData(SmcArguments &args) { - /* Decode arguments. */ - const u64 async_key = args.r[1]; - const uintptr_t user_phys_addr = args.r[2]; - const size_t user_size = args.r[3]; - - /* Allocate a work buffer on the stack. */ - alignas(8) u8 work_buffer[1_KB]; - - /* Validate arguments. */ - SMC_R_UNLESS(g_async_key != InvalidAsyncKey, NoAsyncOperation); - SMC_R_UNLESS(g_async_key == async_key, InvalidAsyncOperation); - SMC_R_UNLESS(user_size <= sizeof(work_buffer), InvalidArgument); - - /* Call the handler. */ - args.r[1] = static_cast(g_async_handler(work_buffer, user_size)); - g_async_key = InvalidAsyncKey; - - /* Map the user buffer. */ - { - UserPageMapper mapper(user_phys_addr); - SMC_R_UNLESS(mapper.Map(), InvalidArgument); - SMC_R_UNLESS(mapper.CopyToUser(user_phys_addr, work_buffer, user_size), InvalidArgument); - } - - return SmcResult::Success; - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_result.hpp b/exosphere2/program/source/smc/secmon_smc_result.hpp deleted file mode 100644 index 853772e..0000000 --- a/exosphere2/program/source/smc/secmon_smc_result.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "secmon_smc_common.hpp" - -namespace ams::secmon::smc { - - using GetResultHandler = SmcResult (*)(void *dst, size_t dst_size); - - u64 BeginAsyncOperation(GetResultHandler handler); - void CancelAsyncOperation(u64 async_key); - void EndAsyncOperation(); - - SmcResult SmcGetResult(SmcArguments &args); - SmcResult SmcGetResultData(SmcArguments &args); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.cpp b/exosphere2/program/source/smc/secmon_smc_rsa.cpp deleted file mode 100644 index d84ce9b..0000000 --- a/exosphere2/program/source/smc/secmon_smc_rsa.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/* - * 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_page_mapper.hpp" -#include "secmon_smc_aes.hpp" -#include "secmon_smc_rsa.hpp" -#include "secmon_smc_se_lock.hpp" - -namespace ams::secmon::smc { - - namespace { - - struct ModularExponentiateByStorageKeyOption { - using Mode = util::BitPack32::Field<0, 2, u32>; - using Reserved = util::BitPack32::Field<2, 30, u32>; - }; - - struct PrepareEsDeviceUniqueKeyOption { - using KeyGeneration = util::BitPack32::Field<0, 6, int>; - using Type = util::BitPack32::Field<6, 1, EsCommonKeyType>; - using Reserved = util::BitPack32::Field<7, 25, u32>; - }; - - constexpr const u8 ModularExponentiateByStorageKeyTable[] = { - static_cast(ImportRsaKey_Lotus), - static_cast(ImportRsaKey_Ssl), - static_cast(ImportRsaKey_EsClientCert), - }; - constexpr size_t ModularExponentiateByStorageKeyTableSize = util::size(ModularExponentiateByStorageKeyTable); - - class PrepareEsDeviceUniqueKeyAsyncArguments { - private: - int generation; - EsCommonKeyType type; - u8 label_digest[crypto::Sha256Generator::HashSize]; - public: - void Set(int gen, EsCommonKeyType t, const u8 ld[crypto::Sha256Generator::HashSize]) { - this->generation = gen; - this->type = t; - std::memcpy(this->label_digest, ld, sizeof(this->label_digest)); - } - - int GetKeyGeneration() const { return this->generation; } - EsCommonKeyType GetCommonKeyType() const { return this->type; } - void GetLabelDigest(u8 dst[crypto::Sha256Generator::HashSize]) const { std::memcpy(dst, this->label_digest, sizeof(this->label_digest)); } - }; - - class ModularExponentiateByStorageKeyAsyncArguments { - private: - u8 msg[se::RsaSize]; - public: - void Set(const void *m, size_t m_size) { - std::memcpy(this->msg, m, sizeof(this->msg)); - } - - const u8 *GetMessage() const { return this->msg; } - }; - - constinit SmcResult g_exp_mod_result = SmcResult::Success; - - constinit bool g_test_exp_mod_public = false; - constinit int g_test_exp_mod_slot = pkg1::RsaKeySlot_Temporary; - constinit ImportRsaKey g_test_exp_mod_key = {}; - - constinit union { - ModularExponentiateByStorageKeyAsyncArguments modular_exponentiate_by_storage_key; - PrepareEsDeviceUniqueKeyAsyncArguments prepare_es_device_unique_key; - } g_async_arguments; - - ALWAYS_INLINE ModularExponentiateByStorageKeyAsyncArguments &GetModularExponentiateByStorageKeyAsyncArguments() { - return g_async_arguments.modular_exponentiate_by_storage_key; - } - - ALWAYS_INLINE PrepareEsDeviceUniqueKeyAsyncArguments &GetPrepareEsDeviceUniqueKeyAsyncArguments() { - return g_async_arguments.prepare_es_device_unique_key; - } - - void SecurityEngineDoneHandler() { - /* End the asynchronous operation. */ - g_exp_mod_result = SmcResult::Success; - EndAsyncOperation(); - } - - void TestRsaPublicKey(ImportRsaKey which, int slot, const void *mod, size_t mod_size, se::DoneHandler handler) { - /* Declare a buffer for our test message. */ - u8 msg[se::RsaSize]; - std::memset(msg, 'D', sizeof(msg)); - - /* Provisionally import the modulus. */ - ImportRsaKeyModulusProvisionally(which, mod, mod_size); - - /* Load the provisional public key into the slot. */ - LoadProvisionalRsaPublicKey(slot, which); - - /* Perform the test exponentiation. */ - se::ModularExponentiateAsync(slot, msg, sizeof(msg), handler); - } - - void TestRsaPrivateKey(ImportRsaKey which, int slot, se::DoneHandler handler) { - /* Get the result of the public key test. */ - u8 msg[se::RsaSize]; - se::GetRsaResult(msg, sizeof(msg)); - - /* Load the provisional private key into the slot. */ - LoadProvisionalRsaKey(slot, which); - - /* Perform the test exponentiation. */ - se::ModularExponentiateAsync(slot, msg, sizeof(msg), handler); - } - - void VerifyTestRsaKeyResult(ImportRsaKey which) { - /* Get the result of the test. */ - u8 msg[se::RsaSize]; - se::GetRsaResult(msg, sizeof(msg)); - - /* Validate the result. */ - const bool is_valid = (msg[0] == 'D') & (crypto::IsSameBytes(msg, msg + 1, sizeof(msg) - 1)); - - /* If the test passes, the key is no longer provisional. */ - if (is_valid) { - CommitRsaKeyModulus(which); - } - } - - void TestRsaKeyDoneHandler() { - if (g_test_exp_mod_public) { - /* If we're testing the public key, we still have another exponentiation to do to test the private key. */ - g_test_exp_mod_public = false; - - /* Test the private key. */ - TestRsaPrivateKey(g_test_exp_mod_key, g_test_exp_mod_slot, TestRsaKeyDoneHandler); - } else { - /* We're testing the private key, so validate the result. */ - VerifyTestRsaKeyResult(g_test_exp_mod_key); - - /* If the test passed, we can proceed to perform the intended exponentiation. */ - if (LoadRsaKey(g_test_exp_mod_slot, g_test_exp_mod_key)) { - se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, GetModularExponentiateByStorageKeyAsyncArguments().GetMessage(), se::RsaSize, SecurityEngineDoneHandler); - } else { - /* The test failed, so end the asynchronous operation. */ - g_exp_mod_result = SmcResult::InvalidArgument; - EndAsyncOperation(); - } - } - } - - SmcResult ModularExponentiateImpl(SmcArguments &args) { - /* Decode arguments. */ - const uintptr_t msg_address = args.r[1]; - const uintptr_t exp_address = args.r[2]; - const uintptr_t mod_address = args.r[3]; - const size_t exp_size = args.r[4]; - - /* Validate arguments. */ - SMC_R_UNLESS(util::IsAligned(exp_size, sizeof(u32)), InvalidArgument); - SMC_R_UNLESS(exp_size <= se::RsaSize, InvalidArgument); - - /* Copy the message and modulus from the user. */ - alignas(8) u8 msg[se::RsaSize]; - alignas(8) u8 exp[se::RsaSize]; - alignas(8) u8 mod[se::RsaSize]; - { - UserPageMapper mapper(msg_address); - SMC_R_UNLESS(mapper.Map(), InvalidArgument); - SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument); - SMC_R_UNLESS(mapper.CopyFromUser(exp, exp_address, exp_size), InvalidArgument); - SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); - } - - /* We're performing an operation, so set the result to busy. */ - g_exp_mod_result = SmcResult::Busy; - - /* Load the key into the temporary keyslot. */ - se::SetRsaKey(pkg1::RsaKeySlot_Temporary, mod, sizeof(mod), exp, exp_size); - - /* Begin the asynchronous exponentiation. */ - se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler); - - return SmcResult::Success; - } - - SmcResult ModularExponentiateByStorageKeyImpl(SmcArguments &args) { - /* Decode arguments. */ - const uintptr_t msg_address = args.r[1]; - const uintptr_t mod_address = args.r[2]; - const util::BitPack32 option = { static_cast(args.r[3]) }; - - const auto mode = option.Get(); - const auto reserved = option.Get(); - - /* Validate arguments. */ - SMC_R_UNLESS(reserved == 0, InvalidArgument); - SMC_R_UNLESS(mode < ModularExponentiateByStorageKeyTableSize, InvalidArgument); - - /* Convert the mode to an import key. */ - const auto import_key = static_cast(ModularExponentiateByStorageKeyTable[mode]); - - /* Copy the message and modulus from the user. */ - alignas(8) u8 msg[se::RsaSize]; - alignas(8) u8 mod[se::RsaSize]; - { - UserPageMapper mapper(msg_address); - SMC_R_UNLESS(mapper.Map(), InvalidArgument); - SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument); - SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); - } - - /* We're performing an operation, so set the result to busy. */ - g_exp_mod_result = SmcResult::Busy; - - /* In the ideal case, the key pair is already verified. If it is, we can use it directly. */ - if (LoadRsaKey(pkg1::RsaKeySlot_Temporary, import_key)) { - se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler); - } else { - /* Set the async arguments. */ - GetModularExponentiateByStorageKeyAsyncArguments().Set(msg, sizeof(msg)); - - /* Test the rsa key. */ - g_test_exp_mod_slot = pkg1::RsaKeySlot_Temporary; - g_test_exp_mod_key = import_key; - g_test_exp_mod_public = true; - - TestRsaPublicKey(import_key, pkg1::RsaKeySlot_Temporary, mod, sizeof(mod), TestRsaKeyDoneHandler); - } - - return SmcResult::Success; - } - - SmcResult PrepareEsDeviceUniqueKeyImpl(SmcArguments &args) { - /* Decode arguments. */ - u8 label_digest[crypto::Sha256Generator::HashSize]; - - const uintptr_t msg_address = args.r[1]; - const uintptr_t mod_address = args.r[2]; - std::memcpy(label_digest, std::addressof(args.r[3]), sizeof(label_digest)); - const util::BitPack32 option = { static_cast(args.r[7]) }; - - const auto generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, option.Get() - 1) : 0; - const auto type = option.Get(); - const auto reserved = option.Get(); - - /* Validate arguments. */ - SMC_R_UNLESS(reserved == 0, InvalidArgument); - SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); - SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument); - SMC_R_UNLESS(type < EsCommonKeyType_Count, InvalidArgument); - - /* Copy the message and modulus from the user. */ - alignas(8) u8 msg[se::RsaSize]; - alignas(8) u8 mod[se::RsaSize]; - { - UserPageMapper mapper(msg_address); - SMC_R_UNLESS(mapper.Map(), InvalidArgument); - SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument); - SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); - } - - /* We're performing an operation, so set the result to busy. */ - g_exp_mod_result = SmcResult::Busy; - - /* Set the async arguments. */ - GetPrepareEsDeviceUniqueKeyAsyncArguments().Set(generation, type, label_digest); - - /* Load the es drm key into the security engine. */ - SMC_R_UNLESS(LoadRsaKey(pkg1::RsaKeySlot_Temporary, ImportRsaKey_EsDrmCert), NotInitialized); - - /* Trigger the asynchronous modular exponentiation. */ - se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler); - - return SmcResult::Success; - } - - SmcResult GetModularExponentiateResult(void *dst, size_t dst_size) { - /* Validate state. */ - SMC_R_TRY(g_exp_mod_result); - SMC_R_UNLESS(dst_size == se::RsaSize, InvalidArgument); - - /* We want to relinquish our security engine lock at the end of scope. */ - ON_SCOPE_EXIT { UnlockSecurityEngine(); }; - - /* Get the result of the exponentiation. */ - se::GetRsaResult(dst, se::RsaSize); - - return SmcResult::Success; - } - - SmcResult GetPrepareEsDeviceUniqueKeyResult(void *dst, size_t dst_size) { - /* Declare variables. */ - u8 key_source[se::AesBlockSize]; - u8 key[se::AesBlockSize]; - u8 access_key[se::AesBlockSize]; - - /* Validate state. */ - SMC_R_TRY(g_exp_mod_result); - SMC_R_UNLESS(dst_size == sizeof(access_key), InvalidArgument); - - /* We want to relinquish our security engine lock at the end of scope. */ - ON_SCOPE_EXIT { UnlockSecurityEngine(); }; - - /* Get the async args. */ - const auto &async_args = GetPrepareEsDeviceUniqueKeyAsyncArguments(); - - /* Get the exponentiation output. */ - alignas(8) u8 msg[se::RsaSize]; - se::GetRsaResult(msg, sizeof(msg)); - - /* Decode the key. */ - { - /* Get the label digest. */ - u8 label_digest[crypto::Sha256Generator::HashSize]; - async_args.GetLabelDigest(label_digest); - - /* Decode the key source. */ - const size_t key_source_size = se::DecodeRsaOaepSha256(key_source, sizeof(key_source), msg, sizeof(msg), label_digest, sizeof(label_digest)); - SMC_R_UNLESS(key_source_size == sizeof(key_source), InvalidArgument); - } - - /* Decrypt the key. */ - DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), async_args.GetCommonKeyType(), async_args.GetKeyGeneration()); - PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key)); - - /* Copy the access key to output. */ - std::memcpy(dst, access_key, sizeof(access_key)); - - return SmcResult::Success; - } - - } - - SmcResult SmcModularExponentiate(SmcArguments &args) { - return LockSecurityEngineAndInvokeAsync(args, ModularExponentiateImpl, GetModularExponentiateResult); - } - - SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args) { - return LockSecurityEngineAndInvokeAsync(args, ModularExponentiateByStorageKeyImpl, GetModularExponentiateResult); - } - - SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) { - return LockSecurityEngineAndInvokeAsync(args, PrepareEsDeviceUniqueKeyImpl, GetPrepareEsDeviceUniqueKeyResult); - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.hpp b/exosphere2/program/source/smc/secmon_smc_rsa.hpp deleted file mode 100644 index 63859a9..0000000 --- a/exosphere2/program/source/smc/secmon_smc_rsa.hpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once -#include -#include "secmon_smc_common.hpp" - -namespace ams::secmon::smc { - - SmcResult SmcModularExponentiate(SmcArguments &args); - SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args); - - SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args); - -} diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp deleted file mode 100644 index b8ce15a..0000000 --- a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2018-2020 Atmosphère-NX - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "../secmon_error.hpp" -#include "secmon_smc_se_lock.hpp" - -namespace ams::secmon::smc { - - namespace { - - constinit std::atomic_bool g_is_locked = false; - - } - - bool TryLockSecurityEngine() { - bool value = false; - return g_is_locked.compare_exchange_strong(value, true); - } - - void UnlockSecurityEngine() { - g_is_locked = false; - } - - bool IsSecurityEngineLocked() { - return g_is_locked; - } - - SmcResult LockSecurityEngineAndInvoke(SmcArguments &args, SmcHandler impl) { - /* Try to lock the security engine. */ - SMC_R_UNLESS(TryLockSecurityEngine(), Busy); - ON_SCOPE_EXIT { UnlockSecurityEngine(); }; - - return impl(args); - } - - SmcResult LockSecurityEngineAndInvokeAsync(SmcArguments &args, SmcHandler impl, GetResultHandler result_handler) { - /* Try to lock the security engine. */ - SMC_R_UNLESS(TryLockSecurityEngine(), Busy); - auto se_guard = SCOPE_GUARD { UnlockSecurityEngine(); }; - - /* Try to start an async operation. */ - const u64 async_key = BeginAsyncOperation(result_handler); - SMC_R_UNLESS(async_key != InvalidAsyncKey, Busy); - auto async_guard = SCOPE_GUARD { CancelAsyncOperation(async_key); }; - - /* Try to invoke the operation. */ - SMC_R_TRY(impl(args)); - - /* We succeeded! Cancel our guards, and return the async key to our caller. */ - async_guard.Cancel(); - se_guard.Cancel(); - - args.r[1] = async_key; - return SmcResult::Success; - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.hpp b/exosphere2/program/source/smc/secmon_smc_se_lock.hpp deleted file mode 100644 index d7fc33d..0000000 --- a/exosphere2/program/source/smc/secmon_smc_se_lock.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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" -#include "secmon_smc_handler.hpp" -#include "secmon_smc_result.hpp" - -namespace ams::secmon::smc { - - bool TryLockSecurityEngine(); - void UnlockSecurityEngine(); - bool IsSecurityEngineLocked(); - - SmcResult LockSecurityEngineAndInvoke(SmcArguments &args, SmcHandler impl); - SmcResult LockSecurityEngineAndInvokeAsync(SmcArguments &args, SmcHandler impl, GetResultHandler result_handler); - -} diff --git a/exosphere2/program/split_program.py b/exosphere2/program/split_program.py deleted file mode 100644 index b52406e..0000000 --- a/exosphere2/program/split_program.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -import sys, lz4 -from struct import unpack as up - -def lz4_compress(data): - return lz4.block.compress(data, 'high_compression', store_size=False) - -def split_binary(data): - A, B, START, BOOT_CODE_START, BOOT_CODE_END, PROGRAM_START, C, D = up('= 0x40 - for (fn, fdata) in split_binary(data): - with open('%s/%s' % (argv[2], fn), 'wb') as f: - f.write(fdata) - return 0 - -if __name__ == '__main__': - sys.exit(main(len(sys.argv), sys.argv)) \ No newline at end of file diff --git a/exosphere2/warmboot/Makefile b/exosphere2/warmboot/Makefile deleted file mode 100644 index 2288e27..0000000 --- a/exosphere2/warmboot/Makefile +++ /dev/null @@ -1,122 +0,0 @@ -#--------------------------------------------------------------------------------- -# Define the atmosphere board and cpu -#--------------------------------------------------------------------------------- -export ATMOSPHERE_BOARD := nx-hac-001 -export ATMOSPHERE_CPU := arm7tdmi - -#--------------------------------------------------------------------------------- -# pull in common atmosphere configuration -#--------------------------------------------------------------------------------- -include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/exosphere.mk - -#--------------------------------------------------------------------------------- -# no real need to edit anything past this point unless you need to add additional -# rules for different file extensions -#--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) -#--------------------------------------------------------------------------------- - -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) -export DEPSDIR := $(CURDIR)/$(BUILD) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) - -#--------------------------------------------------------------------------------- -# use CXX for linking C++ projects, CC for standard C -#--------------------------------------------------------------------------------- -ifeq ($(strip $(CPPFILES)),) -#--------------------------------------------------------------------------------- - export LD := $(CC) -#--------------------------------------------------------------------------------- -else -#--------------------------------------------------------------------------------- - export LD := $(CXX) -#--------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------- - -export OFILES_BIN := $(addsuffix .o,$(BINFILES)) -export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) -export OFILES := $(OFILES_BIN) $(OFILES_SRC) -export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES)))) - -export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ - $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ - -I. - -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) - -.PHONY: $(BUILD) clean all - -#--------------------------------------------------------------------------------- -all: $(BUILD) check_libexo - -$(BUILD): check_libexo - @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -check_libexo: - @$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm - -#--------------------------------------------------------------------------------- -clean: - @echo clean ... - @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 - -#--------------------------------------------------------------------------------- -else -.PHONY: all - -DEPENDS := $(OFILES:.o=.d) - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -all : $(OUTPUT).bin - -$(OUTPUT).bin : $(OUTPUT).elf - $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@ - @echo built ... $(notdir $@) - -$(OUTPUT).elf : $(OFILES) ../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a - -%.elf: - @echo linking $(notdir $@) - $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ - @$(NM) -CSn $@ > $(notdir $*.lst) - -$(OFILES_SRC) : $(HFILES_BIN) - -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.bin.o %_bin.h: %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - --include $(DEPENDS) - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/exosphere2/warmboot/source/warmboot_bootrom_workaround.cpp b/exosphere2/warmboot/source/warmboot_bootrom_workaround.cpp deleted file mode 100644 index f49bf24..0000000 --- a/exosphere2/warmboot/source/warmboot_bootrom_workaround.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 "warmboot_clkrst.hpp" - -namespace ams::warmboot { - - namespace { - - constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); - constexpr inline const uintptr_t EMC = EMC_ADDRESS(0); - - } - - void ApplyMbistWorkaround() { - /* Clear all LVL2 clock gate overrides to zero. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA, 0); - reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB, 0); - reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC, 0); - reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD, 0); - reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE, 0); - - /* Clear clock enable for all but a select few devices. */ - auto devices_to_clear_l = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_L); - reg::ClearBits(static_cast(devices_to_clear_l), CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_RTC ), - CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_TMR ), - CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_GPIO ), - CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_CACHE2)); - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, devices_to_clear_l); - - auto devices_to_clear_h = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_H); - reg::ClearBits(static_cast(devices_to_clear_h), CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_MEM ), - CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_PMC ), - CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_FUSE), - CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_EMC )); - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, devices_to_clear_h); - - auto devices_to_clear_u = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_U); - reg::ClearBits(static_cast(devices_to_clear_u), CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_CSITE), - CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMA), - CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMB), - CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMC), - CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMD), - CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_CRAM2)); - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, devices_to_clear_u); - - auto devices_to_clear_v = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_V); - reg::ClearBits(static_cast(devices_to_clear_v), CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_MSELECT ), - CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_SPDIF_DOUBLER), - CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_TZRAM ), - CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_SE )); - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_CLR, devices_to_clear_v); - - auto devices_to_clear_w = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_W); - reg::ClearBits(static_cast(devices_to_clear_w), CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX0), - CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX1), - CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX2), - CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX3), - CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX4), - CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX5), - CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_ENTROPY)); - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_CLR, devices_to_clear_w); - - auto devices_to_clear_x = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_X); - reg::ClearBits(static_cast(devices_to_clear_x), CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CAPA ), - CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CBPA ), - CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CPU ), - CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_BBC ), - CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_EMC_DLL ), - CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_GPU ), - CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_DBGAPB ), - CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_PLLG_REF)); - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_X_CLR, devices_to_clear_x); - - auto devices_to_clear_y = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_Y); - reg::ClearBits(static_cast(devices_to_clear_y), CLK_RST_REG_BITS_MASK(CLK_ENB_Y_CLK_ENB_MC_CCPA), - CLK_RST_REG_BITS_MASK(CLK_ENB_Y_CLK_ENB_MC_CDPA)); - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_Y_CLR, devices_to_clear_y); - - /* If CH1 is enabled, enable clock to MC1. */ - if (reg::HasValue(EMC + EMC_FBIO_CFG7, EMC_REG_BITS_ENUM(FBIO_CFG7_CH1_ENABLE, ENABLE))) { - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_CLK_ENB_MC1, ENABLE)); - } - } - -} diff --git a/exosphere2/warmboot/source/warmboot_bootrom_workaround.hpp b/exosphere2/warmboot/source/warmboot_bootrom_workaround.hpp deleted file mode 100644 index 039c3df..0000000 --- a/exosphere2/warmboot/source/warmboot_bootrom_workaround.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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::warmboot { - - void ApplyMbistWorkaround(); - -} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_clkrst.cpp b/exosphere2/warmboot/source/warmboot_clkrst.cpp deleted file mode 100644 index 258b3b3..0000000 --- a/exosphere2/warmboot/source/warmboot_clkrst.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 "warmboot_clkrst.hpp" - -namespace ams::warmboot { - - namespace { - - constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); - constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); - constexpr inline const uintptr_t TIMER = secmon::MemoryRegionPhysicalDeviceTimer.GetAddress(); - - } - - void ConfigureOscillators() { - /* Enable the crystal oscillator, and copy the drive strength from pmc. */ - const auto xofs = reg::GetValue(PMC + APBDEV_PMC_OSC_EDPD_OVER, PMC_REG_BITS_MASK(OSC_EDPD_OVER_XOFS)); - - reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_OSC_CTRL, CLK_RST_REG_BITS_ENUM (OSC_CTRL_XOE, ENABLE), - CLK_RST_REG_BITS_VALUE(OSC_CTRL_XOFS, xofs)); - - /* Configure CLK_M_DIVISOR to 2. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_SPARE_REG0, CLK_RST_REG_BITS_ENUM(SPARE_REG0_CLK_M_DIVISOR, CLK_M_DIVISOR2)); - reg::Read(CLKRST + CLK_RST_CONTROLLER_SPARE_REG0); - - /* Restore TIMERUS config to 19.2 MHz. */ - reg::Write(TIMER + TIMERUS_USEC_CFG, TIMER_REG_BITS_VALUE(USEC_CFG_USEC_DIVIDEND, 5 - 1), - TIMER_REG_BITS_VALUE(USEC_CFG_USEC_DIVISOR, 96 - 1)); - - } - -} diff --git a/exosphere2/warmboot/source/warmboot_clkrst.hpp b/exosphere2/warmboot/source/warmboot_clkrst.hpp deleted file mode 100644 index 327f825..0000000 --- a/exosphere2/warmboot/source/warmboot_clkrst.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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::warmboot { - - void ConfigureOscillators(); - -} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_cpu_cluster.cpp b/exosphere2/warmboot/source/warmboot_cpu_cluster.cpp deleted file mode 100644 index 34983e0..0000000 --- a/exosphere2/warmboot/source/warmboot_cpu_cluster.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * 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 "warmboot_clkrst.hpp" -#include "warmboot_util.hpp" - -namespace ams::warmboot { - - namespace { - - constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); - constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); - constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress(); - constexpr inline const uintptr_t GPIO = secmon::MemoryRegionPhysicalDeviceGpio.GetAddress(); - constexpr inline const uintptr_t MSELECT = MSELECT(0); - constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); - constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionPhysicalDeviceSystem.GetAddress(); - - ALWAYS_INLINE void EnableClusterPartition(const reg::BitsValue value, APBDEV_PMC_PWRGATE_TOGGLE_PARTID part_id) { - /* Toggle the partitions if necessary. */ - if (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, value)) { - reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM (PWRGATE_TOGGLE_START, ENABLE), - PMC_REG_BITS_VALUE(PWRGATE_TOGGLE_PARTID, part_id)); - } - - /* Wait for the toggle to complete. */ - while (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, value)) { /* ... */ } - - /* Remove clamping. */ - reg::Write(PMC + APBDEV_PMC_REMOVE_CLAMPING_CMD, value); - - /* Wait for clamping to be removed. */ - while (reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, value)) { /* ... */ } - } - - } - - void InitializeCpuCluster() { - /* Set CoreSight reset. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_U_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SET_SET_CSITE_RST, ENABLE)); - - /* Restore PROD setting to CPU_SOFTRST_CTRL2 by clearing CAR2PMC_CPU_ACK_WIDTH. */ - reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2, CLK_RST_REG_BITS_VALUE(CPU_SOFTRST_CTRL2_CAR2PMC_CPU_ACK_WIDTH, 0)); - - /* Restore the CPU reset vector from PMC scratch. */ - reg::Write(SYSTEM + SB_AA64_RESET_LOW, reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH34) | 1); - reg::Write(SYSTEM + SB_AA64_RESET_HIGH, reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH35)); - - /* Configure SUPER_CCLKG_DIVIDER. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_SUPER_CCLKG_DIVIDER, CLK_RST_REG_BITS_ENUM (SUPER_CCLKG_DIVIDER_SUPER_CDIV_ENB, ENABLE), - CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_FIQ, NO_IMPACT), - CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_FIQ, NO_IMPACT), - CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_IRQ, NO_IMPACT), - CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_IRQ, NO_IMPACT), - CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVIDEND, 0), - CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVISOR, 0)); - - /* Configure SUPER_CCLKLP_DIVIDER. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_SUPER_CCLKLP_DIVIDER, CLK_RST_REG_BITS_ENUM (SUPER_CCLKLP_DIVIDER_SUPER_CDIV_ENB, ENABLE), - CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_FIQ, NO_IMPACT), - CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_FIQ, NO_IMPACT), - CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_IRQ, NO_IMPACT), - CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_IRQ, NO_IMPACT), - CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVIDEND, 0), - CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVISOR, 0)); - - /* Enable CoreSight clock. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_U_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_SET_SET_CLK_ENB_CSITE, ENABLE)); - - /* Clear CoreSight reset. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_U_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_U_CLR_CLR_CSITE_RST, ENABLE)); - - /* Select MSELECT clock source as PLLP_OUT0 with divider of 4. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_MSELECT_MSELECT_CLK_SRC, PLLP_OUT0), - CLK_RST_REG_BITS_VALUE(CLK_SOURCE_MSELECT_MSELECT_CLK_DIVISOR, 6)); - - /* Enable clock to MSELECT. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_V_SET_SET_CLK_ENB_MSELECT, ENABLE)); - - /* Wait two microseconds, then take MSELECT out of reset. */ - util::WaitMicroSeconds(2); - reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_V_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_V_CLR_CLR_MSELECT_RST, ENABLE)); - - /* Workaround bug by disabling MSELECT error mechanism and enabling WRAP type conversion. */ - reg::ReadWrite(MSELECT + MSELECT_CONFIG, MSELECT_REG_BITS_ENUM(CONFIG_ERR_RESP_EN_SLAVE1, DISABLE), - MSELECT_REG_BITS_ENUM(CONFIG_ERR_RESP_EN_SLAVE2, DISABLE), - MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE0, ENABLE), - MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE1, ENABLE), - MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE2, ENABLE)); - - /* Disable PLLX. */ - reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_PLLX_BASE, CLK_RST_REG_BITS_ENUM(PLLX_BASE_PLLX_ENABLE, DISABLE)); - - /* Clear bit 0 of PMC Scratch 190. */ - reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH190, REG_BITS_VALUE(0, 1, 0)); - - /* Clear PMC_DPD_SAMPLE, and wait 10 us for clear to take effect. */ - reg::Write(PMC + APBDEV_PMC_DPD_SAMPLE, 0); - util::WaitMicroSeconds(10); - - /* Configure UART2_RTS low (GPIO controller 2 G). */ - reg::ReadWrite(GPIO + 0x108, REG_BITS_VALUE(2, 1, 1)); /* GPIO_CNF */ - reg::ReadWrite(GPIO + 0x118, REG_BITS_VALUE(2, 1, 1)); /* GPIO_OE */ - reg::ReadWrite(GPIO + 0x128, REG_BITS_VALUE(2, 1, 0)); /* GPIO_OUT */ - - /* Tristate CLDVFS PWN. */ - reg::Write(APB_MISC + PINMUX_AUX_DVFS_PWM, PINMUX_REG_BITS_ENUM(AUX_TRISTATE, TRISTATE), - PINMUX_REG_BITS_ENUM(AUX_DVFS_PWM_PM, CLDVFS)); - reg::Read(APB_MISC + PINMUX_AUX_DVFS_PWM); - - /* Restore PWR_I2C E_INPUT. */ - reg::Write(APB_MISC + PINMUX_AUX_PWR_I2C_SCL, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); - reg::Write(APB_MISC + PINMUX_AUX_PWR_I2C_SDA, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); - - /* Enable CLDVFS clock. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_SET_SET_CLK_ENB_DVFS, ENABLE)); - - /* Set CLDVFS clock source/divider. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DVFS_REF_DVFS_REF_CLK_SRC, PLLP_OUT0), - CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DVFS_REF_DVFS_REF_DIVISOR, 14)); - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DVFS_SOC_DVFS_SOC_CLK_SRC, PLLP_OUT0), - CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DVFS_SOC_DVFS_SOC_DIVISOR, 14)); - - /* Enable PWR_I2C controller (I2C5). */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_SET_SET_CLK_ENB_I2C5, ENABLE)); - reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_H_SET_SET_I2C5_RST, ENABLE)); - util::WaitMicroSeconds(5); - - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_I2C5, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_I2C5_I2C5_CLK_SRC, PLLP_OUT0), - CLK_RST_REG_BITS_VALUE(CLK_SOURCE_I2C5_I2C5_CLK_DIVISOR, 4)); - - reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_H_CLR_CLR_I2C5_RST, ENABLE)); - - /* Set the EN bit in pmic regulator. */ - pmic::SetEnBit(fuse::GetRegulator()); - - /* Wait 2ms. */ - util::WaitMicroSeconds(2'000); - - /* Enable power to the CRAIL partition. */ - EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_CRAIL, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CRAIL); - - /* Remove software clamp to CRAIL. */ - reg::Write(PMC + APBDEV_PMC_SET_SW_CLAMP, 0); - reg::Write(PMC + APBDEV_PMC_REMOVE_CLAMPING_CMD, PMC_REG_BITS_ENUM(REMOVE_CLAMPING_COMMAND_CRAIL, ENABLE)); - while (reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, PMC_REG_BITS_ENUM(CLAMP_STATUS_CRAIL, ENABLE))) { /* ... */ } - - /* Spinloop 8 times, to add a little delay. */ - SpinLoop(8); - - /* Disable PWR_I2C controller (I2C5). */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_H_SET_SET_I2C5_RST, ENABLE)); - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_CLR_CLR_CLK_ENB_I2C5, ENABLE)); - - /* Disable CLDVFS clock. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_CLR_CLR_CLK_ENB_DVFS, ENABLE)); - - /* Perform fast cluster RAM repair if needed. */ - if (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_BPMP_CLUSTER_CONTROL, FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, SLOW))) { - reg::Write(FLOW_CTLR + FLOW_CTLR_RAM_REPAIR, FLOW_REG_BITS_ENUM(RAM_REPAIR_REQ, ENABLE)); - - while (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_RAM_REPAIR, FLOW_REG_BITS_ENUM(RAM_REPAIR_STS, DONE))) { - /* ... */ - } - } - - /* Enable power to the non-cpu partition. */ - EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_C0NC, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_C0NC); - - /* Enable clock to PLLP_OUT_CPU. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_Y_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_Y_SET_SET_CLK_ENB_PLLP_OUT_CPU, ENABLE)); - util::WaitMicroSeconds(2); - - /* Enable clock to the cpu complex. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_SET_SET_CLK_ENB_CPU, ENABLE)); - reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_V_SET_SET_CLK_ENB_CPUG, ENABLE)); - util::WaitMicroSeconds(10); - - /* Select cpu complex clock source. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_CCLKG_BURST_POLICY, CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, PLLP_OUT0), - CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, PLLP_OUT0), - CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, PLLP_OUT0), - CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, PLLP_OUT0), - CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CPU_STATE, RUN)); - - reg::Write(CLKRST + CLK_RST_CONTROLLER_CCLKLP_BURST_POLICY, CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, PLLP_OUT0), - CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, PLLP_OUT0), - CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, PLLP_OUT0), - CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, PLLP_OUT0), - CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CPU_STATE, RUN)); - util::WaitMicroSeconds(10); - - /* Wake non-cpu out of reset. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, ENABLE)); - } - - void PowerOnCpu() { - /* Enable power to the CE0 partition. */ - EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_CE0, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0); - - /* Clear CPU reset. */ - reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET0, ENABLE), - CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET0, ENABLE)); - } - -} diff --git a/exosphere2/warmboot/source/warmboot_cpu_cluster.hpp b/exosphere2/warmboot/source/warmboot_cpu_cluster.hpp deleted file mode 100644 index 6b2e3c8..0000000 --- a/exosphere2/warmboot/source/warmboot_cpu_cluster.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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::warmboot { - - void InitializeCpuCluster(); - void PowerOnCpu(); - -} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_dram.cpp b/exosphere2/warmboot/source/warmboot_dram.cpp deleted file mode 100644 index 1099964..0000000 --- a/exosphere2/warmboot/source/warmboot_dram.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 "warmboot_dram.hpp" - -namespace ams::warmboot { - - namespace { - - constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); - constexpr inline const uintptr_t EMC = EMC_ADDRESS(0); - constexpr inline const uintptr_t EMC0 = EMC0_ADDRESS(0); - constexpr inline const uintptr_t EMC1 = EMC1_ADDRESS(0); - constexpr inline const uintptr_t MC = secmon::MemoryRegionPhysicalDeviceMemoryController.GetAddress(); - constexpr inline const uintptr_t MC0 = secmon::MemoryRegionPhysicalDeviceMemoryController0.GetAddress(); - constexpr inline const uintptr_t MC1 = secmon::MemoryRegionPhysicalDeviceMemoryController1.GetAddress(); - - } - - void RestrictBpmpAccessToMainMemory() { - /* Bpmp memory access is restricted by forcing internal access to an invalid carveout. */ - constexpr u32 ForceInternalAccess0 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS0_AVPCARM7R, ENABLE)); - constexpr u32 ForceInternalAccess1 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS1_AVPCARM7W, ENABLE)); - - 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, 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, UNTRANSLATED_ONLY), - MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), - MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, TZ_SECURE)); - - /* Specify a 128KB carveout at NULL with no clients allowed access, and bpmp forced to access. */ - reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM, 0); - reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM_HI, 0); - reg::Write(MC + MC_SECURITY_CARVEOUT4_SIZE_128KB, 1); - reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0, 0); - reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1, 0); - reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2, 0); - reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3, 0); - reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4, 0); - reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0, ForceInternalAccess0); - reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1, ForceInternalAccess1); - 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, CarveoutConfig); - } - - void RestoreRamSvop() { - reg::ReadWrite(APB_MISC + APB_MISC_GP_ASDBGREG, APB_MISC_REG_BITS_VALUE(GP_ASDBGREG_CFG2TMC_RAM_SVOP_PDP, 2)); - } - - void ConfigureEmcPmacroTraining() { - /* Disable writes to BYTE0-7. */ - reg::Write(EMC + EMC_PMACRO_CFG_PM_GLOBAL_0, EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0, ENABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1, ENABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2, ENABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3, ENABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4, ENABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5, ENABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6, ENABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, ENABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3, DISABLE)); - - /* Set E_WRPTR on Channel 0. */ - reg::Write(EMC + EMC_PMACRO_TRAINING_CTRL_0, EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_ENABLED, DISABLED), - EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_TRAIN_QPOP, DISABLED), - EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_RX_E_DIRECT_ZI, DISABLED), - EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR, ENABLED), - EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_DRV_DQS, DISABLED)); - - /* Set E_WRPTR on Channel 1. */ - reg::Write(EMC + EMC_PMACRO_TRAINING_CTRL_1, EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_ENABLED, DISABLED), - EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_TRAIN_QPOP, DISABLED), - EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_RX_E_DIRECT_ZI, DISABLED), - EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR, ENABLED), - EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_DRV_DQS, DISABLED)); - - /* Re-enable writes to BYTE0-7. */ - reg::Write(EMC + EMC_PMACRO_CFG_PM_GLOBAL_0, EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2, DISABLE), - EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3, DISABLE)); - } - -} diff --git a/exosphere2/warmboot/source/warmboot_dram.hpp b/exosphere2/warmboot/source/warmboot_dram.hpp deleted file mode 100644 index 0e8cbaa..0000000 --- a/exosphere2/warmboot/source/warmboot_dram.hpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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::warmboot { - - void RestrictBpmpAccessToMainMemory(); - void RestoreRamSvop(); - void ConfigureEmcPmacroTraining(); - -} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_exception_vectors.s b/exosphere2/warmboot/source/warmboot_exception_vectors.s deleted file mode 100644 index 7bac9ac..0000000 --- a/exosphere2/warmboot/source/warmboot_exception_vectors.s +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 warmboot_header -warmboot_header: - /* TODO: If Mariko warmboothax ever happens, generate a mariko header? */ - /* Warmboot header. */ - .word __total_size__ - .rept 3 - .word 0x00000000 - .endr - - /* RSA modulus. */ - .rept 0x40 - .word 0xFFFFFFFF - .endr - - /* Padding */ - .rept 4 - .word 0x00000000 - .endr - - /* RSA signature */ - .rept 0x40 - .word 0xFFFFFFFF - .endr - - /* Padding */ - .rept 4 - .word 0x00000000 - .endr - - /* Firmware metadata. */ - .word __total_size__ - .word _reset - .word _reset - .word __executable_size__ - -.global _reset -_reset: - b _ZN3ams8warmboot5StartEv - -.global _metadata -_metadata: - .ascii "WBT0" /* Magic number */ - .word 0x00000000 /* Target firmware. */ - .word 0x00000000 /* Reserved */ - .word 0x00000000 /* Reserved */ \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_main.cpp b/exosphere2/warmboot/source/warmboot_main.cpp deleted file mode 100644 index 933b17e..0000000 --- a/exosphere2/warmboot/source/warmboot_main.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 "warmboot_bootrom_workaround.hpp" -#include "warmboot_clkrst.hpp" -#include "warmboot_cpu_cluster.hpp" -#include "warmboot_dram.hpp" -#include "warmboot_main.hpp" -#include "warmboot_misc.hpp" -#include "warmboot_secure_monitor.hpp" - -namespace ams::warmboot { - - namespace { - - constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); - constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress(); - - constexpr inline const uintptr_t ExpectedMetadataAddress = 0x40010244; - - } - - void Main(const Metadata *metadata) { - /* Ensure that we're running under vaguely sane conditions. */ - AMS_ABORT_UNLESS(metadata->magic == Metadata::Magic); - AMS_ABORT_UNLESS(metadata->target_firmware <= ams::TargetFirmware_Max); - - /* Restrict the bpmp's access to dram. */ - if (metadata->target_firmware >= TargetFirmware_4_0_0) { - RestrictBpmpAccessToMainMemory(); - } - - /* Configure rtck-daisychaining/jtag. */ - ConfigureMiscSystemDebug(); - - /* NOTE: Here, Nintendo checks that the number of burnt anti-downgrade fuses is valid. */ - - /* NOTE: Here, Nintendo validates that APBDEV_PMC_SECURE_SCRATCH32 contains the correct magic number for the current warmboot firmware revision. */ - - /* Validate that we're executing at the correct address. */ - AMS_ABORT_UNLESS(reinterpret_cast(metadata) == ExpectedMetadataAddress); - - /* Validate that we're executing on the bpmp. */ - AMS_ABORT_UNLESS(reg::Read(PG_UP(PG_UP_TAG)) == PG_UP_TAG_PID_COP); - - /* Configure fuse bypass. */ - fuse::ConfigureFuseBypass(); - - /* Configure system oscillators. */ - ConfigureOscillators(); - - /* Restore DRAM configuration. */ - RestoreRamSvop(); - ConfigureEmcPmacroTraining(); - - /* If on Erista, work around the bootrom mbist issue. */ - if (fuse::GetSocType() == fuse::SocType_Erista) { - ApplyMbistWorkaround(); - } - - /* Initialize the cpu cluster. */ - InitializeCpuCluster(); - - /* Restore the secure monitor to tzram. */ - RestoreSecureMonitorToTzram(metadata->target_firmware); - - /* Power on the cpu. */ - PowerOnCpu(); - - /* Halt ourselves. */ - const bool disable_jtag = metadata->target_firmware >= TargetFirmware_4_0_0; - 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_SEL(HALT_COP_EVENTS_JTAG, disable_jtag, DISABLED, ENABLED)); - } - } - - 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() { - warmboot::ExceptionHandler(); - } - -} diff --git a/exosphere2/warmboot/source/warmboot_main.hpp b/exosphere2/warmboot/source/warmboot_main.hpp deleted file mode 100644 index f71a89b..0000000 --- a/exosphere2/warmboot/source/warmboot_main.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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::warmboot { - - struct Metadata { - static constexpr u32 Magic = util::FourCC<'W','B','T','0'>::Code; - - u32 magic; - ams::TargetFirmware target_firmware; - u32 reserved[2]; - }; - static_assert(util::is_pod::value); - static_assert(sizeof(Metadata) == 0x10); - -} diff --git a/exosphere2/warmboot/source/warmboot_misc.cpp b/exosphere2/warmboot/source/warmboot_misc.cpp deleted file mode 100644 index eeb5932..0000000 --- a/exosphere2/warmboot/source/warmboot_misc.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 "warmboot_misc.hpp" - -namespace ams::warmboot { - - namespace { - - constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); - constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); - constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionPhysicalDeviceSystem.GetAddress(); - - } - - void ConfigureMiscSystemDebug() { - /* Enable RTCK daisy-chaining. */ - reg::Write(APB_MISC + APB_MISC_PP_CONFIG_CTL, APB_MISC_REG_BITS_ENUM(PP_CONFIG_CTL_TBE, ENABLE)); - - /* If we're in production mode, perform JTAG configuration. */ - /* NOTE: While this is what NVidia's code does, this is almost certainly a logic error. */ - /* They intend to configure JTAG only when *not* in production mode. */ - /* However, here we will do what they do, and not what they intend. */ - const bool production_mode = fuse::IsOdmProductionMode(); - if (production_mode) { - const bool jtag_sts = reg::HasValue(PMC + APBDEV_PMC_STICKY_BITS, PMC_REG_BITS_ENUM(STICKY_BITS_JTAG_STS, ENABLE)); - - reg::ReadWrite(SYSTEM + SB_PFCFG, SB_REG_BITS_ENUM_SEL(PFCFG_DBGEN, jtag_sts, ENABLE, DISABLE), - SB_REG_BITS_ENUM_SEL(PFCFG_NIDEN, jtag_sts, ENABLE, DISABLE), - SB_REG_BITS_ENUM_SEL(PFCFG_SPNIDEN, jtag_sts, ENABLE, DISABLE), - SB_REG_BITS_ENUM (PFCFG_SPIDEN, DISABLE)); - - reg::ReadWrite(APB_MISC + APB_MISC_PP_CONFIG_CTL, APB_MISC_REG_BITS_ENUM_SEL(PP_CONFIG_CTL_JTAG, jtag_sts, ENABLE, DISABLE)); - } - - /* Configure HDA codec disable. */ - reg::ReadWrite(PMC + APBDEV_PMC_STICKY_BITS, PMC_REG_BITS_ENUM_SEL(STICKY_BITS_HDA_LPBK_DIS, production_mode, ENABLE, DISABLE)); - - /* Set E_INPUT in PINMUX_AUX_GPIO_PA6 (needed by the XUSB and SATA controllers). */ - reg::ReadWrite(APB_MISC + PINMUX_AUX_GPIO_PA6, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); - } - -} diff --git a/exosphere2/warmboot/source/warmboot_misc.hpp b/exosphere2/warmboot/source/warmboot_misc.hpp deleted file mode 100644 index 4cfeeab..0000000 --- a/exosphere2/warmboot/source/warmboot_misc.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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::warmboot { - - void ConfigureMiscSystemDebug(); - -} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_secure_monitor.cpp b/exosphere2/warmboot/source/warmboot_secure_monitor.cpp deleted file mode 100644 index 40b7b84..0000000 --- a/exosphere2/warmboot/source/warmboot_secure_monitor.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 "warmboot_secure_monitor.hpp" - -namespace ams::warmboot { - - namespace { - - constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); - - constexpr inline const pkg1::AesKeySlot SavedAesKeySlots[] = { - pkg1::AesKeySlot_TzramSaveKek, - pkg1::AesKeySlot_RandomForUserWrap, - pkg1::AesKeySlot_RandomForKeyStorageWrap, - pkg1::AesKeySlot_DeviceMaster, - pkg1::AesKeySlot_Master, - pkg1::AesKeySlot_Device, - }; - - constexpr ALWAYS_INLINE bool IsSavedAesKeySlot(int slot) { - for (const auto SavedSlot : SavedAesKeySlots) { - if (slot == SavedSlot) { - return true; - } - } - - return false; - } - - void ClearUnsavedSecurityEngineKeySlots() { - /* Clear unsaved aes keys and all ivs. */ - for (int slot = 0; slot < se::AesKeySlotCount; ++slot) { - if (!IsSavedAesKeySlot(slot)) { - se::ClearAesKeySlot(slot); - } - se::ClearAesKeyIv(slot); - } - - /* Clear all rsa keys. */ - for (int slot = 0; slot < se::RsaKeySlotCount; ++slot) { - se::ClearRsaKeySlot(slot); - } - } - - void RestoreEncryptedTzram(void * const tzram_dst, const void * const tzram_src, size_t tzram_size) { - /* Derive the save key from the save kek. */ - const u32 key_source[se::AesBlockSize / sizeof(u32)] = { reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH24), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH25), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH26), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH27)}; - se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); - se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, sizeof(key_source)); - - /* Decrypt tzram. */ - const u8 tzram_iv[se::AesBlockSize] = {}; - se::DecryptAes256Cbc(tzram_dst, tzram_size, pkg1::AesKeySlot_TzramSaveKey, tzram_src, tzram_size, tzram_iv, sizeof(tzram_iv)); - - /* Calculate the cmac of decrypted tzram. */ - u8 tzram_mac[se::AesBlockSize] = {}; - se::ComputeAes256Cmac(tzram_mac, sizeof(tzram_mac), pkg1::AesKeySlot_TzramSaveKey, tzram_dst, tzram_size); - - /* Get the expected mac from pmc scratch. */ - const u32 expected_mac[sizeof(tzram_mac) / sizeof(u32)] = { reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH112), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH113), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH114), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH115)}; - - /* Validate that the calculated mac is correct. */ - AMS_ABORT_UNLESS(crypto::IsSameBytes(tzram_mac, expected_mac, sizeof(tzram_mac))); - } - - void RestoreSecureMonitorToTzramErista(const TargetFirmware target_fw) { - /* Clear all unsaved security engine keyslots. */ - ClearUnsavedSecurityEngineKeySlots(); - - /* Restore encrypted tzram contents. */ - void * const tzram_src = secmon::MemoryRegionPhysicalDramSecureDataStoreTzram.GetPointer(); - void * const tzram_dst = secmon::MemoryRegionPhysicalTzramNonVolatile.GetPointer(); - const size_t tzram_size = secmon::MemoryRegionPhysicalTzramNonVolatile.GetSize(); - RestoreEncryptedTzram(tzram_dst, tzram_src, tzram_size); - - /* Clear the tzram kek registers. */ - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH24, 0); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH25, 0); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH26, 0); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH27, 0); - - /* Clear the tzram cmac registers. */ - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH112, 0); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH113, 0); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH114, 0); - reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH115, 0); - - /* Clear the keydata used to protect tzram. */ - se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKek); - se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); - - /* Clear the encrypted copy of tzram in dram. */ - /* NOTE: This does not actually clear the encrypted copy, because BPMP access to main memory has been restricted. */ - /* Nintendo seems to not realize this, though, so we'll do the same. */ - std::memset(tzram_src, 0, tzram_size); - - /* Set Tzram to secure-world only. */ - se::SetTzramSecure(); - } - - } - - void RestoreSecureMonitorToTzram(const TargetFirmware target_fw) { - /* If erista, perform restoration procedure. */ - if (fuse::GetSocType() == fuse::SocType_Erista) { - RestoreSecureMonitorToTzramErista(target_fw); - } - - /* Lock secure scratch. */ - pmc::LockSecureRegister(static_cast(pmc::SecureRegister_DramParameters | pmc::SecureRegister_Other)); - - /* Lockout fuses. */ - fuse::Lockout(); - } - -} diff --git a/exosphere2/warmboot/source/warmboot_secure_monitor.hpp b/exosphere2/warmboot/source/warmboot_secure_monitor.hpp deleted file mode 100644 index 499d125..0000000 --- a/exosphere2/warmboot/source/warmboot_secure_monitor.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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::warmboot { - - void RestoreSecureMonitorToTzram(const TargetFirmware target_fw); - -} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_start.s b/exosphere2/warmboot/source/warmboot_start.s deleted file mode 100644 index 18070e0..0000000 --- a/exosphere2/warmboot/source/warmboot_start.s +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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._ZN3ams8warmboot5StartEv, "ax", %progbits -.align 3 -.global _ZN3ams8warmboot5StartEv -_ZN3ams8warmboot5StartEv: - /* Set CPSR_cf and CPSR_cf. */ - msr cpsr_f, #0xC0 - msr cpsr_cf, #0xD3 - - /* Set the stack pointer. */ - ldr sp, =__stack_top__ - - /* Set our link register to the exception handler. */ - ldr lr, =_ZN3ams8warmboot16ExceptionHandlerEv - - /* Invoke main. */ - ldr r0, =_metadata - bl _ZN3ams8warmboot4MainEPKNS0_8MetadataE - - /* Infinite loop. */ - 1: b 1b \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_util.hpp b/exosphere2/warmboot/source/warmboot_util.hpp deleted file mode 100644 index 5ca4402..0000000 --- a/exosphere2/warmboot/source/warmboot_util.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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::warmboot { - - void SpinLoop(unsigned int num); - -} \ No newline at end of file diff --git a/exosphere2/warmboot/source/warmboot_util_asm.s b/exosphere2/warmboot/source/warmboot_util_asm.s deleted file mode 100644 index c23d7f3..0000000 --- a/exosphere2/warmboot/source/warmboot_util_asm.s +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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._ZN3ams8warmboot8SpinLoopEj, "ax", %progbits -.global _ZN3ams8warmboot8SpinLoopEj -.thumb_func -.syntax unified -_ZN3ams8warmboot8SpinLoopEj: - 1: - /* Subtract one from the count. */ - subs r0, r0, #1 - - /* If we aren't at zero, continue looping. */ - bgt 1b - - /* Return. */ - bx lr \ No newline at end of file diff --git a/exosphere2/warmboot/warmboot.ld b/exosphere2/warmboot/warmboot.ld deleted file mode 100644 index 0e3d448..0000000 --- a/exosphere2/warmboot/warmboot.ld +++ /dev/null @@ -1,189 +0,0 @@ -OUTPUT_ARCH(arm) -ENTRY(_reset) - -MEMORY -{ - NULL : ORIGIN = 0, LENGTH = 4K - lp0fw : ORIGIN = 0x40010000, LENGTH = 16K -} - - -SECTIONS -{ - /* =========== CODE section =========== */ - PROVIDE(__start__ = ORIGIN(lp0fw)); - . = __start__; - __code_start = . ; - - .vectors : - { - KEEP (*(.vectors .vectors.*)) - . = ALIGN(8); - } >lp0fw - - .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); - } >lp0fw - - .init : - { - KEEP( *(.init) ) - . = ALIGN(8); - } >lp0fw - - .plt : - { - *(.plt) - *(.iplt) - . = ALIGN(8); - } >lp0fw - - .fini : - { - KEEP( *(.fini) ) - . = ALIGN(8); - } >lp0fw - - - /* =========== RODATA section =========== */ - . = ALIGN(8); - __rodata_start = . ; - - .rodata : - { - *(.rodata .rodata.* .gnu.linkonce.r.*) - . = ALIGN(8); - } >lp0fw - - .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >lp0fw - .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >lp0fw - .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >lp0fw - .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >lp0fw - - .hash : { *(.hash) } >lp0fw - - /* =========== DATA section =========== */ - . = ALIGN(8); - __data_start = . ; - - .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >lp0fw - .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >lp0fw - .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >lp0fw - .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >lp0fw - - .preinit_array ALIGN(8) : - { - PROVIDE (__preinit_array_start = .); - KEEP (*(.preinit_array)) - PROVIDE (__preinit_array_end = .); - } >lp0fw - - .init_array ALIGN(8) : - { - PROVIDE (__init_array_start = .); - KEEP (*(SORT(.init_array.*))) - KEEP (*(.init_array)) - PROVIDE (__init_array_end = .); - } >lp0fw - - .fini_array ALIGN(8) : - { - PROVIDE (__fini_array_start = .); - KEEP (*(.fini_array)) - KEEP (*(SORT(.fini_array.*))) - PROVIDE (__fini_array_end = .); - } >lp0fw - - .ctors ALIGN(8) : - { - KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ - KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) - } >lp0fw - - .dtors ALIGN(8) : - { - KEEP (*crtbegin.o(.dtors)) - KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) - } >lp0fw - - __got_start__ = .; - - .got : { *(.got) *(.igot) } >lp0fw - .got.plt : { *(.got.plt) *(.igot.plt) } >lp0fw - - __got_end__ = .; - - .data ALIGN(8) : - { - *(.data .data.* .gnu.linkonce.d.*) - SORT(CONSTRUCTORS) - } >lp0fw - - __bss_start__ = .; - .bss ALIGN(8) : - { - *(.dynbss) - *(.bss .bss.* .gnu.linkonce.b.*) - *(COMMON) - . = ALIGN(16); - } >lp0fw - __bss_end__ = .; - - __end__ = ABSOLUTE(.) ; - - __total_size__ = (__end__ - __start__); - __executable_size__ = (__end__ - _reset); - - __stack_top__ = 0x40013000; - __stack_bottom__ = 0x40012000; - - /* ================== - ==== Metadata ==== - ================== */ - - /* Discard sections that difficult post-processing */ - /DISCARD/ : { *(.group .comment .note .interp) } - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section so we begin them at 0. */ - - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } -} \ No newline at end of file diff --git a/exosphere2/warmboot/warmboot.specs b/exosphere2/warmboot/warmboot.specs deleted file mode 100644 index 45d0056..0000000 --- a/exosphere2/warmboot/warmboot.specs +++ /dev/null @@ -1,7 +0,0 @@ -%rename link old_link - -*link: -%(old_link) -T %:getenv(TOPDIR /warmboot.ld) --gc-sections --nmagic -nostdlib -nostartfiles - -*startfile: -crti%O%s crtbegin%O%s diff --git a/fusee/fusee-primary/Makefile b/fusee/fusee-primary/Makefile index dd38287..c471da0 100644 --- a/fusee/fusee-primary/Makefile +++ b/fusee/fusee-primary/Makefile @@ -77,7 +77,7 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(AMS)/exosphere2/program/rebootstub + $(AMS)/exosphere/program/rebootstub export DEPSDIR := $(CURDIR)/$(BUILD) @@ -117,7 +117,7 @@ all: check_rebootstub $(BUILD) check_rebootstub: - @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub all + @$(MAKE) -C $(AMS)/exosphere/program/rebootstub all $(BUILD): check_rebootstub @[ -d $@ ] || mkdir -p $@ @@ -126,7 +126,7 @@ #--------------------------------------------------------------------------------- clean: @echo clean ... - @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub clean + @$(MAKE) -C $(AMS)/exosphere/program/rebootstub clean @rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf diff --git a/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile index b2798e1..51a408b 100644 --- a/fusee/fusee-secondary/Makefile +++ b/fusee/fusee-secondary/Makefile @@ -87,7 +87,7 @@ export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/ncm $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(AMS)/exosphere2 $(AMS)/exosphere2/warmboot $(AMS)/exosphere2/program/rebootstub \ + $(AMS)/exosphere $(AMS)/exosphere/warmboot $(AMS)/exosphere/program/rebootstub \ $(AMS)/thermosphere $(AMS)/fusee/fusee-primary $(AMS)/sept/sept-primary \ $(AMS)/sept/sept-secondary $(AMS)/emummc $(AMS)/mesosphere/kernel_ldr $(KIPDIRS) @@ -137,7 +137,7 @@ @$(MAKE) -C $(AMS)/fusee/fusee-primary all check_exosphere: - @$(MAKE) -C $(AMS)/exosphere2 all + @$(MAKE) -C $(AMS)/exosphere all check_sept: @$(MAKE) -C $(AMS)/sept all @@ -166,7 +166,7 @@ clean: @echo clean ... @$(MAKE) -C $(AMS)/fusee/fusee-primary clean - @$(MAKE) -C $(AMS)/exosphere2 clean + @$(MAKE) -C $(AMS)/exosphere clean @$(MAKE) -C $(AMS)/thermosphere clean @$(MAKE) -C $(AMS)/libraries clean @$(MAKE) -C $(AMS)/mesosphere clean diff --git a/sept/sept-secondary/Makefile b/sept/sept-secondary/Makefile index e8567f0..93624f7 100644 --- a/sept/sept-secondary/Makefile +++ b/sept/sept-secondary/Makefile @@ -77,7 +77,7 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(AMS)/exosphere2/program/rebootstub \ + $(AMS)/exosphere/program/rebootstub \ $(TOPDIR)/key_derivation export DEPSDIR := $(CURDIR)/$(BUILD) @@ -118,7 +118,7 @@ all: check_rebootstub check_key_derivation $(BUILD) check_rebootstub: - @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub all + @$(MAKE) -C $(AMS)/exosphere/program/rebootstub all check_key_derivation: @$(MAKE) -C key_derivation @@ -138,7 +138,7 @@ #--------------------------------------------------------------------------------- clean: @echo clean ... - @$(MAKE) -C $(AMS)/exosphere2/program/rebootstub clean + @$(MAKE) -C $(AMS)/exosphere/program/rebootstub clean @$(MAKE) -C key_derivation clean @rm -fr $(BUILD) $(TARGET).bin $(TARGET)_*.enc $(TARGET).elf