exo2: rename exosphere2 -> exosphere

This commit is contained in:
Michael Scire 2020-06-11 01:53:10 -07:00 committed by SciresM
parent 282f8f6612
commit 42f1a3bf60
136 changed files with 15 additions and 15 deletions

138
exosphere/program/Makefile Normal file
View file

@ -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
#---------------------------------------------------------------------------------------

View file

@ -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) }
}

View file

@ -0,0 +1,4 @@
%rename link old_link
*link:
%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic -nostdlib -nostartfiles

View file

@ -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
#---------------------------------------------------------------------------------------

View file

@ -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) }
}

View file

@ -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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
.section .vectors, "ax", %progbits
.align 3
.global reset
reset:
b _ZN3ams10rebootstub4MainEv
.global _ZN3ams10rebootstub10RebootTypeE
_ZN3ams10rebootstub10RebootTypeE:
.word 0x00000001

View file

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

View file

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

View file

@ -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
#---------------------------------------------------------------------------------------

View file

@ -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) }
}

View file

@ -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

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
namespace ams::sc7fw {
void SaveEmcFsp();
void EnableSdramSelfRefresh();
void EnableEmcAllSegmentsRefresh();
void EnableDdrDeepPowerDown();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
.section .vectors, "ax", %progbits
.align 3
.global reset
reset:
b _start
b _ZN3ams5sc7fw16ExceptionHandlerEv

View file

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

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
namespace ams::sc7fw {
void SpinLoop(unsigned int num);
}

View file

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

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#include "secmon_boot_cache.hpp"
namespace ams::secmon::boot {
#include "../secmon_cache_impl.inc"
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
namespace ams::secmon::boot {
#include "../secmon_cache.inc"
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<void *>(dst), size, reinterpret_cast<void *>(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 &params, 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<u32>(initial_tsc_value >> 0);
const u32 hi = static_cast<u32>(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<u32>() - (0x004 / sizeof(*gpu_magic));
u32 *tsec_magic = MemoryRegionDramGpuCarveout.GetEndPointer<u32>() - (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<void *>(dst + meta.payload_offsets[i]), reinterpret_cast<void *>(src), meta.payload_sizes[i]);
}
src += meta.payload_sizes[i];
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
namespace ams::secmon::boot {
void ClearIram();
void WaitForNxBootloader(const pkg1::SecureMonitorParameters &params, 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);
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<se::Sha256Hash *>(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<s32>(++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<uintptr_t>(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<const u8 *>(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<void *>(msg), msg_size);
hw::DataSynchronizationBarrierInnerShareable();
/* Calculate the hash. */
se::Sha256Hash calc_hash;
se::CalculateSha256(std::addressof(calc_hash), reinterpret_cast<void *>(msg), msg_size);
/* Verify the result. */
return crypto::IsSameBytes(std::addressof(calc_hash), hash, sizeof(calc_hash));
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<u32>(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<hw::DataCacheLineSize, KeySize> 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>();
u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>();
/* 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>();
u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>();
/* Unmap. */
UnmapDramImpl(l1, l2_l3, l2_l3);
/* Ensure the mappings are consistent. */
secmon::boot::EnsureMappingConsistency();
}
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<pkg1::SecureMonitorParameters>();
secmon_params.secmon_start_time = *reinterpret_cast<volatile u32 *>(MemoryRegionPhysicalDeviceTimer.GetAddress() + 0x10);
/* Setup DMA controllers. */
SetupSocDmaControllers();
/* Make the page table. */
MakePageTable();
/* Setup memory controllers the MMU. */
SetupCpuMemoryControllersEnableMmu();
/* Clear bss. */
std::memset(reinterpret_cast<void *>(bss_start), 0, bss_end - bss_start);
std::memset(reinterpret_cast<void *>(boot_bss_start), 0, boot_bss_end - boot_bss_start);
/* Call init array. */
__libc_init_array();
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<pkg1::SecureMonitorParameters *>(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<u64>(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<pkg2::Package2Header>();
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();
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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>();
u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer<u64>();
MakePageTablesImpl(l1, l2_l3, l2_l3);
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<const void *>(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;
}
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#include "secmon_cache.hpp"
namespace ams::secmon {
#include "secmon_cache_impl.inc"
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
namespace ams::secmon {
#include "secmon_cache.inc"
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
void FlushEntireDataCache();
void FlushEntireDataCacheLocal();
void InvalidateEntireDataCache();
void EnsureMappingConsistency();
void EnsureMappingConsistency(uintptr_t address);
void EnsureInstructionConsistency();

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
namespace {
ALWAYS_INLINE int FloorLog2(int v) {
return BITSIZEOF(u32) - (hw::CountLeadingZeros(static_cast<u32>(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<hw::CsselrEl1::InD>(0);
csselr.Set<hw::CsselrEl1::Level>(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<hw::CcsidrEl1::NumSets>() + 1;
const int num_ways = ccsidr.Get<hw::CcsidrEl1::Associativity>() + 1;
const int line_size = ccsidr.Get<hw::CcsidrEl1::LineSize>() + 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<u64>(way) << way_shift) | (static_cast<u64>(set) << set_shift) | (static_cast<u64>(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<hw::CsselrEl1::InD>(0);
csselr.Set<hw::CsselrEl1::Level>(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<hw::CcsidrEl1::NumSets>() + 1;
const int num_ways = ccsidr.Get<hw::CcsidrEl1::Associativity>() + 1;
const int line_size = ccsidr.Get<hw::CcsidrEl1::LineSize>() + 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<u64>(way) << way_shift) | (static_cast<u64>(set) << set_shift) | (static_cast<u64>(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<hw::CsselrEl1::InD>(0);
csselr.Set<hw::CsselrEl1::Level>(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<hw::CcsidrEl1::NumSets>() + 1;
const int num_ways = ccsidr.Get<hw::CcsidrEl1::Associativity>() + 1;
const int line_size = ccsidr.Get<hw::CcsidrEl1::LineSize>() + 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<u64>(way) << way_shift) | (static_cast<u64>(set) << set_shift) | (static_cast<u64>(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<hw::ClidrEl1::Loc>());
}
void FlushEntireDataCacheLocal() {
util::BitPack32 clidr;
HW_CPU_GET_CLIDR_EL1(clidr);
FlushDataCacheFrom(clidr.Get<hw::ClidrEl1::Louis>());
}
void InvalidateEntireDataCache() {
util::BitPack32 clidr;
HW_CPU_GET_CLIDR_EL1(clidr);
InvalidateDataCacheTo(clidr.Get<hw::ClidrEl1::Loc>());
}
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();
}

View file

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

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<u32>(temp_reg >> 0);
*(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x14) = static_cast<u32>(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<u32>(temp_reg >> 0);
*(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x0C) = static_cast<u32>(temp_reg >> 32);
__asm__ __volatile__("mrs %0, elr_el3" : "=r"(temp_reg) :: "memory");
*(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(temp_reg >> 0);
*(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x14) = static_cast<u32>(temp_reg >> 32);
__asm__ __volatile__("mrs %0, far_el3" : "=r"(temp_reg) :: "memory");
*(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x18) = static_cast<u32>(temp_reg >> 0);
*(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x1C) = static_cast<u32>(temp_reg >> 32);
__asm__ __volatile__("mov %0, lr" : "=r"(temp_reg) :: "memory");
*(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x20) = static_cast<u32>(temp_reg >> 0);
*(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x24) = static_cast<u32>(temp_reg >> 32);
__asm__ __volatile__("mov %0, sp" : "=r"(temp_reg) :: "memory");
*(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x30) = static_cast<u32>(temp_reg >> 0);
*(volatile u32 *)(secmon::MemoryRegionVirtualDebug.GetAddress() + 0x34) = static_cast<u32>(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();
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
namespace ams::secmon {
void SetError(pkg1::ErrorInfo info);
NORETURN void ErrorReboot();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
/* Some macros taken from https://github.com/ARM-software/arm-trusted-firmware/blob/master/include/common/aarch64/asm_macros.S */
/*
* Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* Declare the exception vector table, enforcing it is aligned on a
* 2KB boundary, as required by the ARMv8 architecture.
* Use zero bytes as the fill value to be stored in the padding bytes
* so that it inserts illegal AArch64 instructions. This increases
* security, robustness and potentially facilitates debugging.
*/
.macro vector_base label, section_name=.vectors
.section \section_name, "ax"
.align 11, 0
\label:
.endm
/*
* Create an entry in the exception vector table, enforcing it is
* aligned on a 128-byte boundary, as required by the ARMv8 architecture.
* Use zero bytes as the fill value to be stored in the padding bytes
* so that it inserts illegal AArch64 instructions. This increases
* security, robustness and potentially facilitates debugging.
*/
.macro vector_entry label, section_name=.vectors
.cfi_sections .debug_frame
.section \section_name, "ax"
.align 7, 0
.type \label, %function
.func \label
.cfi_startproc
\label:
.endm
/*
* This macro verifies that the given vector doesnt exceed the
* architectural limit of 32 instructions. This is meant to be placed
* immediately after the last instruction in the vector. It takes the
* vector entry as the parameter
*/
.macro check_vector_size since
.endfunc
.cfi_endproc
.if (. - \since) > (32 * 4)
.error "Vector exceeds 32 instructions"
.endif
.endm
/* Actual Vectors for 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

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
namespace ams::secmon {
using InterruptHandler = void (*)();
void SetInterruptHandler(int interrupt_id, InterruptHandler handler);
}

View file

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

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<void *>(address), size / 2);
}
void ClearHigh(uintptr_t address, size_t size) {
/* Clear the high part. */
util::ClearMemory(reinterpret_cast<void *>(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>();
u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>();
/* 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>();
u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>();
/* 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<u64>();
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<void>(), MemoryRegionVirtualSmcUserPage.GetSize());
hw::DataSynchronizationBarrierInnerShareable();
u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>();
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<u64>();
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<void>(), MemoryRegionVirtualAtmosphereIramPage.GetSize());
hw::DataSynchronizationBarrierInnerShareable();
/* Unmap the page. */
u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>();
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<u64>();
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<void>(), MemoryRegionVirtualAtmosphereUserPage.GetSize());
hw::DataSynchronizationBarrierInnerShareable();
/* Unmap the page. */
u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer<u64>();
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);
}
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<hw::DbgAuthStatusEl1::Nsid>() == 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<u32>(g_deprecated_boot_reason_state) << 24) | (g_deprecated_boot_reason_value & 0x00FFFFFF);
}
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<void *>(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<MapSmcUserPage>();
}
bool AtmosphereIramPageMapper::Map() {
return this->MapImpl<MapAtmosphereIramPage>();
}
bool AtmosphereUserPageMapper::Map() {
return this->MapImpl<MapAtmosphereUserPage>();
}
AtmosphereIramPageMapper::~AtmosphereIramPageMapper() {
this->UnmapImpl<UnmapAtmosphereIramPage>();
}
AtmosphereUserPageMapper::~AtmosphereUserPageMapper() {
this->UnmapImpl<UnmapAtmosphereUserPage>();
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
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<auto F>
bool MapImpl() {
this->virtual_address = F(this->physical_address);
return this->virtual_address != 0;
}
template<auto F>
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();
};
}

File diff suppressed because it is too large Load diff

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<hw::CpuactlrEl1CortexA57::NonCacheableStreamingEnhancement>(1);
cpuactlr.Set<hw::CpuactlrEl1CortexA57::DisableLoadPassDmb>(1);
HW_CPU_SET_CPUACTLR_EL1(cpuactlr);
}
/* Set cpuectlr_el1. */
{
util::BitPack64 cpuectlr = {};
cpuectlr.Set<hw::CpuectlrEl1CortexA57::Smpen>(1);
cpuectlr.Set<hw::CpuectlrEl1CortexA57::L2LoadStoreDataPrefetchDistance>(3);
cpuectlr.Set<hw::CpuectlrEl1CortexA57::L2InstructionFetchPrefetchDistance>(3);
HW_CPU_SET_CPUECTLR_EL1(cpuectlr);
}
/* Prevent instruction reordering. */
hw::InstructionSynchronizationBarrier();
}
void SetupCpuEl3Controllers() {
/* Set scr_el3. */
{
util::BitPack32 scr = {};
scr.Set<hw::ScrEl3::Ns >(1); /* Set EL0/EL1 as Non-Secure. */
scr.Set<hw::ScrEl3::Irq >(0); /* IRQs are taken in IRQ mode. */
scr.Set<hw::ScrEl3::Fiq >(1); /* FIQs are taken in Monitor mode. */
scr.Set<hw::ScrEl3::Ea >(1); /* External aborts are taken in Monitor mode. */
scr.Set<hw::ScrEl3::Fw >(1); /* CPSR.F is non-secure writable. */
scr.Set<hw::ScrEl3::Aw >(1); /* CPSR.A is non-secure writable. */
scr.Set<hw::ScrEl3::Net >(0); /* This bit is not implemented. */
scr.Set<hw::ScrEl3::Smd >(0); /* Secure Monitor Call is allowed. */
scr.Set<hw::ScrEl3::Hce >(0); /* Hypervisor Calls are disabled. */ /* TODO: Enable for thermosphere? */
scr.Set<hw::ScrEl3::Sif >(1); /* Secure mode cannot fetch instructions from non-secure memory. */
scr.Set<hw::ScrEl3::RwCortexA53>(1); /* Reserved bit. N probably sets it because on Cortex A53, this sets kernel as aarch64. */
scr.Set<hw::ScrEl3::StCortexA53>(0); /* Reserved bit. On Cortex A53, this sets secure registers to EL3 only. */
scr.Set<hw::ScrEl3::Twi >(0); /* WFI is not trapped. */
scr.Set<hw::ScrEl3::Twe >(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<hw::TcrEl3::T0sz >(31); /* Configure TTBR0 addressed size to be 64 GiB */
tcr.Set<hw::TcrEl3::Irgn0>(1); /* Configure PTE walks as inner write-back write-allocate cacheable */
tcr.Set<hw::TcrEl3::Orgn0>(1); /* Configure PTE walks as outer write-back write-allocate cacheable */
tcr.Set<hw::TcrEl3::Sh0 >(3); /* Configure PTE walks as inner shareable */
tcr.Set<hw::TcrEl3::Tg0 >(0); /* Set TTBR0_EL3 granule as 4 KiB */
tcr.Set<hw::TcrEl3::Ps >(1); /* Set the physical addrss size as 36-bit (64 GiB) */
tcr.Set<hw::TcrEl3::Tbi >(0); /* Top byte is not ignored in addrss calculations */
HW_CPU_SET_TCR_EL3(tcr);
}
/* Clear cptr_el3. */
{
util::BitPack32 cptr = {};
cptr.Set<hw::CptrEl3::Tfp >(0); /* FP/SIMD instructions don't trap. */
cptr.Set<hw::CptrEl3::Tta >(0); /* Reserved bit (no trace functionality present). */
cptr.Set<hw::CptrEl3::Tcpac>(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<hw::SctlrEl3::M>(1); /* Globally enable the MMU. */
sctlr.Set<hw::SctlrEl3::A>(0); /* Disable alignment fault checking. */
sctlr.Set<hw::SctlrEl3::C>(1); /* Globally enable the data and unified caches. */
sctlr.Set<hw::SctlrEl3::Sa>(0); /* Disable stack alignment checking. */
sctlr.Set<hw::SctlrEl3::I>(1); /* Globally enable the instruction cache. */
sctlr.Set<hw::SctlrEl3::Wxn>(0); /* Do not force writable pages to be ExecuteNever. */
sctlr.Set<hw::SctlrEl3::Ee>(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>();
u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer<u64>();
/* 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();
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
namespace ams::secmon {
using SpinLockType = u32;
void AcquireSpinLock(SpinLockType &lock);
void ReleaseSpinLock(SpinLockType &lock);
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
.section .warmboot.data._ZN3ams6secmon23CommonWarmbootStackLockE, "aw", %progbits
.global _ZN3ams6secmon23CommonWarmbootStackLockE
_ZN3ams6secmon23CommonWarmbootStackLockE:
/* Define storage for the global common warmboot stack bakery lock. */
.word 0

View file

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

View file

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

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
namespace ams::secmon {
enum UserRebootType {
UserRebootType_None = 0,
UserRebootType_ToRcm = 1,
UserRebootType_ToPayload = 2,
};
void PerformUserRebootToRcm();
void PerformUserRebootToPayload();
void PerformUserShutDown();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
using __ACCESS_TABLE_NAME__ = AccessTable<__ACCESS_TABLE_ADDRESS__, [] {
/* Declare a table. */
std::array<u8, 0x80> 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__);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<ImportRsaKey>(static_cast<int>(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<int>(static_cast<int>(args.r[3]) - 1, pkg1::KeyGeneration_1_0_0);
const util::BitPack32 option = { static_cast<u32>(args.r[4]) };
const bool is_device_unique = option.Get<GenerateAesKekOption::IsDeviceUnique>();
const auto key_type = option.Get<GenerateAesKekOption::KeyTypeIndex>();
const auto seal_key = option.Get<GenerateAesKekOption::SealKeyIndex>();
const u32 reserved = option.Get<GenerateAesKekOption::Reserved>();
/* 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<u32>(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<ComputeAesOption::KeySlot>();
const auto cipher_mode = option.Get<ComputeAesOption::CipherModeIndex>();
/* 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<int>(static_cast<int>(args.r[3]) - 1, pkg1::KeyGeneration_1_0_0) : pkg1::KeyGeneration_1_0_0;
const auto which = static_cast<SpecificAesKey>(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<int>(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<u32>(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<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>();
const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>();
/* 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<u32>(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<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>();
const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>();
/* 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<SecureData>(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);
}
}

View file

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

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
#include "secmon_smc_common.hpp"
namespace ams::secmon::smc {
SmcResult SmcSetKernelCarveoutRegion(SmcArguments &args);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
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<u32>(-1),
PsciInvalidParameters = static_cast<u32>(-2),
PsciDenied = static_cast<u32>(-3),
PsciAlreadyOn = static_cast<u32>(-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;
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<u64>(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<u8>(device_id >> (BITSIZEOF(u64) - BITSIZEOF(u8)));
}
constexpr u64 EncodeDeviceId(u8 device_id_high, u64 device_id_low) {
return (static_cast<u64>(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<const u8 *>(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<const u8 *>(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<const u64 *>(static_cast<const void *>(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<u8 *>(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<hw::DataCacheLineSize, DeviceUniqueDataIvSize> 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<u64 *>(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);
}
}
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#include "../secmon_error.hpp"
#include "secmon_smc_error.hpp"
namespace ams::secmon::smc {
SmcResult SmcShowError(SmcArguments &args) {
/* TODO */
return SmcResult::NotImplemented;
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
#include "secmon_smc_common.hpp"
namespace ams::secmon::smc {
SmcResult SmcShowError(SmcArguments &args);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<SmcFunctionId::CallRange>() == 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<SmcFunctionId::FunctionId>();
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<int> 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<void>(), 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<u32 *>(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<HandlerType>(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<u64>(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<u64>(SmcResult::NotImplemented)) {
*(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xBBBBBBBB;
*(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(info.function_id);
for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) {
((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast<u32 *>(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<u64>(SmcResult::Success) && info.function_id != 0xC3000007 /* generate aes key fails during SetupKekAccessKeys */) {
*(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xCCCCCCCC;
*(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(info.function_id);
for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) {
((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast<u32 *>(std::addressof(args))[i];
}
*(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02;
*(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10;
util::WaitMicroSeconds(1000);
}
#endif
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
#include "secmon_smc_common.hpp"
namespace ams::secmon::smc {
using SmcHandler = SmcResult (*)(SmcArguments &args);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<DebugFillMemory::Next, 1, bool>;
using EnableUserPmuAccess = util::BitPack32::Field<EnableUserExceptionHandlers::Next, 1, bool>;
using IncreaseThreadResourceLimit = util::BitPack32::Field<EnableUserPmuAccess::Next, 1, bool>;
using Reserved4 = util::BitPack32::Field<IncreaseThreadResourceLimit::Next, 4, u32>;
using UseSecureMonitorPanicCall = util::BitPack32::Field<Reserved4::Next, 1, bool>;
using Reserved9 = util::BitPack32::Field<UseSecureMonitorPanicCall::Next, 7, u32>;
using MemorySize = util::BitPack32::Field<Reserved9::Next, 2, u32>; /* 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<KernelConfiguration::Flags1>(bcd.GetKernelFlags1());
value.Set<KernelConfiguration::Flags0>(bcd.GetKernelFlags0());
}
value.Set<KernelConfiguration::PhysicalMemorySize>(memory_size);
/* Exosphere extensions. */
const auto &sc = GetSecmonConfiguration();
if (!sc.DisableUserModeExceptionHandlers()) {
value.Set<KernelConfiguration::EnableUserExceptionHandlers>(true);
}
if (sc.EnableUserModePerformanceCounterAccess()) {
value.Set<KernelConfiguration::EnableUserPmuAccess>(true);
}
return value.value;
}
SmcResult GetConfig(SmcArguments &args, bool kern) {
switch (static_cast<ConfigItem>(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<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) |
(static_cast<u64>(GetKeyGeneration()) << 32) |
(static_cast<u64>(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<ConfigItem>(args.r[1])) {
case ConfigItem::IsChargerHiZModeEnabled:
/* Configure the HiZ mode. */
SetChargerHiZModeEnabled(static_cast<bool>(args.r[3]));
break;
case ConfigItem::ExosphereNeedsReboot:
if (soc_type == fuse::SocType_Erista) {
switch (static_cast<UserRebootType>(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<EmummcMmc>(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<u8 *>(static_cast<void *>(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];
}
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<u32>(args.r[4]) };
const auto copy_type = option.Get<IramCopyOption::CopyType>();
/* 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;
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
#include "secmon_smc_common.hpp"
namespace ams::secmon::smc {
/* This is an atmosphere extension smc. */
SmcResult SmcIramCopy(SmcArguments &args);
SmcResult SmcWriteAddress(SmcArguments &args);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<hw::DataCacheLineSize, se::AesBlockSize> key_source;
GenerateCryptographicallyRandomBytes(key_source, se::AesBlockSize);
const u32 * const key_source_32 = reinterpret_cast<const u32 *>(static_cast<u8 *>(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<se::Context>();
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<u8>() + MemoryRegionVirtualTzramVolatileData.GetSize() + MemoryRegionVirtualTzramVolatileStack.GetSize();
void * const tzram_save_dst = MemoryRegionVirtualIramSc7Work.GetPointer<void>();
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<void>();
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<u32>(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x0);
constexpr const u32 Sc7FirmwarePanicVector = static_cast<u32>(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<void>();
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<u32>(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<SuspendCpuPowerState::StateType>();
const auto state_id = power_state.Get<SuspendCpuPowerState::StateId>();
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<void *>(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;
}
}

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<hw::DataCacheLineSize, MaxRandomBytes> 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;
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
#include "secmon_smc_common.hpp"
namespace ams::secmon::smc {
SmcResult SmcGenerateRandomBytes(SmcArguments &args);
SmcResult SmcGenerateRandomBytesNonBlocking(SmcArguments &args);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#include "../secmon_error.hpp"
#include "secmon_smc_register_access.hpp"
namespace ams::secmon::smc {
namespace {
template<size_t N>
constexpr void SetRegisterTableAllowed(std::array<u8, N> &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<size_t N>
consteval std::pair<size_t, size_t> GetReducedAccessTableInfo(const std::array<u8, N> &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<size_t>(start), static_cast<size_t>(end));
}
}
return std::make_pair(static_cast<size_t>(0), static_cast<size_t>(end));
}
}
/* All empty perm table is disallowed. */
AMS_ASSUME(false);
}
template<u32 _Address, auto RawTable>
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<u8, ReducedAccessTableSize> {
std::array<u8, ReducedAccessTableSize> 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<u32>(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<u32>(0)) {
out = reg::Read(virtual_address);
}
if (mask != static_cast<u32>(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;
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
#include "secmon_smc_common.hpp"
namespace ams::secmon::smc {
SmcResult SmcReadWriteRegister(SmcArguments &args);
}

View file

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

View file

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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#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<u8>(ImportRsaKey_Lotus),
static_cast<u8>(ImportRsaKey_Ssl),
static_cast<u8>(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<u32>(args.r[3]) };
const auto mode = option.Get<ModularExponentiateByStorageKeyOption::Mode>();
const auto reserved = option.Get<PrepareEsDeviceUniqueKeyOption::Reserved>();
/* 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<ImportRsaKey>(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<u32>(args.r[7]) };
const auto generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, option.Get<PrepareEsDeviceUniqueKeyOption::KeyGeneration>() - 1) : 0;
const auto type = option.Get<PrepareEsDeviceUniqueKeyOption::Type>();
const auto reserved = option.Get<PrepareEsDeviceUniqueKeyOption::Reserved>();
/* 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);
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
#include "secmon_smc_common.hpp"
namespace ams::secmon::smc {
SmcResult SmcModularExponentiate(SmcArguments &args);
SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args);
SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args);
}

View file

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

View file

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

Some files were not shown because too many files have changed in this diff Show more