From 7bcd5c6e3b321f5bd225897aa21302736615ae4b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 15 Nov 2020 12:58:13 -0800 Subject: [PATCH] exo: implement start of mariko fatal handler --- .../loader_stub/source/secmon_loader_main.cpp | 1 - exosphere/mariko_fatal/Makefile | 138 ++++++++ exosphere/mariko_fatal/mariko_fatal.ld | 227 +++++++++++++ exosphere/mariko_fatal/mariko_fatal.specs | 7 + .../mariko_fatal/source/fatal_abort_impl.cpp | 29 ++ exosphere/mariko_fatal/source/fatal_crt0.s | 32 ++ .../mariko_fatal/source/fatal_crt0_cpp.cpp | 30 ++ .../source/fatal_device_page_table.cpp | 307 ++++++++++++++++++ .../source/fatal_device_page_table.hpp | 23 ++ exosphere/mariko_fatal/source/fatal_main.cpp | 50 +++ exosphere/mariko_fatal/source/fatal_sdmmc.cpp | 50 +++ exosphere/mariko_fatal/source/fatal_sdmmc.hpp | 23 ++ exosphere/program/program.ld | 19 +- .../source/boot/secmon_boot_functions.cpp | 2 +- .../source/secmon_exception_handler.cpp | 96 ++++++ .../program/source/secmon_exception_vectors.s | 6 + .../source/secmon_user_power_management.cpp | 20 ++ .../source/secmon_user_power_management.hpp | 8 +- .../program/source/smc/secmon_smc_info.cpp | 11 +- .../hw/hw_arm64_system_registers.hpp | 18 + .../exosphere/secmon/secmon_memory_layout.hpp | 12 +- .../stratosphere/ams/ams_exosphere_api.hpp | 1 + .../source/ams/ams_exosphere_api.cpp | 4 + .../libvapours/source/dd/dd_io_mapping.cpp | 17 +- .../sdmmc/impl/sdmmc_i_host_controller.hpp | 6 +- .../source/bpc_mitm/bpc_ams_power_utils.cpp | 2 +- stratosphere/boot/source/boot_main.cpp | 2 + stratosphere/boot/source/boot_power_utils.cpp | 21 +- 28 files changed, 1138 insertions(+), 24 deletions(-) create mode 100644 exosphere/mariko_fatal/Makefile create mode 100644 exosphere/mariko_fatal/mariko_fatal.ld create mode 100644 exosphere/mariko_fatal/mariko_fatal.specs create mode 100644 exosphere/mariko_fatal/source/fatal_abort_impl.cpp create mode 100644 exosphere/mariko_fatal/source/fatal_crt0.s create mode 100644 exosphere/mariko_fatal/source/fatal_crt0_cpp.cpp create mode 100644 exosphere/mariko_fatal/source/fatal_device_page_table.cpp create mode 100644 exosphere/mariko_fatal/source/fatal_device_page_table.hpp create mode 100644 exosphere/mariko_fatal/source/fatal_main.cpp create mode 100644 exosphere/mariko_fatal/source/fatal_sdmmc.cpp create mode 100644 exosphere/mariko_fatal/source/fatal_sdmmc.hpp create mode 100644 exosphere/program/source/secmon_exception_handler.cpp diff --git a/exosphere/loader_stub/source/secmon_loader_main.cpp b/exosphere/loader_stub/source/secmon_loader_main.cpp index 40b16c3fa..70dc8555a 100644 --- a/exosphere/loader_stub/source/secmon_loader_main.cpp +++ b/exosphere/loader_stub/source/secmon_loader_main.cpp @@ -24,7 +24,6 @@ namespace ams::secmon::loader { /* Uncompress the program image. */ Uncompress(secmon::MemoryRegionPhysicalTzramFullProgramImage.GetPointer(), secmon::MemoryRegionPhysicalTzramFullProgramImage.GetSize(), program_lz4, program_lz4_size); - /* Copy the boot image to the end of IRAM */ u8 *relocated_boot_code = secmon::MemoryRegionPhysicalIramBootCodeImage.GetEndPointer() - boot_code_lz4_size; std::memcpy(relocated_boot_code, boot_code_lz4, boot_code_lz4_size); diff --git a/exosphere/mariko_fatal/Makefile b/exosphere/mariko_fatal/Makefile new file mode 100644 index 000000000..86e5211d2 --- /dev/null +++ b/exosphere/mariko_fatal/Makefile @@ -0,0 +1,138 @@ +#--------------------------------------------------------------------------------- +# Define the atmosphere board and cpu +#--------------------------------------------------------------------------------- +export ATMOSPHERE_BOARD := nx-hac-001 +export ATMOSPHERE_CPU := arm-cortex-a57 + +#--------------------------------------------------------------------------------- +# pull in common atmosphere configuration +#--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) +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 ($(__RECURSIVE__),1) +#--------------------------------------------------------------------------------- + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),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)/$(ATMOSPHERE_LIBRARY_DIR)) + +export TOPDIR := $(CURRENT_DIRECTORY) + +OUTPUT_BASE := $(TOPDIR)/$(notdir $(TOPDIR)) + +#--------------------------------------------------------------------------------- + +ATMOSPHERE_BUILD_CONFIGS := +all: release + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): check_libexo_$(strip $1) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) + @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(OUTPUT_BASE)$(strip $2) $(3) \ + DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + LIBEXOSPHERE_NAME=exosphere$(strip $2) \ + --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + -f $$(THIS_MAKEFILE) + +check_libexo_$(strip $1): + @$$(MAKE) --no-print-directory -C $$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere $$(ATMOSPHERE_ARCH_NAME)-$(strip $1) + +clean-$(strip $1): + @echo clean $(strip $1) ... + @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(OUTPUT_BASE)$(strip $2).bin $$(OUTPUT_BASE)$(strip $2).elf + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, , \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_DEBUGGING" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_AUDITING" \ +)) + +$(ATMOSPHERE_BUILD_DIR)/%: + @[ -d $@ ] || mkdir -p $@ + +#--------------------------------------------------------------------------------- +clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config)) + +.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- + +$(OUTPUT).bin : $(OUTPUT).elf + $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@ + @echo built ... $(notdir $@) + +$(OUTPUT).elf : $(OFILES) + +$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/lib$(LIBEXOSPHERE_NAME).a + +%.elf: + @echo linking $(notdir $@) + $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + @$(NM) -CSn $@ > $(notdir $*.lst) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h: %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/exosphere/mariko_fatal/mariko_fatal.ld b/exosphere/mariko_fatal/mariko_fatal.ld new file mode 100644 index 000000000..a89dd4101 --- /dev/null +++ b/exosphere/mariko_fatal/mariko_fatal.ld @@ -0,0 +1,227 @@ +OUTPUT_ARCH(aarch64) +ENTRY(_start) + +MEMORY +{ + NULL : ORIGIN = 0, LENGTH = 4K + mariko_tzram : ORIGIN = 0x1F00D0000, LENGTH = 128K +} + + +SECTIONS +{ + /* =========== CODE section =========== */ + PROVIDE(__start__ = ORIGIN(mariko_tzram)); + . = __start__; + __code_start = . ; + + .crt0 : + { + KEEP (*(.crt0 .crt0.*)) + KEEP (fatal_crt0_cpp.o(.text*)) + *(.crt0.rodata*) + fatal_crt0_cpp.o(.rodata*) + *(.crt0.data*) + fatal_crt0_cpp.o(.data*) + . = ALIGN(8); + } >mariko_tzram + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } >mariko_tzram + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } >mariko_tzram + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } >mariko_tzram + + .ctors ALIGN(8) : + { + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >mariko_tzram + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >mariko_tzram + + .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); + } >mariko_tzram + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } >mariko_tzram + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } >mariko_tzram + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } >mariko_tzram + + + /* =========== RODATA section =========== */ + . = ALIGN(8); + __rodata_start = . ; + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } >mariko_tzram + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >mariko_tzram + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >mariko_tzram + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >mariko_tzram + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >mariko_tzram + + .hash : { *(.hash) } >mariko_tzram + + /* =========== DATA section =========== */ + . = ALIGN(8); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >mariko_tzram + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >mariko_tzram + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >mariko_tzram + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >mariko_tzram + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } >mariko_tzram + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } >mariko_tzram + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } >mariko_tzram + + .ctors ALIGN(8) : + { + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >mariko_tzram + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >mariko_tzram + + __got_start__ = .; + + .got : { *(.got) *(.igot) } >mariko_tzram + .got.plt : { *(.got.plt) *(.igot.plt) } >mariko_tzram + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >mariko_tzram + + __bss_start__ = .; + .bss ALIGN(8) : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + } >mariko_tzram + __bss_end__ = .; + + __end__ = ABSOLUTE(.) ; + + /* ================== + ==== Metadata ==== + ================== */ + + /* Discard sections that difficult post-processing */ + /DISCARD/ : { *(.group .comment .note .interp) } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } +} \ No newline at end of file diff --git a/exosphere/mariko_fatal/mariko_fatal.specs b/exosphere/mariko_fatal/mariko_fatal.specs new file mode 100644 index 000000000..e9b2e9fdf --- /dev/null +++ b/exosphere/mariko_fatal/mariko_fatal.specs @@ -0,0 +1,7 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /mariko_fatal.ld) --gc-sections --nmagic -nostdlib -nostartfiles + +*startfile: +crti%O%s crtbegin%O%s diff --git a/exosphere/mariko_fatal/source/fatal_abort_impl.cpp b/exosphere/mariko_fatal/source/fatal_abort_impl.cpp new file mode 100644 index 000000000..3ee576e23 --- /dev/null +++ b/exosphere/mariko_fatal/source/fatal_abort_impl.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::diag { + + void AbortImpl() { + AMS_SECMON_LOG("AbortImpl was called\n"); + + /* TODO: Reboot */ + AMS_INFINITE_LOOP(); + } + + #include + +} diff --git a/exosphere/mariko_fatal/source/fatal_crt0.s b/exosphere/mariko_fatal/source/fatal_crt0.s new file mode 100644 index 000000000..a49cacffe --- /dev/null +++ b/exosphere/mariko_fatal/source/fatal_crt0.s @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.section .crt0.text.start, "ax", %progbits +.align 6 +.global _start +_start: + /* Set the stack pointer to a temporary location. */ + ldr x20, =0x1F00FC000 + mov sp, x20 + + /* Initialize all memory to expected state. */ + ldr x0, =__bss_start__ + ldr x1, =__bss_end__ + bl _ZN3ams6secmon5fatal10InitializeEmm + + /* Jump to the fatal program. */ + ldr x16, =_ZN3ams6secmon5fatal4MainEv + br x16 diff --git a/exosphere/mariko_fatal/source/fatal_crt0_cpp.cpp b/exosphere/mariko_fatal/source/fatal_crt0_cpp.cpp new file mode 100644 index 000000000..bae3a9cb0 --- /dev/null +++ b/exosphere/mariko_fatal/source/fatal_crt0_cpp.cpp @@ -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 . + */ +#include + +extern "C" void __libc_init_array(); + +namespace ams::secmon::fatal { + + void Initialize(uintptr_t bss_start, size_t bss_end) { + /* Clear bss. */ + std::memset(reinterpret_cast(bss_start), 0, bss_end - bss_start); + + /* Call init array. */ + __libc_init_array(); + } + +} \ No newline at end of file diff --git a/exosphere/mariko_fatal/source/fatal_device_page_table.cpp b/exosphere/mariko_fatal/source/fatal_device_page_table.cpp new file mode 100644 index 000000000..84d713210 --- /dev/null +++ b/exosphere/mariko_fatal/source/fatal_device_page_table.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::secmon::fatal { + + namespace { + + /* Definitions. */ + constexpr size_t PageDirectorySize = mmu::PageSize; + constexpr size_t PageTableSize = mmu::PageSize; + static_assert(PageDirectorySize == mmu::PageSize); + + using DeviceVirtualAddress = u64; + + constexpr size_t AsidCount = 0x80; + constexpr size_t PhysicalAddressBits = 34; + constexpr size_t PhysicalAddressMask = (1ul << PhysicalAddressBits) - 1ul; + constexpr size_t DeviceVirtualAddressBits = 34; + constexpr size_t DeviceVirtualAddressMask = (1ul << DeviceVirtualAddressBits) - 1ul; + + constexpr size_t DevicePageBits = 12; + constexpr size_t DevicePageSize = (1ul << DevicePageBits); + static_assert(DevicePageSize == mmu::PageSize); + + constexpr size_t DeviceLargePageBits = 22; + constexpr size_t DeviceLargePageSize = (1ul << DeviceLargePageBits); + static_assert(DeviceLargePageSize % DevicePageSize == 0); + + constexpr size_t DeviceRegionBits = 32; + constexpr size_t DeviceRegionSize = (1ul << DeviceRegionBits); + static_assert(DeviceRegionSize % DeviceLargePageSize == 0); + + constexpr const uintptr_t MC = secmon::MemoryRegionVirtualDeviceMemoryController.GetAddress(); + + constexpr size_t TableCount = (1ul << DeviceVirtualAddressBits) / DeviceRegionSize; + + constexpr u8 SdmmcAsid = 1; + + constexpr u32 SdmmcAsidRegisterValue = [] { + u32 value = 0x80000000u; + for (size_t t = 0; t < TableCount; t++) { + value |= (SdmmcAsid << (BITSIZEOF(u8) * t)); + } + return value; + }(); + + constexpr dd::PhysicalAddress SdmmcL0PageTablePhysical = MemoryRegionPhysicalDramSdmmc1L0DevicePageTable.GetAddress(); + constexpr dd::PhysicalAddress SdmmcL1PageTablePhysical = MemoryRegionPhysicalDramSdmmc1L1DevicePageTable.GetAddress(); + + /* Types. */ + class EntryBase { + protected: + enum Bit : u32 { + Bit_Table = 28, + Bit_NonSecure = 29, + Bit_Writeable = 30, + Bit_Readable = 31, + }; + private: + u32 value; + protected: + constexpr ALWAYS_INLINE u32 SelectBit(Bit n) const { + return (this->value & (1u << n)); + } + + constexpr ALWAYS_INLINE bool GetBit(Bit n) const { + return this->SelectBit(n) != 0; + } + + static constexpr ALWAYS_INLINE u32 EncodeBit(Bit n, bool en) { + return en ? (1u << n) : 0; + } + + static constexpr ALWAYS_INLINE u32 EncodeValue(bool r, bool w, bool ns, dd::PhysicalAddress addr, bool t) { + return EncodeBit(Bit_Readable, r) | EncodeBit(Bit_Writeable, w) | EncodeBit(Bit_NonSecure, ns) | EncodeBit(Bit_Table, t) | static_cast(addr >> DevicePageBits); + } + + ALWAYS_INLINE void SetValue(u32 v) { + /* Prevent re-ordering around entry modifications. */ + __asm__ __volatile__("" ::: "memory"); + this->value = v; + __asm__ __volatile__("" ::: "memory"); + } + public: + static constexpr ALWAYS_INLINE u32 EncodePtbDataValue(dd::PhysicalAddress addr) { + return EncodeValue(true, true, true, addr, false); + } + public: + constexpr ALWAYS_INLINE bool IsNonSecure() const { return this->GetBit(Bit_NonSecure); } + constexpr ALWAYS_INLINE bool IsWriteable() const { return this->GetBit(Bit_Writeable); } + constexpr ALWAYS_INLINE bool IsReadable() const { return this->GetBit(Bit_Readable); } + constexpr ALWAYS_INLINE bool IsValid() const { return this->IsWriteable() || this->IsReadable(); } + + constexpr ALWAYS_INLINE u32 GetAttributes() const { return this->SelectBit(Bit_NonSecure) | this->SelectBit(Bit_Writeable) | this->SelectBit(Bit_Readable); } + + constexpr ALWAYS_INLINE dd::PhysicalAddress GetPhysicalAddress() const { return (static_cast(this->value) << DevicePageBits) & PhysicalAddressMask; } + + ALWAYS_INLINE void Invalidate() { this->SetValue(0); } + }; + + class PageDirectoryEntry : public EntryBase { + public: + constexpr ALWAYS_INLINE bool IsTable() const { return this->GetBit(Bit_Table); } + + ALWAYS_INLINE void SetTable(bool r, bool w, bool ns, dd::PhysicalAddress addr) { + AMS_ASSERT(util::IsAligned(addr, DevicePageSize)); + this->SetValue(EncodeValue(r, w, ns, addr, true)); + } + + ALWAYS_INLINE void SetLargePage(bool r, bool w, bool ns, dd::PhysicalAddress addr) { + AMS_ASSERT(util::IsAligned(addr, DeviceLargePageSize)); + this->SetValue(EncodeValue(r, w, ns, addr, false)); + } + }; + + class PageTableEntry : public EntryBase { + public: + ALWAYS_INLINE void SetPage(bool r, bool w, bool ns, dd::PhysicalAddress addr) { + AMS_ASSERT(util::IsAligned(addr, DevicePageSize)); + this->SetValue(EncodeValue(r, w, ns, addr, true)); + } + }; + + /* Memory controller access functionality. */ + void WriteMcRegister(size_t offset, u32 value) { + reg::Write(MC + offset, value); + } + + u32 ReadMcRegister(size_t offset) { + return reg::Read(MC + offset); + } + + /* Memory controller utilities. */ + void SmmuSynchronizationBarrier() { + ReadMcRegister(MC_SMMU_CONFIG); + } + + void InvalidatePtc() { + WriteMcRegister(MC_SMMU_PTC_FLUSH_0, 0); + } + + void InvalidatePtc(dd::PhysicalAddress address) { + WriteMcRegister(MC_SMMU_PTC_FLUSH_1, (static_cast(address) >> 32)); + WriteMcRegister(MC_SMMU_PTC_FLUSH_0, (address & 0xFFFFFFF0u) | 1u); + } + + enum TlbFlushVaMatch : u32 { + TlbFlushVaMatch_All = 0, + TlbFlushVaMatch_Section = 2, + TlbFlushVaMatch_Group = 3, + }; + + static constexpr ALWAYS_INLINE u32 EncodeTlbFlushValue(bool match_asid, u8 asid, dd::PhysicalAddress address, TlbFlushVaMatch match) { + return ((match_asid ? 1u : 0u) << 31) | ((asid & 0x7F) << 24) | (((address & 0xFFC00000u) >> DevicePageBits)) | (match); + } + + void InvalidateTlb() { + return WriteMcRegister(MC_SMMU_TLB_FLUSH, EncodeTlbFlushValue(false, 0, 0, TlbFlushVaMatch_All)); + } + + void InvalidateTlb(u8 asid) { + return WriteMcRegister(MC_SMMU_TLB_FLUSH, EncodeTlbFlushValue(true, asid, 0, TlbFlushVaMatch_All)); + } + + void InvalidateTlbSection(u8 asid, dd::PhysicalAddress address) { + return WriteMcRegister(MC_SMMU_TLB_FLUSH, EncodeTlbFlushValue(true, asid, address, TlbFlushVaMatch_Section)); + } + + void SetTable(u8 asid, dd::PhysicalAddress address) { + /* Write the table address. */ + { + WriteMcRegister(MC_SMMU_PTB_ASID, asid); + WriteMcRegister(MC_SMMU_PTB_DATA, EntryBase::EncodePtbDataValue(address)); + + SmmuSynchronizationBarrier(); + } + + /* Ensure consistency. */ + InvalidatePtc(); + InvalidateTlb(asid); + SmmuSynchronizationBarrier(); + } + + void MapImpl(dd::PhysicalAddress phys_addr, size_t size, DeviceVirtualAddress address) { + /* Cache permissions. */ + const bool read = true; + const bool write = true; + + /* Walk the directory. */ + u64 remaining = size; + while (remaining > 0) { + const size_t l1_index = (address % DeviceRegionSize) / DeviceLargePageSize; + const size_t l2_index = (address % DeviceLargePageSize) / DevicePageSize; + + /* Get and validate l1. */ + PageDirectoryEntry *l1 = static_cast(MemoryRegionVirtualDramSdmmc1L0DevicePageTable.GetPointer()); + AMS_ASSERT(l1 != nullptr); + + /* Setup an l1 table/entry, if needed. */ + if (!l1[l1_index].IsTable()) { + /* Check that an entry doesn't already exist. */ + AMS_ASSERT(!l1[l1_index].IsValid()); + + /* If we can make an l1 entry, do so. */ + if (l2_index == 0 && util::IsAligned(phys_addr, DeviceLargePageSize) && remaining >= DeviceLargePageSize) { + /* Set the large page. */ + l1[l1_index].SetLargePage(read, write, true, phys_addr); + hw::FlushDataCache(std::addressof(l1[l1_index]), sizeof(PageDirectoryEntry)); + + /* Synchronize. */ + InvalidatePtc(SdmmcL1PageTablePhysical); + InvalidateTlbSection(SdmmcAsid, address); + SmmuSynchronizationBarrier(); + + /* Advance. */ + phys_addr += DeviceLargePageSize; + address += DeviceLargePageSize; + remaining -= DeviceLargePageSize; + continue; + } else { + /* Make an l1 table. */ + std::memset(MemoryRegionVirtualDramSdmmc1L1DevicePageTable.GetPointer(), 0, mmu::PageSize); + hw::FlushDataCache(MemoryRegionVirtualDramSdmmc1L1DevicePageTable.GetPointer(), mmu::PageSize); + + /* Set the l1 table. */ + l1[l1_index].SetTable(true, true, true, SdmmcL1PageTablePhysical); + hw::FlushDataCache(std::addressof(l1[l1_index]), sizeof(PageDirectoryEntry)); + + /* Synchronize. */ + InvalidatePtc(SdmmcL1PageTablePhysical); + InvalidateTlbSection(SdmmcAsid, address); + SmmuSynchronizationBarrier(); + } + } + + /* If we get to this point, l1 must be a table. */ + AMS_ASSERT(l1[l1_index].IsTable()); + + /* Map l2 entries. */ + { + PageTableEntry *l2 = static_cast(MemoryRegionVirtualDramSdmmc1L1DevicePageTable.GetPointer()); + + const size_t remaining_in_entry = (PageTableSize / sizeof(PageTableEntry)) - l2_index; + const size_t map_count = std::min(remaining_in_entry, remaining / DevicePageSize); + + /* Set the entries. */ + for (size_t i = 0; i < map_count; ++i) { + AMS_ASSERT(!l2[l2_index + i].IsValid()); + l2[l2_index + i].SetPage(read, write, true, phys_addr + DevicePageSize * i); + } + hw::FlushDataCache(std::addressof(l2[l2_index]), map_count * sizeof(PageTableEntry)); + + /* Invalidate the page table cache. */ + for (size_t i = util::AlignDown(l2_index, 4); i <= util::AlignDown(l2_index + map_count - 1, 4); i += 4) { + InvalidatePtc(SdmmcL1PageTablePhysical + i * sizeof(PageTableEntry)); + } + + /* Synchronize. */ + InvalidateTlbSection(SdmmcAsid, address); + SmmuSynchronizationBarrier(); + + /* Advance. */ + phys_addr += map_count * DevicePageSize; + address += map_count * DevicePageSize; + remaining -= map_count * DevicePageSize; + } + } + } + + } + + void InitializeDevicePageTableForSdmmc1() { + /* Configure sdmmc to use our new page table. */ + WriteMcRegister(MC_SMMU_SDMMC1A_ASID, SdmmcAsidRegisterValue); + SmmuSynchronizationBarrier(); + + /* Ensure consistency. */ + InvalidatePtc(); + InvalidateTlb(); + SmmuSynchronizationBarrier(); + + /* Clear the L0 Page Table. */ + std::memset(MemoryRegionVirtualDramSdmmc1L0DevicePageTable.GetPointer(), 0, mmu::PageSize); + hw::FlushDataCache(MemoryRegionVirtualDramSdmmc1L0DevicePageTable.GetPointer(), mmu::PageSize); + + /* Set the page table for the sdmmc asid. */ + SetTable(SdmmcAsid, SdmmcL0PageTablePhysical); + + /* Map the appropriate region into the asid. */ + MapImpl(MemoryRegionPhysicalDramSdmmcMappedData.GetAddress(), MemoryRegionPhysicalDramSdmmcMappedData.GetSize(), MemoryRegionVirtualDramSdmmcMappedData.GetAddress()); + } + +} diff --git a/exosphere/mariko_fatal/source/fatal_device_page_table.hpp b/exosphere/mariko_fatal/source/fatal_device_page_table.hpp new file mode 100644 index 000000000..de5c70db2 --- /dev/null +++ b/exosphere/mariko_fatal/source/fatal_device_page_table.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon::fatal { + + void InitializeDevicePageTableForSdmmc1(); + +} diff --git a/exosphere/mariko_fatal/source/fatal_main.cpp b/exosphere/mariko_fatal/source/fatal_main.cpp new file mode 100644 index 000000000..76dee3e8c --- /dev/null +++ b/exosphere/mariko_fatal/source/fatal_main.cpp @@ -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 . + */ +#include +#include "fatal_sdmmc.hpp" + +namespace ams::secmon::fatal { + + 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(), MemoryRegionVirtualDeviceSecurityEngine2.GetAddress()); + uart::SetRegisterAddress(MemoryRegionVirtualDeviceUart.GetAddress()); + wdt::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress()); + util::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress()); + + /* Ensure that the log library is initialized. */ + log::Initialize(); + + AMS_SECMON_LOG("%s\n", "Fatal start."); + + /* Initialize the sdmmc driver. */ + Result result = InitializeSdCard(); + AMS_SECMON_LOG("InitializeSdCard: %08x\n", result.GetValue()); + + /* TODO */ + AMS_INFINITE_LOOP(); + } + +} diff --git a/exosphere/mariko_fatal/source/fatal_sdmmc.cpp b/exosphere/mariko_fatal/source/fatal_sdmmc.cpp new file mode 100644 index 000000000..47293bea2 --- /dev/null +++ b/exosphere/mariko_fatal/source/fatal_sdmmc.cpp @@ -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 . + */ +#include +#include "fatal_device_page_table.hpp" + +namespace ams::secmon::fatal { + + namespace { + + constexpr inline auto Port = sdmmc::Port_SdCard0; + + ALWAYS_INLINE u8 *GetSdCardWorkBuffer() { + return MemoryRegionVirtualDramSdmmcMappedData.GetPointer() + MemoryRegionVirtualDramSdmmcMappedData.GetSize() - mmu::PageSize; + } + + } + + Result InitializeSdCard() { + /* Map main memory for the sdmmc device. */ + AMS_SECMON_LOG("%s\n", "Initializing Device Page Table."); + InitializeDevicePageTableForSdmmc1(); + AMS_SECMON_LOG("%s\n", "Initialized Device Page Table."); + + /* Initialize sdmmc library. */ + sdmmc::Initialize(Port); + AMS_SECMON_LOG("%s\n", "Initialized Sdmmc Port."); + + sdmmc::SetSdCardWorkBuffer(Port, GetSdCardWorkBuffer(), sdmmc::SdCardWorkBufferSize); + AMS_SECMON_LOG("%s\n", "Set SD Card Work Buffer."); + + R_TRY(sdmmc::Activate(Port)); + AMS_SECMON_LOG("%s\n", "Activated."); + + return ResultSuccess(); + } + +} diff --git a/exosphere/mariko_fatal/source/fatal_sdmmc.hpp b/exosphere/mariko_fatal/source/fatal_sdmmc.hpp new file mode 100644 index 000000000..bdd7ec89d --- /dev/null +++ b/exosphere/mariko_fatal/source/fatal_sdmmc.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon::fatal { + + Result InitializeSdCard(); + +} diff --git a/exosphere/program/program.ld b/exosphere/program/program.ld index ed062f0d3..19339ae64 100644 --- a/exosphere/program/program.ld +++ b/exosphere/program/program.ld @@ -106,6 +106,17 @@ SECTIONS .debug_code : { KEEP (*(.text._ZN3ams3log6PrintfEPKcz .text._ZN3ams3log7VPrintfEPKcSt9__va_list .text._ZN3ams3log4DumpEPKvm)) KEEP (*(.text._ZN3ams4util10TVSNPrintfEPcmPKcSt9__va_list .text._ZN3ams4util12_GLOBAL__N_114TVSNPrintfImplEPcmPKcSt9__va_list .text._ZZN3ams4util12_GLOBAL__N_114TVSNPrintfImplEPcmPKcSt9__va_listENKUlbmE3_clEbm)) + KEEP(secmon_exception_handler.o(.text*)) + secmon_exception_handler.o(.rodata*) + secmon_exception_handler.o(.data*) + } >debug_code AT>glob + + .debug_code.bss_fill : + { + FILL(0x00000000); + secmon_exception_handler.o(.bss* COMMON) + . = ORIGIN(debug_code) + LENGTH(debug_code) - 1; + BYTE(0x00); } >debug_code AT>glob __bootcode_end__ = ABSOLUTE(.) - ORIGIN(debug_code) + 0x40034000; @@ -138,8 +149,9 @@ SECTIONS . = ALIGN(8); } >tzram_boot AT>glob - .tzram_boot_code.bss : + .tzram_boot_code.bss_fill : { + FILL(0x00000000); __boot_bss_start__ = ABSOLUTE(.); secmon_main.o(.bss* COMMON) secmon_boot_functions.o(.bss* COMMON) @@ -149,11 +161,6 @@ SECTIONS secmon_boot_rsa.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 diff --git a/exosphere/program/source/boot/secmon_boot_functions.cpp b/exosphere/program/source/boot/secmon_boot_functions.cpp index 2e0d7bab2..3c02af55a 100644 --- a/exosphere/program/source/boot/secmon_boot_functions.cpp +++ b/exosphere/program/source/boot/secmon_boot_functions.cpp @@ -188,7 +188,7 @@ namespace ams::secmon::boot { const u8 key_generation = meta.GetKeyGeneration(); /* Decrypt or load each payload in order. */ for (int i = 0; i < pkg2::PayloadCount; ++i) { - AMS_SECMON_LOG("pkg2 payload[%d]: %09lx -> %09lx size=%08x\n", i, dst + meta.payload_offsets[i], src, meta.payload_sizes[i]); + AMS_SECMON_LOG("pkg2 payload[%d]: %09lx -> %09lx size=%08x\n", i, src, dst + meta.payload_offsets[i], meta.payload_sizes[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); diff --git a/exosphere/program/source/secmon_exception_handler.cpp b/exosphere/program/source/secmon_exception_handler.cpp new file mode 100644 index 000000000..6625ee946 --- /dev/null +++ b/exosphere/program/source/secmon_exception_handler.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_error.hpp" + +namespace ams::secmon { + + namespace { + + constexpr inline uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); + + constinit std::atomic_bool g_is_locked = false; + + } + + void ExceptionHandlerImpl(uintptr_t lr, uintptr_t sp) { + /* Ensure that previous logs have been flushed. */ + AMS_LOG_FLUSH(); + + /* Get system registers. */ + uintptr_t far_el1, far_el3, elr_el3; + util::BitPack32 esr_el3; + + HW_CPU_GET_FAR_EL1(far_el1); + HW_CPU_GET_FAR_EL3(far_el3); + HW_CPU_GET_ELR_EL3(elr_el3); + HW_CPU_GET_ESR_EL3(esr_el3); + + /* Print some whitespace before the exception handler. */ + AMS_LOG("\n\n"); + AMS_SECMON_LOG("ExceptionHandler\n"); + AMS_SECMON_LOG("----------------\n"); + AMS_SECMON_LOG("esr: 0x%08X\n", esr_el3.value); + AMS_SECMON_LOG(" Exception Class: 0x%02X\n", esr_el3.Get()); + AMS_SECMON_LOG(" Instruction Length: %d\n", esr_el3.Get() ? 32 : 16); + AMS_SECMON_LOG(" Instruction Specific Syndrome: 0x%07X\n", esr_el3.Get()); + + AMS_SECMON_LOG("far_el1: 0x%016lX\n", far_el1); + AMS_SECMON_LOG("far_el3: 0x%016lX\n", far_el3); + AMS_SECMON_LOG("elr_el3: 0x%016lX\n", elr_el3); + + AMS_SECMON_LOG("lr: 0x%016lX\n", lr); + AMS_SECMON_LOG("sp: 0x%016lX\n", sp); + + AMS_DUMP(reinterpret_cast(sp), util::AlignUp(sp, mmu::PageSize) - sp); + + AMS_LOG_FLUSH(); + } + + NORETURN void ExceptionHandler() { + /* Get link register and stack pointer. */ + u64 lr, sp; + { + __asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory"); + __asm__ __volatile__("mov %0, sp" : "=r"(sp) :: "memory"); + } + + /* Acquire exclusive access to exception handling logic. */ + if (g_is_locked.exchange(true)) { + /* Invoke the exception handler impl. */ + ExceptionHandlerImpl(lr, sp); + + /* Lockout the security engine. */ + se::Lockout(); + + /* Lockout fuses. */ + fuse::Lockout(); + + /* Disable crypto operations after reboot. */ + reg::Write(PMC + APBDEV_PMC_CRYPTO_OP, 0); + + /* Perform an error reboot. */ + secmon::SetError(pkg1::ErrorInfo_UnknownAbort); + secmon::ErrorReboot(); + } else { + /* Wait forever while the first core prints the exception and reboots. */ + while (true) { + util::WaitMicroSeconds(1000); + } + } + } + +} \ No newline at end of file diff --git a/exosphere/program/source/secmon_exception_vectors.s b/exosphere/program/source/secmon_exception_vectors.s index 2749f9f64..88d7fe8a5 100644 --- a/exosphere/program/source/secmon_exception_vectors.s +++ b/exosphere/program/source/secmon_exception_vectors.s @@ -76,6 +76,11 @@ vector_entry synch_sp0 .endfunc .cfi_endproc _ZN3ams6secmon26UnexpectedExceptionHandlerEv: + #if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING) + /* Jump to the debug exception handler. */ + ldr x16, =_ZN3ams6secmon16ExceptionHandlerEv + br x16 + #else /* Load the ErrorInfo scratch. */ ldr x0, =0x1F004AC40 @@ -85,6 +90,7 @@ _ZN3ams6secmon26UnexpectedExceptionHandlerEv: /* Perform an error reboot. */ b _ZN3ams6secmon11ErrorRebootEv + #endif vector_entry irq_sp0 /* An unexpected exception was taken. */ diff --git a/exosphere/program/source/secmon_user_power_management.cpp b/exosphere/program/source/secmon_user_power_management.cpp index 4fb063b96..63ad881d8 100644 --- a/exosphere/program/source/secmon_user_power_management.cpp +++ b/exosphere/program/source/secmon_user_power_management.cpp @@ -84,6 +84,26 @@ namespace ams::secmon { PerformPmcReboot(); } + void PerformUserRebootToFatalError() { + if (fuse::GetSocType() == fuse::SocType_Erista) { + /* On Erista, we reboot to fatal error by jumping to fusee primary's handler. */ + return PerformUserRebootToPayload(); + } else /* if (fuse::GetSocType() == fuse::SocType_Mariko) */ { + /* TODO: Send a SGI FIQ to the other CPUs, so that user code stops executing. */ + + /* TODO: On cores other than 3, halt/wfi. */ + + AMS_SECMON_LOG("%s\n", "Jumping to Mariko Fatal."); + AMS_LOG_FLUSH(); + + /* Jump to the mariko fatal program. */ + reinterpret_cast(secmon::MemoryRegionVirtualTzramMarikoProgram.GetAddress())(); + + /* The mariko fatal program never returns. */ + __builtin_unreachable(); + } + } + void PerformUserShutDown() { /* Load our reboot stub to iram. */ LoadRebootStub(RebootStubAction_ShutDown); diff --git a/exosphere/program/source/secmon_user_power_management.hpp b/exosphere/program/source/secmon_user_power_management.hpp index 9a50e8227..c818c8771 100644 --- a/exosphere/program/source/secmon_user_power_management.hpp +++ b/exosphere/program/source/secmon_user_power_management.hpp @@ -19,13 +19,15 @@ namespace ams::secmon { enum UserRebootType { - UserRebootType_None = 0, - UserRebootType_ToRcm = 1, - UserRebootType_ToPayload = 2, + UserRebootType_None = 0, + UserRebootType_ToRcm = 1, + UserRebootType_ToPayload = 2, + UserRebootType_ToFatalError = 3, }; void PerformUserRebootToRcm(); void PerformUserRebootToPayload(); + void PerformUserRebootToFatalError(); void PerformUserShutDown(); } diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp index 857eeccc4..b837ddf94 100644 --- a/exosphere/program/source/smc/secmon_smc_info.cpp +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -304,11 +304,20 @@ namespace ams::secmon::smc { case UserRebootType_ToPayload: PerformUserRebootToPayload(); break; + case UserRebootType_ToFatalError: + PerformUserRebootToFatalError(); + break; default: return SmcResult::InvalidArgument; } } else /* if (soc_type == fuse::SocType_Mariko) */ { - return SmcResult::NotImplemented; + switch (static_cast(args.r[3])) { + case UserRebootType_ToFatalError: + PerformUserRebootToFatalError(); + break; + default: + return SmcResult::InvalidArgument; + } } break; case ConfigItem::ExosphereNeedsShutdown: diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp index 5792ee713..26f7298bb 100644 --- a/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp @@ -43,6 +43,15 @@ namespace ams::hw::arch::arm64 { #define HW_CPU_GET_VBAR_EL3(value) HW_CPU_GET_SYSREG(vbar_el3, value) #define HW_CPU_SET_VBAR_EL3(value) HW_CPU_SET_SYSREG(vbar_el3, value) + #define HW_CPU_GET_ELR_EL3(value) HW_CPU_GET_SYSREG(elr_el3, value) + #define HW_CPU_SET_ELR_EL3(value) HW_CPU_SET_SYSREG(elr_el3, value) + + #define HW_CPU_GET_FAR_EL3(value) HW_CPU_GET_SYSREG(far_el3, value) + #define HW_CPU_SET_FAR_EL3(value) HW_CPU_SET_SYSREG(far_el3, value) + + #define HW_CPU_GET_ESR_EL3(value) HW_CPU_GET_SYSREG(esr_el3, value) + #define HW_CPU_SET_ESR_EL3(value) HW_CPU_SET_SYSREG(esr_el3, value) + #define HW_CPU_GET_CLIDR_EL1(value) HW_CPU_GET_SYSREG(clidr_el1, value) #define HW_CPU_SET_CLIDR_EL1(value) HW_CPU_SET_SYSREG(clidr_el1, value) @@ -84,6 +93,9 @@ namespace ams::hw::arch::arm64 { #define HW_CPU_GET_DBGCLAIMCLR_EL1(value) HW_CPU_GET_SYSREG(dbgclaimclr_el1, value) #define HW_CPU_SET_DBGCLAIMCLR_EL1(value) HW_CPU_SET_SYSREG(dbgclaimclr_el1, value) + #define HW_CPU_GET_FAR_EL1(value) HW_CPU_GET_SYSREG(far_el1, value) + #define HW_CPU_SET_FAR_EL1(value) HW_CPU_SET_SYSREG(far_el1, value) + #define HW_CPU_GET_DBGVCR32_EL2(value) HW_CPU_GET_SYSREG(dbgvcr32_el2, value) #define HW_CPU_SET_DBGVCR32_EL2(value) HW_CPU_SET_SYSREG(dbgvcr32_el2, value) @@ -327,4 +339,10 @@ namespace ams::hw::arch::arm64 { using Rw = util::BitPack64::Field<31, 1>; }; + struct EsrEl3 { + using Iss = util::BitPack32::Field< 0, 25>; + using Il = util::BitPack32::Field<25, 1>; + using Ec = util::BitPack32::Field<26, 6>; + }; + } diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index 620b08866..fb9f35751 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -143,7 +143,8 @@ namespace ams::secmon { HANDLER(I2c1, Gpio, UINT64_C(0x7000C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ HANDLER(ExceptionVectors, I2c1, UINT64_C(0x6000F000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ HANDLER(MemoryController0, ExceptionVectors, UINT64_C(0x7001C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ - HANDLER(MemoryController1, MemoryController0, UINT64_C(0x7001D000), UINT64_C(0x1000), true, ## __VA_ARGS__) + HANDLER(MemoryController1, MemoryController0, UINT64_C(0x7001D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(Sdmmc, MemoryController1, UINT64_C(0x700B0000), UINT64_C(0x1000), true, ## __VA_ARGS__) #define DEFINE_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \ constexpr inline const MemoryRegion MemoryRegionVirtualDevice##_NAME_ = MemoryRegion(MemoryRegionVirtualDevice##_PREV_.GetEndAddress() + 0x1000, _SIZE_); \ @@ -258,6 +259,15 @@ namespace ams::secmon { static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDramSecureDataStore)); static_assert(MemoryRegionDram.Contains(MemoryRegionPhysicalDramSecureDataStore)); + constexpr inline const MemoryRegion MemoryRegionVirtualDramSdmmc1L0DevicePageTable = MemoryRegion(UINT64_C(0x1F010F000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSdmmc1L0DevicePageTable = MemoryRegion( UINT64_C(0x8001F000), 0x1000); + + constexpr inline const MemoryRegion MemoryRegionVirtualDramSdmmc1L1DevicePageTable = MemoryRegion(UINT64_C(0x1F010E000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSdmmc1L1DevicePageTable = MemoryRegion( UINT64_C(0x8001E000), 0x1000); + + constexpr inline const MemoryRegion MemoryRegionVirtualDramSdmmcMappedData = MemoryRegion(UINT64_C(0x1F0100000), 0xE000); + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSdmmcMappedData = MemoryRegion(UINT64_C(0x80010000), 0xE000); + constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStoreTzram = MemoryRegion(UINT64_C(0x1F0100000), 0xE000); constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStoreWarmbootFirmware = MemoryRegion(UINT64_C(0x1F010E000), 0x17C0); constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStoreSecurityEngineState = MemoryRegion(UINT64_C(0x1F010F7C0), 0x0840); diff --git a/libraries/libstratosphere/include/stratosphere/ams/ams_exosphere_api.hpp b/libraries/libstratosphere/include/stratosphere/ams/ams_exosphere_api.hpp index bc0efd139..69bbb4d8f 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/ams_exosphere_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/ams_exosphere_api.hpp @@ -23,6 +23,7 @@ namespace ams::exosphere { void ForceRebootToRcm(); void ForceRebootToIramPayload(); + void ForceRebootToFatalError(); void ForceShutdown(); bool IsRcmBugPatched(); diff --git a/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp b/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp index a8cf21b9a..502288b9e 100644 --- a/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp +++ b/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp @@ -36,6 +36,10 @@ namespace ams::exosphere { R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 2))); } + void ForceRebootToFatalError() { + R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 3))); + } + void ForceShutdown() { R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::SetConfig(spl::ConfigItem::ExosphereNeedsShutdown, 1))); } diff --git a/libraries/libvapours/source/dd/dd_io_mapping.cpp b/libraries/libvapours/source/dd/dd_io_mapping.cpp index 1bbdeb8c0..966de2fb2 100644 --- a/libraries/libvapours/source/dd/dd_io_mapping.cpp +++ b/libraries/libvapours/source/dd/dd_io_mapping.cpp @@ -28,9 +28,20 @@ namespace ams::dd { uintptr_t QueryIoMapping(dd::PhysicalAddress phys_addr, size_t size) { #if defined(ATMOSPHERE_IS_EXOSPHERE) #if defined(ATMOSPHERE_ARCH_ARM64) - /* TODO: Secure Monitor translation? */ - AMS_UNUSED(size); - return static_cast(phys_addr); + /* TODO: Do this in a less shitty way. */ + if (secmon::MemoryRegionPhysicalDeviceClkRst.Contains(phys_addr, size)) { + return secmon::MemoryRegionVirtualDeviceClkRst.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + } else if (secmon::MemoryRegionPhysicalDeviceGpio.Contains(phys_addr, size)) { + return secmon::MemoryRegionVirtualDeviceGpio.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDeviceGpio.GetAddress(); + } else if (secmon::MemoryRegionPhysicalDeviceApbMisc.Contains(phys_addr, size)) { + return secmon::MemoryRegionVirtualDeviceApbMisc.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + } else if (secmon::MemoryRegionPhysicalDeviceSdmmc.Contains(phys_addr, size)) { + return secmon::MemoryRegionVirtualDeviceSdmmc.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDeviceSdmmc.GetAddress(); + } else { + AMS_UNUSED(size); + return static_cast(phys_addr); + } + #elif defined(ATMOSPHERE_ARCH_ARM) /* TODO: BPMP translation? */ AMS_UNUSED(size); diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp index 3ac285761..f4439bb72 100644 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp @@ -143,9 +143,9 @@ namespace ams::sdmmc::impl { virtual void SetWorkBuffer(void *wb, size_t wb_size) = 0; virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) = 0; - virtual void Shutdown(); - virtual void PutToSleep(); - virtual Result Awaken(); + virtual void Shutdown() = 0; + virtual void PutToSleep() = 0; + virtual Result Awaken() = 0; virtual Result SwitchToSdr12(); diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp index 79a410283..738143fe7 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp @@ -68,7 +68,7 @@ namespace ams::mitm::bpc { exosphere::CopyToIram(IramPayloadBase + IramFatalErrorContextOffset, g_work_page, sizeof(g_work_page)); } - exosphere::ForceRebootToIramPayload(); + exosphere::ForceRebootToFatalError(); } } diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp index b5d3e8c21..5019243cd 100644 --- a/stratosphere/boot/source/boot_main.cpp +++ b/stratosphere/boot/source/boot_main.cpp @@ -216,6 +216,8 @@ int main(int argc, char **argv) boot::CheckBatteryCharge(); } + AMS_ABORT_UNLESS(spl::GetSocType() != spl::SocType_Mariko); + /* Configure pinmux + drive pads. */ boot::SetInitialPinmuxConfiguration(); diff --git a/stratosphere/boot/source/boot_power_utils.cpp b/stratosphere/boot/source/boot_power_utils.cpp index d0fa765af..c5cd17a0e 100644 --- a/stratosphere/boot/source/boot_power_utils.cpp +++ b/stratosphere/boot/source/boot_power_utils.cpp @@ -43,7 +43,20 @@ namespace ams::boot { } } - void DoRebootToPayload(ams::FatalErrorContext *ctx) { + void DoRebootToPayload() { + /* Ensure clean IRAM state. */ + ClearIram(); + + /* Copy in payload. */ + for (size_t ofs = 0; ofs < fusee_primary_bin_size; ofs += sizeof(g_work_page)) { + std::memcpy(g_work_page, &fusee_primary_bin[ofs], std::min(static_cast(fusee_primary_bin_size - ofs), sizeof(g_work_page))); + exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page)); + } + + exosphere::ForceRebootToIramPayload(); + } + + void DoRebootToFatalError(ams::FatalErrorContext *ctx) { /* Ensure clean IRAM state. */ ClearIram(); @@ -61,14 +74,14 @@ namespace ams::boot { exosphere::CopyToIram(IramPayloadBase + IramFatalErrorContextOffset, g_work_page, sizeof(g_work_page)); } - exosphere::ForceRebootToIramPayload(); + exosphere::ForceRebootToFatalError(); } } void RebootSystem() { if (spl::GetSocType() == spl::SocType_Erista) { - DoRebootToPayload(nullptr); + DoRebootToPayload(); } else { /* On Mariko, we can't reboot to payload, so we should just do a reboot. */ PmicDriver().RebootSystem(); @@ -84,7 +97,7 @@ namespace ams::boot { } void RebootForFatalError(ams::FatalErrorContext *ctx) { - DoRebootToPayload(ctx); + DoRebootToFatalError(ctx); } }