mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-05-29 14:05:17 -04:00
exo2: rename exosphere2 -> exosphere
This commit is contained in:
parent
282f8f6612
commit
42f1a3bf60
136 changed files with 15 additions and 15 deletions
138
exosphere/program/Makefile
Normal file
138
exosphere/program/Makefile
Normal 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
|
||||
#---------------------------------------------------------------------------------------
|
285
exosphere/program/program.ld
Normal file
285
exosphere/program/program.ld
Normal 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) }
|
||||
}
|
4
exosphere/program/program.specs
Normal file
4
exosphere/program/program.specs
Normal file
|
@ -0,0 +1,4 @@
|
|||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic -nostdlib -nostartfiles
|
122
exosphere/program/rebootstub/Makefile
Normal file
122
exosphere/program/rebootstub/Makefile
Normal 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
|
||||
#---------------------------------------------------------------------------------------
|
183
exosphere/program/rebootstub/rebootstub.ld
Normal file
183
exosphere/program/rebootstub/rebootstub.ld
Normal 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) }
|
||||
}
|
7
exosphere/program/rebootstub/rebootstub.specs
Normal file
7
exosphere/program/rebootstub/rebootstub.specs
Normal 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
|
|
@ -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
|
50
exosphere/program/rebootstub/source/rebootstub_main.s
Normal file
50
exosphere/program/rebootstub/source/rebootstub_main.s
Normal 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
|
61
exosphere/program/rebootstub/source/rebootstub_power_off.cpp
Normal file
61
exosphere/program/rebootstub/source/rebootstub_power_off.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
122
exosphere/program/sc7fw/Makefile
Normal file
122
exosphere/program/sc7fw/Makefile
Normal 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
|
||||
#---------------------------------------------------------------------------------------
|
183
exosphere/program/sc7fw/sc7fw.ld
Normal file
183
exosphere/program/sc7fw/sc7fw.ld
Normal 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) }
|
||||
}
|
7
exosphere/program/sc7fw/sc7fw.specs
Normal file
7
exosphere/program/sc7fw/sc7fw.specs
Normal 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
|
182
exosphere/program/sc7fw/source/sc7fw_dram.cpp
Normal file
182
exosphere/program/sc7fw/source/sc7fw_dram.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
26
exosphere/program/sc7fw/source/sc7fw_dram.hpp
Normal file
26
exosphere/program/sc7fw/source/sc7fw_dram.hpp
Normal 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();
|
||||
|
||||
}
|
22
exosphere/program/sc7fw/source/sc7fw_exception_vectors.s
Normal file
22
exosphere/program/sc7fw/source/sc7fw_exception_vectors.s
Normal 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
|
116
exosphere/program/sc7fw/source/sc7fw_main.cpp
Normal file
116
exosphere/program/sc7fw/source/sc7fw_main.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
35
exosphere/program/sc7fw/source/sc7fw_start.s
Normal file
35
exosphere/program/sc7fw/source/sc7fw_start.s
Normal 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
|
23
exosphere/program/sc7fw/source/sc7fw_util.hpp
Normal file
23
exosphere/program/sc7fw/source/sc7fw_util.hpp
Normal 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);
|
||||
|
||||
}
|
30
exosphere/program/sc7fw/source/sc7fw_util_asm.s
Normal file
30
exosphere/program/sc7fw/source/sc7fw_util_asm.s
Normal 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
|
42
exosphere/program/source/boot/secmon_boot.hpp
Normal file
42
exosphere/program/source/boot/secmon_boot.hpp
Normal 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);
|
||||
|
||||
}
|
23
exosphere/program/source/boot/secmon_boot_cache.cpp
Normal file
23
exosphere/program/source/boot/secmon_boot_cache.cpp
Normal 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"
|
||||
|
||||
}
|
23
exosphere/program/source/boot/secmon_boot_cache.hpp
Normal file
23
exosphere/program/source/boot/secmon_boot_cache.hpp
Normal 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"
|
||||
|
||||
}
|
34
exosphere/program/source/boot/secmon_boot_config.cpp
Normal file
34
exosphere/program/source/boot/secmon_boot_config.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
193
exosphere/program/source/boot/secmon_boot_functions.cpp
Normal file
193
exosphere/program/source/boot/secmon_boot_functions.cpp
Normal 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 ¶ms, pkg1::BootloaderState state) {
|
||||
/* Check NX Bootloader's state once per microsecond until it's advanced enough. */
|
||||
while (params.bootloader_state < state) {
|
||||
util::WaitMicroSeconds(1);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadBootConfig(const void *src) {
|
||||
pkg1::BootConfig * const dst = secmon::impl::GetBootConfigStorage();
|
||||
|
||||
if (pkg1::IsProduction()) {
|
||||
std::memset(dst, 0, sizeof(*dst));
|
||||
} else {
|
||||
hw::FlushDataCache(src, sizeof(*dst));
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
std::memcpy(dst, src, sizeof(*dst));
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyOrClearBootConfig() {
|
||||
/* On production hardware, the boot config is already cleared. */
|
||||
if (pkg1::IsProduction()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pkg1::BootConfig * const bc = secmon::impl::GetBootConfigStorage();
|
||||
|
||||
/* Determine if the bc is valid for the device. */
|
||||
bool valid_for_device = false;
|
||||
{
|
||||
const bool valid_signature = secmon::boot::VerifyBootConfigSignature(*bc, secmon::boot::GetBootConfigRsaModulus(), se::RsaSize);
|
||||
if (valid_signature) {
|
||||
valid_for_device = secmon::boot::VerifyBootConfigEcid(*bc);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the boot config is not valid for the device, clear its signed data. */
|
||||
if (!valid_for_device) {
|
||||
util::ClearMemory(std::addressof(bc->signed_data), sizeof(bc->signed_data));
|
||||
}
|
||||
}
|
||||
|
||||
void EnableTsc(u64 initial_tsc_value) {
|
||||
/* Write the initial value to the CNTCV registers. */
|
||||
const u32 lo = static_cast<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];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
42
exosphere/program/source/boot/secmon_boot_functions.hpp
Normal file
42
exosphere/program/source/boot/secmon_boot_functions.hpp
Normal 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 ¶ms, pkg1::BootloaderState state);
|
||||
|
||||
void LoadBootConfig(const void *src);
|
||||
void VerifyOrClearBootConfig();
|
||||
|
||||
void EnableTsc(u64 initial_tsc_value);
|
||||
|
||||
void WriteGpuCarveoutMagicNumbers();
|
||||
|
||||
void UpdateBootConfigForPackage2Header(const pkg2::Package2Header &header);
|
||||
void VerifyPackage2HeaderSignature(pkg2::Package2Header &header, bool verify);
|
||||
void DecryptPackage2Header(pkg2::Package2Meta *dst, const pkg2::Package2Meta &src, bool encrypted);
|
||||
|
||||
void VerifyPackage2Header(const pkg2::Package2Meta &meta);
|
||||
|
||||
void DecryptAndLoadPackage2Payloads(uintptr_t dst, const pkg2::Package2Meta &meta, uintptr_t src, bool encrypted);
|
||||
|
||||
void CheckVerifyResult(bool verify_result, pkg1::ErrorInfo error_info, const char *message);
|
||||
|
||||
}
|
77
exosphere/program/source/boot/secmon_boot_key_data.s
Normal file
77
exosphere/program/source/boot/secmon_boot_key_data.s
Normal 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
|
159
exosphere/program/source/boot/secmon_boot_rsa.cpp
Normal file
159
exosphere/program/source/boot/secmon_boot_rsa.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
386
exosphere/program/source/boot/secmon_boot_setup.cpp
Normal file
386
exosphere/program/source/boot/secmon_boot_setup.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
37
exosphere/program/source/boot/secmon_crt0.s
Normal file
37
exosphere/program/source/boot/secmon_crt0.s
Normal 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
|
46
exosphere/program/source/boot/secmon_crt0_cpp.cpp
Normal file
46
exosphere/program/source/boot/secmon_crt0_cpp.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
185
exosphere/program/source/boot/secmon_main.cpp
Normal file
185
exosphere/program/source/boot/secmon_main.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
152
exosphere/program/source/boot/secmon_make_page_table.cpp
Normal file
152
exosphere/program/source/boot/secmon_make_page_table.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
151
exosphere/program/source/boot/secmon_package2.cpp
Normal file
151
exosphere/program/source/boot/secmon_package2.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
26
exosphere/program/source/boot/secmon_size_data.s
Normal file
26
exosphere/program/source/boot/secmon_size_data.s
Normal 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
|
23
exosphere/program/source/secmon_cache.cpp
Normal file
23
exosphere/program/source/secmon_cache.cpp
Normal 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"
|
||||
|
||||
}
|
23
exosphere/program/source/secmon_cache.hpp
Normal file
23
exosphere/program/source/secmon_cache.hpp
Normal 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"
|
||||
|
||||
}
|
23
exosphere/program/source/secmon_cache.inc
Normal file
23
exosphere/program/source/secmon_cache.inc
Normal 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();
|
172
exosphere/program/source/secmon_cache_impl.inc
Normal file
172
exosphere/program/source/secmon_cache_impl.inc
Normal 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();
|
||||
}
|
192
exosphere/program/source/secmon_cpu_context.cpp
Normal file
192
exosphere/program/source/secmon_cpu_context.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
41
exosphere/program/source/secmon_cpu_context.hpp
Normal file
41
exosphere/program/source/secmon_cpu_context.hpp
Normal 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();
|
||||
|
||||
}
|
109
exosphere/program/source/secmon_error.cpp
Normal file
109
exosphere/program/source/secmon_error.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
24
exosphere/program/source/secmon_error.hpp
Normal file
24
exosphere/program/source/secmon_error.hpp
Normal 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();
|
||||
|
||||
}
|
326
exosphere/program/source/secmon_exception_vectors.s
Normal file
326
exosphere/program/source/secmon_exception_vectors.s
Normal 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
|
64
exosphere/program/source/secmon_interrupt_handler.cpp
Normal file
64
exosphere/program/source/secmon_interrupt_handler.cpp
Normal 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.");
|
||||
}
|
||||
|
||||
}
|
25
exosphere/program/source/secmon_interrupt_handler.hpp
Normal file
25
exosphere/program/source/secmon_interrupt_handler.hpp
Normal 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);
|
||||
|
||||
}
|
109
exosphere/program/source/secmon_key_storage.cpp
Normal file
109
exosphere/program/source/secmon_key_storage.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
47
exosphere/program/source/secmon_key_storage.hpp
Normal file
47
exosphere/program/source/secmon_key_storage.hpp
Normal 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);
|
||||
|
||||
}
|
298
exosphere/program/source/secmon_map.cpp
Normal file
298
exosphere/program/source/secmon_map.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
34
exosphere/program/source/secmon_map.hpp
Normal file
34
exosphere/program/source/secmon_map.hpp
Normal 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();
|
||||
|
||||
}
|
66
exosphere/program/source/secmon_misc.cpp
Normal file
66
exosphere/program/source/secmon_misc.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
34
exosphere/program/source/secmon_misc.hpp
Normal file
34
exosphere/program/source/secmon_misc.hpp
Normal 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();
|
||||
|
||||
}
|
80
exosphere/program/source/secmon_page_mapper.cpp
Normal file
80
exosphere/program/source/secmon_page_mapper.cpp
Normal 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>();
|
||||
}
|
||||
|
||||
}
|
76
exosphere/program/source/secmon_page_mapper.hpp
Normal file
76
exosphere/program/source/secmon_page_mapper.hpp
Normal 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();
|
||||
};
|
||||
|
||||
}
|
1220
exosphere/program/source/secmon_setup.cpp
Normal file
1220
exosphere/program/source/secmon_setup.cpp
Normal file
File diff suppressed because it is too large
Load diff
44
exosphere/program/source/secmon_setup.hpp
Normal file
44
exosphere/program/source/secmon_setup.hpp
Normal 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);
|
||||
|
||||
}
|
286
exosphere/program/source/secmon_setup_warm.cpp
Normal file
286
exosphere/program/source/secmon_setup_warm.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
26
exosphere/program/source/secmon_spinlock.hpp
Normal file
26
exosphere/program/source/secmon_spinlock.hpp
Normal 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);
|
||||
|
||||
}
|
44
exosphere/program/source/secmon_spinlock.s
Normal file
44
exosphere/program/source/secmon_spinlock.s
Normal 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
|
21
exosphere/program/source/secmon_stack_warm.s
Normal file
21
exosphere/program/source/secmon_stack_warm.s
Normal 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
|
212
exosphere/program/source/secmon_start_virtual.s
Normal file
212
exosphere/program/source/secmon_start_virtual.s
Normal 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
|
212
exosphere/program/source/secmon_start_warm.s
Normal file
212
exosphere/program/source/secmon_start_warm.s
Normal 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
|
95
exosphere/program/source/secmon_user_power_management.cpp
Normal file
95
exosphere/program/source/secmon_user_power_management.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
31
exosphere/program/source/secmon_user_power_management.hpp
Normal file
31
exosphere/program/source/secmon_user_power_management.hpp
Normal 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();
|
||||
|
||||
}
|
30
exosphere/program/source/smc/secmon_define_access_table.inc
Normal file
30
exosphere/program/source/smc/secmon_define_access_table.inc
Normal 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__);
|
|
@ -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__
|
|
@ -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__
|
|
@ -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__
|
|
@ -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 */
|
195
exosphere/program/source/smc/secmon_mc_access_table_data.inc
Normal file
195
exosphere/program/source/smc/secmon_mc_access_table_data.inc
Normal 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 */
|
|
@ -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 */
|
96
exosphere/program/source/smc/secmon_random_cache.cpp
Normal file
96
exosphere/program/source/smc/secmon_random_cache.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
28
exosphere/program/source/smc/secmon_random_cache.hpp
Normal file
28
exosphere/program/source/smc/secmon_random_cache.hpp
Normal 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);
|
||||
|
||||
}
|
768
exosphere/program/source/smc/secmon_smc_aes.cpp
Normal file
768
exosphere/program/source/smc/secmon_smc_aes.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
53
exosphere/program/source/smc/secmon_smc_aes.hpp
Normal file
53
exosphere/program/source/smc/secmon_smc_aes.hpp
Normal 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);
|
||||
|
||||
}
|
41
exosphere/program/source/smc/secmon_smc_carveout.cpp
Normal file
41
exosphere/program/source/smc/secmon_smc_carveout.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
24
exosphere/program/source/smc/secmon_smc_carveout.hpp
Normal file
24
exosphere/program/source/smc/secmon_smc_carveout.hpp
Normal 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);
|
||||
|
||||
}
|
51
exosphere/program/source/smc/secmon_smc_common.hpp
Normal file
51
exosphere/program/source/smc/secmon_smc_common.hpp
Normal 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;
|
||||
|
||||
}
|
94
exosphere/program/source/smc/secmon_smc_cpu_asm.s
Normal file
94
exosphere/program/source/smc/secmon_smc_cpu_asm.s
Normal 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
|
||||
|
||||
|
||||
|
201
exosphere/program/source/smc/secmon_smc_device_unique_data.cpp
Normal file
201
exosphere/program/source/smc/secmon_smc_device_unique_data.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
27
exosphere/program/source/smc/secmon_smc_error.cpp
Normal file
27
exosphere/program/source/smc/secmon_smc_error.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
24
exosphere/program/source/smc/secmon_smc_error.hpp
Normal file
24
exosphere/program/source/smc/secmon_smc_error.hpp
Normal 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);
|
||||
|
||||
}
|
314
exosphere/program/source/smc/secmon_smc_handler.cpp
Normal file
314
exosphere/program/source/smc/secmon_smc_handler.cpp
Normal 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
|
||||
}
|
||||
|
||||
}
|
24
exosphere/program/source/smc/secmon_smc_handler.hpp
Normal file
24
exosphere/program/source/smc/secmon_smc_handler.hpp
Normal 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);
|
||||
|
||||
}
|
391
exosphere/program/source/smc/secmon_smc_info.cpp
Normal file
391
exosphere/program/source/smc/secmon_smc_info.cpp
Normal 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];
|
||||
}
|
||||
|
||||
}
|
62
exosphere/program/source/smc/secmon_smc_info.hpp
Normal file
62
exosphere/program/source/smc/secmon_smc_info.hpp
Normal 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();
|
||||
|
||||
}
|
75
exosphere/program/source/smc/secmon_smc_memory_access.cpp
Normal file
75
exosphere/program/source/smc/secmon_smc_memory_access.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
26
exosphere/program/source/smc/secmon_smc_memory_access.hpp
Normal file
26
exosphere/program/source/smc/secmon_smc_memory_access.hpp
Normal 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);
|
||||
|
||||
}
|
501
exosphere/program/source/smc/secmon_smc_power_management.cpp
Normal file
501
exosphere/program/source/smc/secmon_smc_power_management.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
30
exosphere/program/source/smc/secmon_smc_power_management.hpp
Normal file
30
exosphere/program/source/smc/secmon_smc_power_management.hpp
Normal 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);
|
||||
|
||||
}
|
77
exosphere/program/source/smc/secmon_smc_random.cpp
Normal file
77
exosphere/program/source/smc/secmon_smc_random.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
25
exosphere/program/source/smc/secmon_smc_random.hpp
Normal file
25
exosphere/program/source/smc/secmon_smc_random.hpp
Normal 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);
|
||||
|
||||
}
|
184
exosphere/program/source/smc/secmon_smc_register_access.cpp
Normal file
184
exosphere/program/source/smc/secmon_smc_register_access.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
24
exosphere/program/source/smc/secmon_smc_register_access.hpp
Normal file
24
exosphere/program/source/smc/secmon_smc_register_access.hpp
Normal 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);
|
||||
|
||||
}
|
106
exosphere/program/source/smc/secmon_smc_result.cpp
Normal file
106
exosphere/program/source/smc/secmon_smc_result.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
31
exosphere/program/source/smc/secmon_smc_result.hpp
Normal file
31
exosphere/program/source/smc/secmon_smc_result.hpp
Normal 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);
|
||||
|
||||
}
|
357
exosphere/program/source/smc/secmon_smc_rsa.cpp
Normal file
357
exosphere/program/source/smc/secmon_smc_rsa.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
27
exosphere/program/source/smc/secmon_smc_rsa.hpp
Normal file
27
exosphere/program/source/smc/secmon_smc_rsa.hpp
Normal 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);
|
||||
|
||||
}
|
70
exosphere/program/source/smc/secmon_smc_se_lock.cpp
Normal file
70
exosphere/program/source/smc/secmon_smc_se_lock.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
31
exosphere/program/source/smc/secmon_smc_se_lock.hpp
Normal file
31
exosphere/program/source/smc/secmon_smc_se_lock.hpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue